mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 16:28:38 +09:00
Merge remote-tracking branch 'origin/master' into custom
This commit is contained in:
commit
838b5e4e48
569 changed files with 1183 additions and 62621 deletions
|
|
@ -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 |
|
||||
|
|
@ -122,10 +128,6 @@ static RedirEntry kRedirMap[] = {
|
|||
nsIAboutModule::ALLOW_SCRIPT
|
||||
},
|
||||
#endif
|
||||
{
|
||||
"accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
|
||||
nsIAboutModule::ALLOW_SCRIPT
|
||||
},
|
||||
{
|
||||
"reader", "chrome://global/content/reader/aboutReader.html",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -200,7 +200,6 @@ var CustomizableUIInternal = {
|
|||
"find-button",
|
||||
"preferences-button",
|
||||
"add-ons-button",
|
||||
"sync-button",
|
||||
];
|
||||
|
||||
if (!AppConstants.MOZ_DEV_EDITION) {
|
||||
|
|
|
|||
|
|
@ -287,144 +287,6 @@ const CustomizableWidgets = [
|
|||
onViewHiding: function(aEvent) {
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,13 +20,15 @@ DIRS += [
|
|||
'sessionstore',
|
||||
'shell',
|
||||
'selfsupport',
|
||||
'syncedtabs',
|
||||
'translation',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WEBEXTENSIONS']:
|
||||
DIRS += ['webextensions']
|
||||
|
||||
if CONFIG['MOZ_SERVICES_SYNC']:
|
||||
DIRS += ['sync']
|
||||
|
||||
DIRS += ['build']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
|
@ -39,6 +41,9 @@ XPIDL_MODULE = 'browsercompsbase'
|
|||
EXTRA_COMPONENTS += [
|
||||
'BrowserComponents.manifest',
|
||||
'nsBrowserContentHandler.js',
|
||||
]
|
||||
|
||||
EXTRA_PP_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);
|
||||
|
|
@ -2274,90 +2272,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 URIs in background tabs.
|
||||
* We open the received URI in a background tab.
|
||||
*
|
||||
* 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 {
|
||||
let tabbrowser = RecentWindow.getMostRecentBrowserWindow({private: false}).gBrowser;
|
||||
|
||||
// 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();
|
||||
|
||||
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);
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@
|
|||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'PlacesUIUtils.jsm',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -13,6 +13,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
|
||||
|
|
|
|||
|
|
@ -65,7 +65,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");
|
||||
|
|
|
|||
|
|
@ -23,8 +23,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 % containersDTD SYSTEM
|
||||
|
|
@ -43,8 +45,10 @@
|
|||
%privacyDTD;
|
||||
%tabsDTD;
|
||||
%searchDTD;
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
%syncBrandDTD;
|
||||
%syncDTD;
|
||||
#endif
|
||||
%securityDTD;
|
||||
%containersDTD;
|
||||
%sanitizeDTD;
|
||||
|
|
@ -150,6 +154,7 @@
|
|||
<label class="category-name" flex="1">&paneSecurity.title;</label>
|
||||
</richlistitem>
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<richlistitem id="category-sync"
|
||||
class="category"
|
||||
value="paneSync"
|
||||
|
|
@ -159,6 +164,7 @@
|
|||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1">&paneSync.title;</label>
|
||||
</richlistitem>
|
||||
#endif
|
||||
|
||||
<richlistitem id="category-advanced"
|
||||
class="category"
|
||||
|
|
@ -188,7 +194,9 @@
|
|||
#include applications.xul
|
||||
#include content.xul
|
||||
#include security.xul
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include sync.xul
|
||||
#endif
|
||||
</prefpane>
|
||||
</vbox>
|
||||
|
||||
|
|
|
|||
|
|
@ -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,247 +63,50 @@ var gSyncPane = {
|
|||
xps.ensureLoaded();
|
||||
},
|
||||
|
||||
_showLoadPage: function (xps) {
|
||||
let username;
|
||||
try {
|
||||
username = Services.prefs.getCharPref("services.sync.username");
|
||||
} catch (e) {}
|
||||
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;
|
||||
try {
|
||||
cachedComputerName = Services.prefs.getCharPref("services.sync.client.name");
|
||||
}
|
||||
catch (e) {
|
||||
cachedComputerName = "";
|
||||
}
|
||||
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,
|
||||
|
|
@ -336,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();
|
||||
|
|
@ -345,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);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -384,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) {
|
||||
|
|
@ -552,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;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
11
application/basilisk/components/sync/aboutSyncTabs.css
Normal file
11
application/basilisk/components/sync/aboutSyncTabs.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
richlistitem[type="tab"] {
|
||||
-moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#tab-listing);
|
||||
}
|
||||
|
||||
richlistitem[type="client"] {
|
||||
-moz-binding: url(chrome://browser/content/sync/aboutSyncTabs-bindings.xml#client-listing);
|
||||
}
|
||||
313
application/basilisk/components/sync/aboutSyncTabs.js
Normal file
313
application/basilisk/components/sync/aboutSyncTabs.js
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource://services-sync/main.js");
|
||||
Cu.import("resource:///modules/PlacesUIUtils.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var RemoteTabViewer = {
|
||||
_tabsList: null,
|
||||
|
||||
init: function () {
|
||||
Services.obs.addObserver(this, "weave:service:login:finish", false);
|
||||
Services.obs.addObserver(this, "weave:engine:sync:finish", false);
|
||||
|
||||
this._tabsList = document.getElementById("tabsList");
|
||||
|
||||
this.buildList(true);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
Services.obs.removeObserver(this, "weave:service:login:finish");
|
||||
Services.obs.removeObserver(this, "weave:engine:sync:finish");
|
||||
},
|
||||
|
||||
createItem: function(attrs) {
|
||||
let item = document.createElement("richlistitem");
|
||||
|
||||
// Copy the attributes from the argument into the item
|
||||
for (let attr in attrs) {
|
||||
item.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
|
||||
if (attrs["type"] == "tab") {
|
||||
item.label = attrs.title != "" ? attrs.title : attrs.url;
|
||||
}
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
filterTabs: function(event) {
|
||||
let val = event.target.value.toLowerCase();
|
||||
let numTabs = this._tabsList.getRowCount();
|
||||
let clientTabs = 0;
|
||||
let currentClient = null;
|
||||
|
||||
for (let i = 0; i < numTabs; i++) {
|
||||
let item = this._tabsList.getItemAtIndex(i);
|
||||
let hide = false;
|
||||
if (item.getAttribute("type") == "tab") {
|
||||
if (!item.getAttribute("url").toLowerCase().includes(val) &&
|
||||
!item.getAttribute("title").toLowerCase().includes(val)) {
|
||||
hide = true;
|
||||
} else {
|
||||
clientTabs++;
|
||||
}
|
||||
}
|
||||
else if (item.getAttribute("type") == "client") {
|
||||
if (currentClient) {
|
||||
if (clientTabs == 0) {
|
||||
currentClient.hidden = true;
|
||||
}
|
||||
}
|
||||
currentClient = item;
|
||||
clientTabs = 0;
|
||||
}
|
||||
item.hidden = hide;
|
||||
}
|
||||
if (clientTabs == 0) {
|
||||
currentClient.hidden = true;
|
||||
}
|
||||
},
|
||||
|
||||
openSelected: function() {
|
||||
let items = this._tabsList.selectedItems;
|
||||
let urls = [];
|
||||
for (let i = 0;i < items.length;i++) {
|
||||
if (items[i].getAttribute("type") == "tab") {
|
||||
urls.push(items[i].getAttribute("url"));
|
||||
let index = this._tabsList.getIndexOfItem(items[i]);
|
||||
this._tabsList.removeItemAt(index);
|
||||
}
|
||||
}
|
||||
if (urls.length) {
|
||||
getTopWin().gBrowser.loadTabs(urls);
|
||||
this._tabsList.clearSelection();
|
||||
}
|
||||
},
|
||||
|
||||
bookmarkSingleTab: function() {
|
||||
let item = this._tabsList.selectedItems[0];
|
||||
let uri = Weave.Utils.makeURI(item.getAttribute("url"));
|
||||
let title = item.getAttribute("title");
|
||||
PlacesUIUtils.showBookmarkDialog({ action: "add"
|
||||
, type: "bookmark"
|
||||
, uri: uri
|
||||
, title: title
|
||||
, hiddenRows: [ "description"
|
||||
, "location"
|
||||
, "loadInSidebar"
|
||||
, "keyword" ]
|
||||
}, window.top);
|
||||
},
|
||||
|
||||
bookmarkSelectedTabs: function() {
|
||||
let items = this._tabsList.selectedItems;
|
||||
let URIs = [];
|
||||
for (let i = 0;i < items.length;i++) {
|
||||
if (items[i].getAttribute("type") == "tab") {
|
||||
let uri = Weave.Utils.makeURI(items[i].getAttribute("url"));
|
||||
if (!uri) {
|
||||
continue;
|
||||
}
|
||||
|
||||
URIs.push(uri);
|
||||
}
|
||||
}
|
||||
if (URIs.length) {
|
||||
PlacesUIUtils.showBookmarkDialog({ action: "add"
|
||||
, type: "folder"
|
||||
, URIList: URIs
|
||||
, hiddenRows: [ "description" ]
|
||||
}, window.top);
|
||||
}
|
||||
},
|
||||
|
||||
getIcon: function (iconUri, defaultIcon) {
|
||||
try {
|
||||
let iconURI = Weave.Utils.makeURI(iconUri);
|
||||
return PlacesUtils.favicons.getFaviconLinkForIcon(iconURI).spec;
|
||||
} catch (ex) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// Just give the provided default icon or the system's default.
|
||||
return defaultIcon || PlacesUtils.favicons.defaultFavicon.spec;
|
||||
},
|
||||
|
||||
_waitingForBuildList: false,
|
||||
|
||||
_buildListRequested: false,
|
||||
|
||||
buildList: function (force) {
|
||||
if (this._waitingForBuildList) {
|
||||
this._buildListRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this._waitingForBuildList = true;
|
||||
this._buildListRequested = false;
|
||||
|
||||
this._clearTabList();
|
||||
|
||||
if (Weave.Service.isLoggedIn && this._refetchTabs(force)) {
|
||||
this._generateWeaveTabList();
|
||||
} else {
|
||||
//XXXzpao We should say something about not being logged in & not having data
|
||||
// or tell the appropriate condition. (bug 583344)
|
||||
}
|
||||
|
||||
function complete() {
|
||||
this._waitingForBuildList = false;
|
||||
if (this._buildListRequested) {
|
||||
CommonUtils.nextTick(this.buildList, this);
|
||||
}
|
||||
}
|
||||
|
||||
complete();
|
||||
},
|
||||
|
||||
_clearTabList: function () {
|
||||
let list = this._tabsList;
|
||||
|
||||
// Clear out existing richlistitems
|
||||
let count = list.getRowCount();
|
||||
if (count > 0) {
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
list.removeItemAt(i);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_generateWeaveTabList: function () {
|
||||
let engine = Weave.Service.engineManager.get("tabs");
|
||||
let list = this._tabsList;
|
||||
|
||||
let seenURLs = new Set();
|
||||
let localURLs = engine.getOpenURLs();
|
||||
|
||||
for (let [guid, client] in Iterator(engine.getAllClients())) {
|
||||
// Create the client node, but don't add it in-case we don't show any tabs
|
||||
let appendClient = true;
|
||||
|
||||
client.tabs.forEach(function({title, urlHistory, icon}) {
|
||||
let url = urlHistory[0];
|
||||
if (!url || localURLs.has(url) || seenURLs.has(url)) {
|
||||
return;
|
||||
}
|
||||
seenURLs.add(url);
|
||||
|
||||
if (appendClient) {
|
||||
let attrs = {
|
||||
type: "client",
|
||||
clientName: client.clientName,
|
||||
class: Weave.Service.clientsEngine.isMobile(client.id) ? "mobile" : "desktop"
|
||||
};
|
||||
let clientEnt = this.createItem(attrs);
|
||||
list.appendChild(clientEnt);
|
||||
appendClient = false;
|
||||
clientEnt.disabled = true;
|
||||
}
|
||||
let attrs = {
|
||||
type: "tab",
|
||||
title: title || url,
|
||||
url: url,
|
||||
icon: this.getIcon(icon),
|
||||
}
|
||||
let tab = this.createItem(attrs);
|
||||
list.appendChild(tab);
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
adjustContextMenu: function(event) {
|
||||
let mode = "all";
|
||||
switch (this._tabsList.selectedItems.length) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
mode = "single"
|
||||
break;
|
||||
default:
|
||||
mode = "multiple";
|
||||
break;
|
||||
}
|
||||
|
||||
let menu = document.getElementById("tabListContext");
|
||||
let el = menu.firstChild;
|
||||
while (el) {
|
||||
let showFor = el.getAttribute("showFor");
|
||||
if (showFor) {
|
||||
el.hidden = showFor != mode && showFor != "all";
|
||||
}
|
||||
|
||||
el = el.nextSibling;
|
||||
}
|
||||
},
|
||||
|
||||
_refetchTabs: function(force) {
|
||||
if (!force) {
|
||||
// Don't bother refetching tabs if we already did so recently
|
||||
let lastFetch = 0;
|
||||
try {
|
||||
lastFetch = Services.prefs.getIntPref("services.sync.lastTabFetch");
|
||||
}
|
||||
catch (e) {
|
||||
/* Just use the default value of 0 */
|
||||
}
|
||||
|
||||
let now = Math.floor(Date.now() / 1000);
|
||||
if (now - lastFetch < 30) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if Clients hasn't synced yet this session, we need to sync it as well.
|
||||
if (Weave.Service.clientsEngine.lastSync == 0) {
|
||||
Weave.Service.clientsEngine.sync();
|
||||
}
|
||||
|
||||
// Force a sync only for the tabs engine
|
||||
let engine = Weave.Service.engineManager.get("tabs");
|
||||
engine.lastModified = null;
|
||||
engine.sync();
|
||||
Services.prefs.setIntPref("services.sync.lastTabFetch",
|
||||
Math.floor(Date.now() / 1000));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "weave:service:login:finish":
|
||||
this.buildList(true);
|
||||
break;
|
||||
case "weave:engine:sync:finish":
|
||||
if (subject == "tabs") {
|
||||
this.buildList(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleClick: function(event) {
|
||||
if (event.target.getAttribute("type") != "tab") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (event.button == 1) {
|
||||
let url = event.target.getAttribute("url");
|
||||
openUILink(url, event);
|
||||
let index = this._tabsList.getIndexOfItem(event.target);
|
||||
this._tabsList.removeItemAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
application/basilisk/components/sync/aboutSyncTabs.xul
Normal file
68
application/basilisk/components/sync/aboutSyncTabs.xul
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/aboutSyncTabs.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/sync/aboutSyncTabs.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % aboutSyncTabsDTD SYSTEM "chrome://browser/locale/aboutSyncTabs.dtd">
|
||||
%aboutSyncTabsDTD;
|
||||
]>
|
||||
|
||||
<window id="tabs-display"
|
||||
onload="RemoteTabViewer.init()"
|
||||
onunload="RemoteTabViewer.uninit()"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&tabs.otherDevices.label;">
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/sync/aboutSyncTabs.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
<html:head>
|
||||
<html:link rel="icon" href="chrome://browser/skin/sync-16.png"/>
|
||||
</html:head>
|
||||
|
||||
<popupset id="contextmenus">
|
||||
<menupopup id="tabListContext">
|
||||
<menuitem label="&tabs.context.openTab.label;"
|
||||
accesskey="&tabs.context.openTab.accesskey;"
|
||||
oncommand="RemoteTabViewer.openSelected()"
|
||||
showFor="single"/>
|
||||
<menuitem label="&tabs.context.bookmarkSingleTab.label;"
|
||||
accesskey="&tabs.context.bookmarkSingleTab.accesskey;"
|
||||
oncommand="RemoteTabViewer.bookmarkSingleTab(event)"
|
||||
showFor="single"/>
|
||||
<menuitem label="&tabs.context.openMultipleTabs.label;"
|
||||
accesskey="&tabs.context.openMultipleTabs.accesskey;"
|
||||
oncommand="RemoteTabViewer.openSelected()"
|
||||
showFor="multiple"/>
|
||||
<menuitem label="&tabs.context.bookmarkMultipleTabs.label;"
|
||||
accesskey="&tabs.context.bookmarkMultipleTabs.accesskey;"
|
||||
oncommand="RemoteTabViewer.bookmarkSelectedTabs()"
|
||||
showFor="multiple"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&tabs.context.refreshList.label;"
|
||||
accesskey="&tabs.context.refreshList.accesskey;"
|
||||
oncommand="RemoteTabViewer.buildList()"
|
||||
showFor="all"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
<richlistbox context="tabListContext" id="tabsList" seltype="multiple"
|
||||
align="center" flex="1"
|
||||
onclick="RemoteTabViewer.handleClick(event)"
|
||||
oncontextmenu="RemoteTabViewer.adjustContextMenu(event)">
|
||||
<hbox id="headers" align="center">
|
||||
<label id="tabsListHeading"
|
||||
value="&tabs.otherDevices.label;"/>
|
||||
<spacer flex="1"/>
|
||||
<textbox type="search"
|
||||
emptytext="&tabs.searchText.label;"
|
||||
oncommand="RemoteTabViewer.filterTabs(event)"/>
|
||||
</hbox>
|
||||
|
||||
</richlistbox>
|
||||
</window>
|
||||
|
||||
157
application/basilisk/components/sync/addDevice.js
Normal file
157
application/basilisk/components/sync/addDevice.js
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://services-sync/main.js");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const PIN_PART_LENGTH = 4;
|
||||
|
||||
const ADD_DEVICE_PAGE = 0;
|
||||
const SYNC_KEY_PAGE = 1;
|
||||
const DEVICE_CONNECTED_PAGE = 2;
|
||||
|
||||
var gSyncAddDevice = {
|
||||
|
||||
init: function init() {
|
||||
this.pin1.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.pin2.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
this.pin3.setAttribute("maxlength", PIN_PART_LENGTH);
|
||||
|
||||
this.nextFocusEl = {pin1: this.pin2,
|
||||
pin2: this.pin3,
|
||||
pin3: this.wizard.getButton("next")};
|
||||
|
||||
this.throbber = document.getElementById("pairDeviceThrobber");
|
||||
this.errorRow = document.getElementById("errorRow");
|
||||
|
||||
// Kick off a sync. That way the server will have the most recent data from
|
||||
// this computer and it will show up immediately on the new device.
|
||||
Weave.Service.scheduler.scheduleNextSync(0);
|
||||
},
|
||||
|
||||
onPageShow: function onPageShow() {
|
||||
this.wizard.getButton("back").hidden = true;
|
||||
|
||||
switch (this.wizard.pageIndex) {
|
||||
case ADD_DEVICE_PAGE:
|
||||
this.onTextBoxInput();
|
||||
this.wizard.canRewind = false;
|
||||
this.wizard.getButton("next").hidden = false;
|
||||
this.pin1.focus();
|
||||
break;
|
||||
case SYNC_KEY_PAGE:
|
||||
this.wizard.canAdvance = false;
|
||||
this.wizard.canRewind = true;
|
||||
this.wizard.getButton("back").hidden = false;
|
||||
this.wizard.getButton("next").hidden = true;
|
||||
document.getElementById("weavePassphrase").value =
|
||||
Weave.Utils.hyphenatePassphrase(Weave.Service.identity.syncKey);
|
||||
break;
|
||||
case DEVICE_CONNECTED_PAGE:
|
||||
this.wizard.canAdvance = true;
|
||||
this.wizard.canRewind = false;
|
||||
this.wizard.getButton("cancel").hidden = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onWizardAdvance: function onWizardAdvance() {
|
||||
switch (this.wizard.pageIndex) {
|
||||
case ADD_DEVICE_PAGE:
|
||||
this.startTransfer();
|
||||
return false;
|
||||
case DEVICE_CONNECTED_PAGE:
|
||||
window.close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
startTransfer: function startTransfer() {
|
||||
this.errorRow.hidden = true;
|
||||
// When onAbort is called, Weave may already be gone.
|
||||
const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
|
||||
|
||||
let self = this;
|
||||
let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
|
||||
onPaired: function onPaired() {
|
||||
let credentials = {account: Weave.Service.identity.account,
|
||||
password: Weave.Service.identity.basicPassword,
|
||||
synckey: Weave.Service.identity.syncKey,
|
||||
serverURL: Weave.Service.serverURL};
|
||||
jpakeclient.sendAndComplete(credentials);
|
||||
},
|
||||
onComplete: function onComplete() {
|
||||
delete self._jpakeclient;
|
||||
self.wizard.pageIndex = DEVICE_CONNECTED_PAGE;
|
||||
|
||||
// Schedule a Sync for soonish to fetch the data uploaded by the
|
||||
// device with which we just paired.
|
||||
Weave.Service.scheduler.scheduleNextSync(Weave.Service.scheduler.activeInterval);
|
||||
},
|
||||
onAbort: function onAbort(error) {
|
||||
delete self._jpakeclient;
|
||||
|
||||
// Aborted by user, ignore.
|
||||
if (error == JPAKE_ERROR_USERABORT) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.errorRow.hidden = false;
|
||||
self.throbber.hidden = true;
|
||||
self.pin1.value = self.pin2.value = self.pin3.value = "";
|
||||
self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
|
||||
self.pin1.focus();
|
||||
}
|
||||
});
|
||||
this.throbber.hidden = false;
|
||||
this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true;
|
||||
this.wizard.canAdvance = false;
|
||||
|
||||
let pin = this.pin1.value + this.pin2.value + this.pin3.value;
|
||||
let expectDelay = false;
|
||||
jpakeclient.pairWithPIN(pin, expectDelay);
|
||||
},
|
||||
|
||||
onWizardBack: function onWizardBack() {
|
||||
if (this.wizard.pageIndex != SYNC_KEY_PAGE)
|
||||
return true;
|
||||
|
||||
this.wizard.pageIndex = ADD_DEVICE_PAGE;
|
||||
return false;
|
||||
},
|
||||
|
||||
onWizardCancel: function onWizardCancel() {
|
||||
if (this._jpakeclient) {
|
||||
this._jpakeclient.abort();
|
||||
delete this._jpakeclient;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
onTextBoxInput: function onTextBoxInput(textbox) {
|
||||
if (textbox && textbox.value.length == PIN_PART_LENGTH)
|
||||
this.nextFocusEl[textbox.id].focus();
|
||||
|
||||
this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH
|
||||
&& this.pin2.value.length == PIN_PART_LENGTH
|
||||
&& this.pin3.value.length == PIN_PART_LENGTH);
|
||||
},
|
||||
|
||||
goToSyncKeyPage: function goToSyncKeyPage() {
|
||||
this.wizard.pageIndex = SYNC_KEY_PAGE;
|
||||
}
|
||||
|
||||
};
|
||||
// onWizardAdvance() and onPageShow() are run before init() so we'll set
|
||||
// these up as lazy getters.
|
||||
["wizard", "pin1", "pin2", "pin3"].forEach(function (id) {
|
||||
XPCOMUtils.defineLazyGetter(gSyncAddDevice, id, function() {
|
||||
return document.getElementById(id);
|
||||
});
|
||||
});
|
||||
129
application/basilisk/components/sync/addDevice.xul
Normal file
129
application/basilisk/components/sync/addDevice.xul
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
|
||||
%brandDTD;
|
||||
%syncBrandDTD;
|
||||
%syncSetupDTD;
|
||||
]>
|
||||
<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
id="wizard"
|
||||
title="&pairDevice.title.label;"
|
||||
windowtype="Sync:AddDevice"
|
||||
persist="screenX screenY"
|
||||
onwizardnext="return gSyncAddDevice.onWizardAdvance();"
|
||||
onwizardback="return gSyncAddDevice.onWizardBack();"
|
||||
onwizardcancel="gSyncAddDevice.onWizardCancel();"
|
||||
onload="gSyncAddDevice.init();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/addDevice.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/utils.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/printUtils.js"/>
|
||||
|
||||
<wizardpage id="addDevicePage"
|
||||
label="&pairDevice.title.label;"
|
||||
onpageshow="gSyncAddDevice.onPageShow();">
|
||||
<description>
|
||||
&pairDevice.dialog.description.label;
|
||||
<label class="text-link"
|
||||
value="&addDevice.showMeHow.label;"
|
||||
href="http://www.palemoon.org/sync/help/easy-setup.shtml"/>
|
||||
</description>
|
||||
<separator class="groove-thin"/>
|
||||
<description>
|
||||
&addDevice.dialog.enterCode.label;
|
||||
</description>
|
||||
<separator class="groove-thin"/>
|
||||
<vbox align="center">
|
||||
<textbox id="pin1"
|
||||
class="pin"
|
||||
oninput="gSyncAddDevice.onTextBoxInput(this);"
|
||||
onfocus="this.select();"
|
||||
/>
|
||||
<textbox id="pin2"
|
||||
class="pin"
|
||||
oninput="gSyncAddDevice.onTextBoxInput(this);"
|
||||
onfocus="this.select();"
|
||||
/>
|
||||
<textbox id="pin3"
|
||||
class="pin"
|
||||
oninput="gSyncAddDevice.onTextBoxInput(this);"
|
||||
onfocus="this.select();"
|
||||
/>
|
||||
</vbox>
|
||||
<separator class="groove-thin"/>
|
||||
<vbox id="pairDeviceThrobber" align="center" hidden="true">
|
||||
<image/>
|
||||
</vbox>
|
||||
<hbox id="errorRow" pack="center" hidden="true">
|
||||
<image class="statusIcon" status="error"/>
|
||||
<label class="status"
|
||||
value="&addDevice.dialog.tryAgain.label;"/>
|
||||
</hbox>
|
||||
<spacer flex="3"/>
|
||||
<label class="text-link"
|
||||
value="&addDevice.dontHaveDevice.label;"
|
||||
onclick="gSyncAddDevice.goToSyncKeyPage();"/>
|
||||
</wizardpage>
|
||||
|
||||
<!-- Need a non-empty label here, otherwise we get a default label on Mac -->
|
||||
<wizardpage id="syncKeyPage"
|
||||
label=" "
|
||||
onpageshow="gSyncAddDevice.onPageShow();">
|
||||
<description>
|
||||
&addDevice.dialog.recoveryKey.label;
|
||||
</description>
|
||||
<spacer/>
|
||||
|
||||
<groupbox>
|
||||
<label value="&recoveryKeyEntry.label;"
|
||||
accesskey="&recoveryKeyEntry.accesskey;"
|
||||
control="weavePassphrase"/>
|
||||
<textbox id="weavePassphrase"
|
||||
readonly="true"/>
|
||||
</groupbox>
|
||||
|
||||
<groupbox align="center">
|
||||
<description>&recoveryKeyBackup.description;</description>
|
||||
<hbox>
|
||||
<button id="printSyncKeyButton"
|
||||
label="&button.syncKeyBackup.print.label;"
|
||||
accesskey="&button.syncKeyBackup.print.accesskey;"
|
||||
oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
|
||||
<button id="saveSyncKeyButton"
|
||||
label="&button.syncKeyBackup.save.label;"
|
||||
accesskey="&button.syncKeyBackup.save.accesskey;"
|
||||
oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="deviceConnectedPage"
|
||||
label="&addDevice.dialog.connected.label;"
|
||||
onpageshow="gSyncAddDevice.onPageShow();">
|
||||
<vbox align="center">
|
||||
<image id="successPageIcon"/>
|
||||
</vbox>
|
||||
<separator/>
|
||||
<description class="normal">
|
||||
&addDevice.dialog.successful.label;
|
||||
</description>
|
||||
</wizardpage>
|
||||
|
||||
</wizard>
|
||||
234
application/basilisk/components/sync/genericChange.js
Normal file
234
application/basilisk/components/sync/genericChange.js
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
|
||||
Components.utils.import("resource://services-sync/main.js");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var Change = {
|
||||
_dialog: null,
|
||||
_dialogType: null,
|
||||
_status: null,
|
||||
_statusIcon: null,
|
||||
_firstBox: null,
|
||||
_secondBox: null,
|
||||
|
||||
get _passphraseBox() {
|
||||
delete this._passphraseBox;
|
||||
return this._passphraseBox = document.getElementById("passphraseBox");
|
||||
},
|
||||
|
||||
get _currentPasswordInvalid() {
|
||||
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
},
|
||||
|
||||
get _updatingPassphrase() {
|
||||
return this._dialogType == "UpdatePassphrase";
|
||||
},
|
||||
|
||||
onLoad: function Change_onLoad() {
|
||||
/* Load labels */
|
||||
let introText = document.getElementById("introText");
|
||||
let introText2 = document.getElementById("introText2");
|
||||
let warningText = document.getElementById("warningText");
|
||||
|
||||
// load some other elements & info from the window
|
||||
this._dialog = document.getElementById("change-dialog");
|
||||
this._dialogType = window.arguments[0];
|
||||
this._duringSetup = window.arguments[1];
|
||||
this._status = document.getElementById("status");
|
||||
this._statusIcon = document.getElementById("statusIcon");
|
||||
this._statusRow = document.getElementById("statusRow");
|
||||
this._firstBox = document.getElementById("textBox1");
|
||||
this._secondBox = document.getElementById("textBox2");
|
||||
|
||||
this._dialog.getButton("finish").disabled = true;
|
||||
this._dialog.getButton("back").hidden = true;
|
||||
|
||||
this._stringBundle =
|
||||
Services.strings.createBundle("chrome://browser/locale/syncGenericChange.properties");
|
||||
|
||||
switch (this._dialogType) {
|
||||
case "UpdatePassphrase":
|
||||
case "ResetPassphrase":
|
||||
document.getElementById("textBox1Row").hidden = true;
|
||||
document.getElementById("textBox2Row").hidden = true;
|
||||
document.getElementById("passphraseLabel").value
|
||||
= this._str("new.recoverykey.label");
|
||||
document.getElementById("passphraseSpacer").hidden = false;
|
||||
|
||||
if (this._updatingPassphrase) {
|
||||
document.getElementById("passphraseHelpBox").hidden = false;
|
||||
document.title = this._str("new.recoverykey.title");
|
||||
introText.textContent = this._str("new.recoverykey.introText");
|
||||
this._dialog.getButton("finish").label
|
||||
= this._str("new.recoverykey.acceptButton");
|
||||
}
|
||||
else {
|
||||
document.getElementById("generatePassphraseButton").hidden = false;
|
||||
document.getElementById("passphraseBackupButtons").hidden = false;
|
||||
let pp = Weave.Service.identity.syncKey;
|
||||
if (Weave.Utils.isPassphrase(pp))
|
||||
pp = Weave.Utils.hyphenatePassphrase(pp);
|
||||
this._passphraseBox.value = pp;
|
||||
this._passphraseBox.focus();
|
||||
document.title = this._str("change.recoverykey.title");
|
||||
introText.textContent = this._str("change.synckey.introText2");
|
||||
warningText.textContent = this._str("change.recoverykey.warningText");
|
||||
this._dialog.getButton("finish").label
|
||||
= this._str("change.recoverykey.acceptButton");
|
||||
if (this._duringSetup) {
|
||||
this._dialog.getButton("finish").disabled = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "ChangePassword":
|
||||
document.getElementById("passphraseRow").hidden = true;
|
||||
let box1label = document.getElementById("textBox1Label");
|
||||
let box2label = document.getElementById("textBox2Label");
|
||||
box1label.value = this._str("new.password.label");
|
||||
|
||||
if (this._currentPasswordInvalid) {
|
||||
document.title = this._str("new.password.title");
|
||||
introText.textContent = this._str("new.password.introText");
|
||||
this._dialog.getButton("finish").label
|
||||
= this._str("new.password.acceptButton");
|
||||
document.getElementById("textBox2Row").hidden = true;
|
||||
}
|
||||
else {
|
||||
document.title = this._str("change.password.title");
|
||||
box2label.value = this._str("new.password.confirm");
|
||||
introText.textContent = this._str("change.password3.introText");
|
||||
warningText.textContent = this._str("change.password.warningText");
|
||||
this._dialog.getButton("finish").label
|
||||
= this._str("change.password.acceptButton");
|
||||
}
|
||||
break;
|
||||
}
|
||||
document.getElementById("change-page")
|
||||
.setAttribute("label", document.title);
|
||||
},
|
||||
|
||||
_clearStatus: function _clearStatus() {
|
||||
this._status.value = "";
|
||||
this._statusIcon.removeAttribute("status");
|
||||
},
|
||||
|
||||
_updateStatus: function Change__updateStatus(str, state) {
|
||||
this._updateStatusWithString(this._str(str), state);
|
||||
},
|
||||
|
||||
_updateStatusWithString: function Change__updateStatusWithString(string, state) {
|
||||
this._statusRow.hidden = false;
|
||||
this._status.value = string;
|
||||
this._statusIcon.setAttribute("status", state);
|
||||
|
||||
let error = state == "error";
|
||||
this._dialog.getButton("cancel").disabled = !error;
|
||||
this._dialog.getButton("finish").disabled = !error;
|
||||
document.getElementById("printSyncKeyButton").disabled = !error;
|
||||
document.getElementById("saveSyncKeyButton").disabled = !error;
|
||||
|
||||
if (state == "success")
|
||||
window.setTimeout(window.close, 1500);
|
||||
},
|
||||
|
||||
onDialogAccept: function() {
|
||||
switch (this._dialogType) {
|
||||
case "UpdatePassphrase":
|
||||
case "ResetPassphrase":
|
||||
return this.doChangePassphrase();
|
||||
break;
|
||||
case "ChangePassword":
|
||||
return this.doChangePassword();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
doGeneratePassphrase: function () {
|
||||
let passphrase = Weave.Utils.generatePassphrase();
|
||||
this._passphraseBox.value = Weave.Utils.hyphenatePassphrase(passphrase);
|
||||
this._dialog.getButton("finish").disabled = false;
|
||||
},
|
||||
|
||||
doChangePassphrase: function Change_doChangePassphrase() {
|
||||
let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value);
|
||||
if (this._updatingPassphrase) {
|
||||
Weave.Service.identity.syncKey = pp;
|
||||
if (Weave.Service.login()) {
|
||||
this._updateStatus("change.recoverykey.success", "success");
|
||||
Weave.Service.persistLogin();
|
||||
Weave.Service.scheduler.delayedAutoConnect(0);
|
||||
}
|
||||
else {
|
||||
this._updateStatus("new.passphrase.status.incorrect", "error");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._updateStatus("change.recoverykey.label", "active");
|
||||
|
||||
if (Weave.Service.changePassphrase(pp))
|
||||
this._updateStatus("change.recoverykey.success", "success");
|
||||
else
|
||||
this._updateStatus("change.recoverykey.error", "error");
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
doChangePassword: function Change_doChangePassword() {
|
||||
if (this._currentPasswordInvalid) {
|
||||
Weave.Service.identity.basicPassword = this._firstBox.value;
|
||||
if (Weave.Service.login()) {
|
||||
this._updateStatus("change.password.status.success", "success");
|
||||
Weave.Service.persistLogin();
|
||||
}
|
||||
else {
|
||||
this._updateStatus("new.password.status.incorrect", "error");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._updateStatus("change.password.status.active", "active");
|
||||
|
||||
if (Weave.Service.changePassword(this._firstBox.value))
|
||||
this._updateStatus("change.password.status.success", "success");
|
||||
else
|
||||
this._updateStatus("change.password.status.error", "error");
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
validate: function (event) {
|
||||
let valid = false;
|
||||
let errorString = "";
|
||||
|
||||
if (this._dialogType == "ChangePassword") {
|
||||
if (this._currentPasswordInvalid)
|
||||
[valid, errorString] = gSyncUtils.validatePassword(this._firstBox);
|
||||
else
|
||||
[valid, errorString] = gSyncUtils.validatePassword(this._firstBox, this._secondBox);
|
||||
}
|
||||
else {
|
||||
//Pale Moon: Enforce minimum length of 8 for allowed custom passphrase
|
||||
//and don't restrict it to "out of sync" situations only. People who
|
||||
//go to this page generally know what they are doing ;)
|
||||
valid = this._passphraseBox.value.length >= 8;
|
||||
}
|
||||
|
||||
if (errorString == "")
|
||||
this._clearStatus();
|
||||
else
|
||||
this._updateStatusWithString(errorString, "error");
|
||||
|
||||
this._statusRow.hidden = valid;
|
||||
this._dialog.getButton("finish").disabled = !valid;
|
||||
},
|
||||
|
||||
_str: function Change__string(str) {
|
||||
return this._stringBundle.GetStringFromName(str);
|
||||
}
|
||||
};
|
||||
123
application/basilisk/components/sync/genericChange.xul
Normal file
123
application/basilisk/components/sync/genericChange.xul
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
|
||||
%brandDTD;
|
||||
%syncBrandDTD;
|
||||
%syncSetupDTD;
|
||||
]>
|
||||
<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
id="change-dialog"
|
||||
windowtype="Weave:ChangeSomething"
|
||||
persist="screenX screenY"
|
||||
onwizardnext="Change.onLoad()"
|
||||
onwizardfinish="return Change.onDialogAccept();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/genericChange.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/utils.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/printUtils.js"/>
|
||||
|
||||
<wizardpage id="change-page"
|
||||
label="">
|
||||
|
||||
<description id="introText">
|
||||
</description>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<groupbox>
|
||||
<grid>
|
||||
<columns>
|
||||
<column align="right"/>
|
||||
<column flex="3"/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row id="textBox1Row" align="center">
|
||||
<label id="textBox1Label" control="textBox1"/>
|
||||
<textbox id="textBox1" type="password" oninput="Change.validate()"/>
|
||||
<spacer/>
|
||||
</row>
|
||||
<row id="textBox2Row" align="center">
|
||||
<label id="textBox2Label" control="textBox2"/>
|
||||
<textbox id="textBox2" type="password" oninput="Change.validate()"/>
|
||||
<spacer/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
|
||||
<vbox id="passphraseRow">
|
||||
<hbox flex="1">
|
||||
<label id="passphraseLabel" control="passphraseBox"/>
|
||||
<spacer flex="1"/>
|
||||
<label id="generatePassphraseButton"
|
||||
hidden="true"
|
||||
value="&syncGenerateNewKey.label;"
|
||||
class="text-link inline-link"
|
||||
onclick="event.stopPropagation();
|
||||
Change.doGeneratePassphrase();"/>
|
||||
</hbox>
|
||||
<textbox id="passphraseBox"
|
||||
flex="1"
|
||||
onfocus="this.select()"
|
||||
oninput="Change.validate()"/>
|
||||
</vbox>
|
||||
|
||||
<vbox id="feedback" pack="center">
|
||||
<hbox id="statusRow" align="center">
|
||||
<image id="statusIcon" class="statusIcon"/>
|
||||
<label id="status" class="status" value=" "/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<hbox id="passphraseBackupButtons"
|
||||
hidden="true"
|
||||
pack="center">
|
||||
<button id="printSyncKeyButton"
|
||||
label="&button.syncKeyBackup.print.label;"
|
||||
accesskey="&button.syncKeyBackup.print.accesskey;"
|
||||
oncommand="gSyncUtils.passphrasePrint('passphraseBox');"/>
|
||||
<button id="saveSyncKeyButton"
|
||||
label="&button.syncKeyBackup.save.label;"
|
||||
accesskey="&button.syncKeyBackup.save.accesskey;"
|
||||
oncommand="gSyncUtils.passphraseSave('passphraseBox');"/>
|
||||
</hbox>
|
||||
|
||||
<vbox id="passphraseHelpBox"
|
||||
hidden="true">
|
||||
<description>
|
||||
&existingRecoveryKey.description;
|
||||
<label class="text-link"
|
||||
href="http://www.palemoon.org/sync/help/recoverykey.shtml">
|
||||
&addDevice.showMeHow.label;
|
||||
</label>
|
||||
</description>
|
||||
</vbox>
|
||||
|
||||
<spacer id="passphraseSpacer"
|
||||
flex="1"
|
||||
hidden="true"/>
|
||||
|
||||
<description id="warningText" class="data">
|
||||
</description>
|
||||
|
||||
<spacer flex="1"/>
|
||||
</wizardpage>
|
||||
</wizard>
|
||||
22
application/basilisk/components/sync/jar.mn
Normal file
22
application/basilisk/components/sync/jar.mn
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/sync/aboutSyncTabs.xul
|
||||
content/browser/sync/aboutSyncTabs.js
|
||||
content/browser/sync/aboutSyncTabs.css
|
||||
content/browser/sync/aboutSyncTabs-bindings.xml
|
||||
content/browser/sync/setup.xul
|
||||
content/browser/sync/addDevice.js
|
||||
content/browser/sync/addDevice.xul
|
||||
content/browser/sync/setup.js
|
||||
content/browser/sync/genericChange.xul
|
||||
content/browser/sync/genericChange.js
|
||||
content/browser/sync/key.xhtml
|
||||
content/browser/sync/notification.xml
|
||||
content/browser/sync/quota.xul
|
||||
content/browser/sync/quota.js
|
||||
content/browser/sync/utils.js
|
||||
content/browser/sync/progress.js
|
||||
content/browser/sync/progress.xhtml
|
||||
54
application/basilisk/components/sync/key.xhtml
Normal file
54
application/basilisk/components/sync/key.xhtml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
%syncBrandDTD;
|
||||
<!ENTITY % syncKeyDTD SYSTEM "chrome://browser/locale/syncKey.dtd">
|
||||
%syncKeyDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
|
||||
%globalDTD;
|
||||
]>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&syncKey.page.title;</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<meta name="robots" content="noindex"/>
|
||||
<style type="text/css">
|
||||
#synckey { font-size: 150% }
|
||||
footer { font-size: 70% }
|
||||
/* Bug 575675: Need to have an a:visited rule in a chrome document. */
|
||||
a:visited { color: purple; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
<h1>&syncKey.page.title;</h1>
|
||||
|
||||
<p id="synckey" dir="ltr">SYNCKEY</p>
|
||||
|
||||
<p>&syncKey.page.description2;</p>
|
||||
|
||||
<div id="column1">
|
||||
<h2>&syncKey.keepItSecret.heading;</h2>
|
||||
<p>&syncKey.keepItSecret.description;</p>
|
||||
</div>
|
||||
|
||||
<div id="column2">
|
||||
<h2>&syncKey.keepItSafe.heading;</h2>
|
||||
<p><em>&syncKey.keepItSafe1.description;</em>&syncKey.keepItSafe2.description;<em>&syncKey.keepItSafe3.description;</em>&syncKey.keepItSafe4a.description;</p>
|
||||
</div>
|
||||
|
||||
<p>&syncKey.findOutMore1.label;<a href="http://www.palemoon.org/sync/">http://www.palemoon.org/sync/</a>&syncKey.findOutMore2.label;</p>
|
||||
|
||||
<footer>
|
||||
&syncKey.footer1.label;<a id="tosLink" href="termsURL">termsURL</a>&syncKey.footer2.label;<a id="ppLink" href="privacyURL">privacyURL</a>&syncKey.footer3.label;
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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']
|
||||
|
||||
129
application/basilisk/components/sync/notification.xml
Normal file
129
application/basilisk/components/sync/notification.xml
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
|
||||
%notificationDTD;
|
||||
]>
|
||||
|
||||
<bindings id="notificationBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="notificationbox" extends="chrome://global/content/bindings/notification.xml#notificationbox">
|
||||
<content>
|
||||
<xul:vbox xbl:inherits="hidden=notificationshidden">
|
||||
<xul:spacer/>
|
||||
<children includes="notification"/>
|
||||
</xul:vbox>
|
||||
<children/>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
let temp = {};
|
||||
Cu.import("resource://services-common/observers.js", temp);
|
||||
temp.Observers.add("weave:notification:added", this.onNotificationAdded, this);
|
||||
temp.Observers.add("weave:notification:removed", this.onNotificationRemoved, this);
|
||||
|
||||
for each (var notification in Weave.Notifications.notifications)
|
||||
this._appendNotification(notification);
|
||||
]]></constructor>
|
||||
|
||||
<destructor><![CDATA[
|
||||
let temp = {};
|
||||
Cu.import("resource://services-common/observers.js", temp);
|
||||
temp.Observers.remove("weave:notification:added", this.onNotificationAdded, this);
|
||||
temp.Observers.remove("weave:notification:removed", this.onNotificationRemoved, this);
|
||||
]]></destructor>
|
||||
|
||||
<method name="onNotificationAdded">
|
||||
<parameter name="subject"/>
|
||||
<parameter name="data"/>
|
||||
<body><![CDATA[
|
||||
this._appendNotification(subject);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onNotificationRemoved">
|
||||
<parameter name="subject"/>
|
||||
<parameter name="data"/>
|
||||
<body><![CDATA[
|
||||
// If the view of the notification hasn't been removed yet, remove it.
|
||||
var notifications = this.allNotifications;
|
||||
for each (var notification in notifications) {
|
||||
if (notification.notification == subject) {
|
||||
notification.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_appendNotification">
|
||||
<parameter name="notification"/>
|
||||
<body><![CDATA[
|
||||
var node = this.appendNotification(notification.description,
|
||||
notification.title,
|
||||
notification.iconURL,
|
||||
notification.priority,
|
||||
notification.buttons);
|
||||
node.notification = notification;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="notification" extends="chrome://global/content/bindings/notification.xml#notification">
|
||||
<content>
|
||||
<xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type">
|
||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||
class="messageCloseButton close-icon tabbable"
|
||||
xbl:inherits="hidden=hideclose"
|
||||
tooltiptext="&closeNotification.tooltip;"
|
||||
oncommand="document.getBindingParent(this).close()"/>
|
||||
<xul:hbox anonid="details" align="center" flex="1">
|
||||
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type"/>
|
||||
<xul:description anonid="messageText" class="messageText" xbl:inherits="xbl:text=label"/>
|
||||
|
||||
<!-- The children are the buttons defined by the notification. -->
|
||||
<xul:hbox oncommand="document.getBindingParent(this)._doButtonCommand(event);">
|
||||
<children/>
|
||||
</xul:hbox>
|
||||
</xul:hbox>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
<implementation>
|
||||
<!-- Note: this used to be a field, but for some reason it kept getting
|
||||
- reset to its default value for TabNotification elements.
|
||||
- As a property, that doesn't happen, even though the property stores
|
||||
- its value in a JS property |_notification| that is not defined
|
||||
- in XBL as a field or property. Maybe this is wrong, but it works.
|
||||
-->
|
||||
<property name="notification"
|
||||
onget="return this._notification"
|
||||
onset="this._notification = val; return val;"/>
|
||||
<method name="close">
|
||||
<body><![CDATA[
|
||||
Weave.Notifications.remove(this.notification);
|
||||
|
||||
// We should be able to call the base class's close method here
|
||||
// to remove the notification element from the notification box,
|
||||
// but we can't because of bug 373652, so instead we copied its code
|
||||
// and execute it below.
|
||||
var control = this.control;
|
||||
if (control)
|
||||
control.removeNotification(this);
|
||||
else
|
||||
this.hidden = true;
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
||||
71
application/basilisk/components/sync/progress.js
Normal file
71
application/basilisk/components/sync/progress.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://services-sync/main.js");
|
||||
|
||||
var gProgressBar;
|
||||
var gCounter = 0;
|
||||
|
||||
function onLoad(event) {
|
||||
Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
|
||||
Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
|
||||
Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
|
||||
Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
|
||||
|
||||
gProgressBar = document.getElementById('uploadProgressBar');
|
||||
|
||||
if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
gProgressBar.hidden = false;
|
||||
}
|
||||
else {
|
||||
gProgressBar.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function onUnload(event) {
|
||||
cleanUpObservers();
|
||||
}
|
||||
|
||||
function cleanUpObservers() {
|
||||
try {
|
||||
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish");
|
||||
Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error");
|
||||
Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish");
|
||||
Services.obs.removeObserver(onServiceSync, "weave:service:sync:error");
|
||||
}
|
||||
catch (e) {
|
||||
// may be double called by unload & exit. Ignore.
|
||||
}
|
||||
}
|
||||
|
||||
function onEngineSync(subject, topic, data) {
|
||||
// The Clients engine syncs first. At this point we don't necessarily know
|
||||
// yet how many engines will be enabled, so we'll ignore the Clients engine
|
||||
// and evaluate how many engines are enabled when the first "real" engine
|
||||
// syncs.
|
||||
if (data == "clients") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gCounter &&
|
||||
Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
|
||||
gProgressBar.max = Weave.Service.engineManager.getEnabled().length;
|
||||
}
|
||||
|
||||
gCounter += 1;
|
||||
gProgressBar.setAttribute("value", gCounter);
|
||||
}
|
||||
|
||||
function onServiceSync(subject, topic, data) {
|
||||
// To address the case where 0 engines are synced, we will fill the
|
||||
// progress bar so the user knows that the sync has finished.
|
||||
gProgressBar.setAttribute("value", gProgressBar.max);
|
||||
cleanUpObservers();
|
||||
}
|
||||
|
||||
function closeTab() {
|
||||
window.close();
|
||||
}
|
||||
55
application/basilisk/components/sync/progress.xhtml
Normal file
55
application/basilisk/components/sync/progress.xhtml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % htmlDTD
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % syncProgressDTD
|
||||
SYSTEM "chrome://browser/locale/syncProgress.dtd">
|
||||
%syncProgressDTD;
|
||||
<!ENTITY % syncSetupDTD
|
||||
SYSTEM "chrome://browser/locale/syncSetup.dtd">
|
||||
%syncSetupDTD;
|
||||
<!ENTITY % globalDTD
|
||||
SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&syncProgress.pageTitle;</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="all"
|
||||
href="chrome://browser/skin/syncProgress.css"/>
|
||||
|
||||
<link rel="icon" type="image/png" id="favicon"
|
||||
href="chrome://browser/skin/sync-16.png"/>
|
||||
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/sync/progress.js"/>
|
||||
</head>
|
||||
<body onload="onLoad(event)" onunload="onUnload(event)" dir="&locale.dir;">
|
||||
<title>&setup.successPage.title;</title>
|
||||
<div id="floatingBox" class="main-content">
|
||||
<div id="title">
|
||||
<h1>&setup.successPage.title;</h1>
|
||||
</div>
|
||||
<div id="successLogo">
|
||||
<img id="brandSyncLogo" src="chrome://browser/skin/sync-128.png" alt="&syncProgress.logoAltText;" />
|
||||
</div>
|
||||
<div id="loadingText">
|
||||
<p id="blurb">&syncProgress.textBlurb; </p>
|
||||
</div>
|
||||
<div id="progressBar">
|
||||
<progress id="uploadProgressBar" value="0"/>
|
||||
</div>
|
||||
<div id="bottomRow">
|
||||
<button id="closeButton" onclick="closeTab()">&syncProgress.closeButton; </button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
247
application/basilisk/components/sync/quota.js
Normal file
247
application/basilisk/components/sync/quota.js
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://services-sync/main.js");
|
||||
Cu.import("resource://gre/modules/DownloadUtils.jsm");
|
||||
|
||||
var gSyncQuota = {
|
||||
|
||||
init: function init() {
|
||||
this.bundle = document.getElementById("quotaStrings");
|
||||
let caption = document.getElementById("treeCaption");
|
||||
caption.firstChild.nodeValue = this.bundle.getString("quota.treeCaption.label");
|
||||
|
||||
gUsageTreeView.init();
|
||||
this.tree = document.getElementById("usageTree");
|
||||
this.tree.view = gUsageTreeView;
|
||||
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
loadData: function loadData() {
|
||||
this._usage_req = Weave.Service.getStorageInfo(Weave.INFO_COLLECTION_USAGE,
|
||||
function (error, usage) {
|
||||
delete gSyncQuota._usage_req;
|
||||
// displayUsageData handles null values, so no need to check 'error'.
|
||||
gUsageTreeView.displayUsageData(usage);
|
||||
});
|
||||
|
||||
let usageLabel = document.getElementById("usageLabel");
|
||||
let bundle = this.bundle;
|
||||
|
||||
this._quota_req = Weave.Service.getStorageInfo(Weave.INFO_QUOTA,
|
||||
function (error, quota) {
|
||||
delete gSyncQuota._quota_req;
|
||||
|
||||
if (error) {
|
||||
usageLabel.value = bundle.getString("quota.usageError.label");
|
||||
return;
|
||||
}
|
||||
let used = gSyncQuota.convertKB(quota[0]);
|
||||
if (!quota[1]) {
|
||||
// No quota on the server.
|
||||
usageLabel.value = bundle.getFormattedString(
|
||||
"quota.usageNoQuota.label", used);
|
||||
return;
|
||||
}
|
||||
let percent = Math.round(100 * quota[0] / quota[1]);
|
||||
let total = gSyncQuota.convertKB(quota[1]);
|
||||
usageLabel.value = bundle.getFormattedString(
|
||||
"quota.usagePercentage.label", [percent].concat(used).concat(total));
|
||||
});
|
||||
},
|
||||
|
||||
onCancel: function onCancel() {
|
||||
if (this._usage_req) {
|
||||
this._usage_req.abort();
|
||||
}
|
||||
if (this._quota_req) {
|
||||
this._quota_req.abort();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
onAccept: function onAccept() {
|
||||
let engines = gUsageTreeView.getEnginesToDisable();
|
||||
for each (let engine in engines) {
|
||||
Weave.Service.engineManager.get(engine).enabled = false;
|
||||
}
|
||||
if (engines.length) {
|
||||
// The 'Weave' object will disappear once the window closes.
|
||||
let Service = Weave.Service;
|
||||
Weave.Utils.nextTick(function() { Service.sync(); });
|
||||
}
|
||||
return this.onCancel();
|
||||
},
|
||||
|
||||
convertKB: function convertKB(value) {
|
||||
return DownloadUtils.convertByteUnits(value * 1024);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var gUsageTreeView = {
|
||||
|
||||
_ignored: {keys: true,
|
||||
meta: true,
|
||||
clients: true},
|
||||
|
||||
/*
|
||||
* Internal data structures underlaying the tree.
|
||||
*/
|
||||
_collections: [],
|
||||
_byname: {},
|
||||
|
||||
init: function init() {
|
||||
let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label");
|
||||
for each (let engine in Weave.Service.engineManager.getEnabled()) {
|
||||
if (this._ignored[engine.name])
|
||||
continue;
|
||||
|
||||
// Some engines use the same pref, which means they can only be turned on
|
||||
// and off together. We need to combine them here as well.
|
||||
let existing = this._byname[engine.prefName];
|
||||
if (existing) {
|
||||
existing.engines.push(engine.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
let obj = {name: engine.prefName,
|
||||
title: this._collectionTitle(engine),
|
||||
engines: [engine.name],
|
||||
enabled: true,
|
||||
sizeLabel: retrievingLabel};
|
||||
this._collections.push(obj);
|
||||
this._byname[engine.prefName] = obj;
|
||||
}
|
||||
},
|
||||
|
||||
_collectionTitle: function _collectionTitle(engine) {
|
||||
try {
|
||||
return gSyncQuota.bundle.getString(
|
||||
"collection." + engine.prefName + ".label");
|
||||
} catch (ex) {
|
||||
return engine.Name;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Process the quota information as returned by info/collection_usage.
|
||||
*/
|
||||
displayUsageData: function displayUsageData(data) {
|
||||
for each (let coll in this._collections) {
|
||||
coll.size = 0;
|
||||
// If we couldn't retrieve any data, just blank out the label.
|
||||
if (!data) {
|
||||
coll.sizeLabel = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
for each (let engineName in coll.engines)
|
||||
coll.size += data[engineName] || 0;
|
||||
let sizeLabel = "";
|
||||
sizeLabel = gSyncQuota.bundle.getFormattedString(
|
||||
"quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size));
|
||||
coll.sizeLabel = sizeLabel;
|
||||
}
|
||||
let sizeColumn = this.treeBox.columns.getNamedColumn("size");
|
||||
this.treeBox.invalidateColumn(sizeColumn);
|
||||
},
|
||||
|
||||
/*
|
||||
* Handle click events on the tree.
|
||||
*/
|
||||
onTreeClick: function onTreeClick(event) {
|
||||
if (event.button == 2)
|
||||
return;
|
||||
|
||||
let cell = this.treeBox.getCellAt(event.clientX, event.clientY);
|
||||
if (cell.col && cell.col.id == "enabled")
|
||||
this.toggle(cell.row);
|
||||
},
|
||||
|
||||
/*
|
||||
* Toggle enabled state of an engine.
|
||||
*/
|
||||
toggle: function toggle(row) {
|
||||
// Update the tree
|
||||
let collection = this._collections[row];
|
||||
collection.enabled = !collection.enabled;
|
||||
this.treeBox.invalidateRow(row);
|
||||
},
|
||||
|
||||
/*
|
||||
* Return a list of engines (or rather their pref names) that should be
|
||||
* disabled.
|
||||
*/
|
||||
getEnginesToDisable: function getEnginesToDisable() {
|
||||
// Tycho: return [coll.name for each (coll in this._collections) if (!coll.enabled)];
|
||||
let engines = [];
|
||||
for each (let coll in this._collections) {
|
||||
if (!coll.enabled) {
|
||||
engines.push(coll.name);
|
||||
}
|
||||
}
|
||||
return engines;
|
||||
},
|
||||
|
||||
// nsITreeView
|
||||
|
||||
get rowCount() {
|
||||
return this._collections.length;
|
||||
},
|
||||
|
||||
getRowProperties: function(index) { return ""; },
|
||||
getCellProperties: function(row, col) { return ""; },
|
||||
getColumnProperties: function(col) { return ""; },
|
||||
isContainer: function(index) { return false; },
|
||||
isContainerOpen: function(index) { return false; },
|
||||
isContainerEmpty: function(index) { return false; },
|
||||
isSeparator: function(index) { return false; },
|
||||
isSorted: function() { return false; },
|
||||
canDrop: function(index, orientation, dataTransfer) { return false; },
|
||||
drop: function(row, orientation, dataTransfer) {},
|
||||
getParentIndex: function(rowIndex) {},
|
||||
hasNextSibling: function(rowIndex, afterIndex) { return false; },
|
||||
getLevel: function(index) { return 0; },
|
||||
getImageSrc: function(row, col) {},
|
||||
|
||||
getCellValue: function(row, col) {
|
||||
return this._collections[row].enabled;
|
||||
},
|
||||
|
||||
getCellText: function getCellText(row, col) {
|
||||
let collection = this._collections[row];
|
||||
switch (col.id) {
|
||||
case "collection":
|
||||
return collection.title;
|
||||
case "size":
|
||||
return collection.sizeLabel;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
setTree: function setTree(tree) {
|
||||
this.treeBox = tree;
|
||||
},
|
||||
|
||||
toggleOpenState: function(index) {},
|
||||
cycleHeader: function(col) {},
|
||||
selectionChanged: function() {},
|
||||
cycleCell: function(row, col) {},
|
||||
isEditable: function(row, col) { return false; },
|
||||
isSelectable: function (row, col) { return false; },
|
||||
setCellValue: function(row, col, value) {},
|
||||
setCellText: function(row, col, value) {},
|
||||
performAction: function(action) {},
|
||||
performActionOnRow: function(action, row) {},
|
||||
performActionOnCell: function(action, row, col) {}
|
||||
|
||||
};
|
||||
65
application/basilisk/components/sync/quota.xul
Normal file
65
application/basilisk/components/sync/quota.xul
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncQuota.css"?>
|
||||
|
||||
<!DOCTYPE dialog [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
<!ENTITY % syncQuotaDTD SYSTEM "chrome://browser/locale/syncQuota.dtd">
|
||||
%brandDTD;
|
||||
%syncBrandDTD;
|
||||
%syncQuotaDTD;
|
||||
]>
|
||||
<dialog id="quotaDialog"
|
||||
windowtype="Sync:ViewQuota"
|
||||
persist="screenX screenY width height"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
onload="gSyncQuota.init()"
|
||||
buttons="accept,cancel"
|
||||
title=""a.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=""a.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=""a.typeColumn.label;"
|
||||
flex="1"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="size"
|
||||
label=""a.sizeColumn.label;"
|
||||
flex="1"/>
|
||||
</treecols>
|
||||
<treechildren flex="1"/>
|
||||
</tree>
|
||||
<separator/>
|
||||
<description id="treeCaption"> </description>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
||||
1071
application/basilisk/components/sync/setup.js
Normal file
1071
application/basilisk/components/sync/setup.js
Normal file
File diff suppressed because it is too large
Load diff
491
application/basilisk/components/sync/setup.xul
Normal file
491
application/basilisk/components/sync/setup.xul
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncSetup.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/syncCommon.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
<!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
|
||||
%brandDTD;
|
||||
%syncBrandDTD;
|
||||
%syncSetupDTD;
|
||||
]>
|
||||
<wizard id="wizard"
|
||||
title="&accountSetupTitle.label;"
|
||||
windowtype="Weave:AccountSetup"
|
||||
persist="screenX screenY"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
onwizardnext="return gSyncSetup.onWizardAdvance()"
|
||||
onwizardback="return gSyncSetup.onWizardBack()"
|
||||
onwizardcancel="gSyncSetup.onWizardCancel()"
|
||||
onload="gSyncSetup.init()">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/setup.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/utils.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/printUtils.js"/>
|
||||
|
||||
<wizardpage id="addDevicePage"
|
||||
label="&pairDevice.title.label;"
|
||||
onpageshow="gSyncSetup.onPageShow()">
|
||||
<description>
|
||||
&pairDevice.dialog.description.label;
|
||||
<label class="text-link"
|
||||
value="&addDevice.showMeHow.label;"
|
||||
href="http://www.palemoon.org/sync/help/easy-setup.shtml"/>
|
||||
</description>
|
||||
<separator class="groove-thin"/>
|
||||
<description>
|
||||
&addDevice.dialog.enterCode.label;
|
||||
</description>
|
||||
<separator class="groove-thin"/>
|
||||
<vbox align="center">
|
||||
<textbox id="pin1"
|
||||
class="pin"
|
||||
oninput="gSyncSetup.onPINInput(this);"
|
||||
onfocus="this.select();"
|
||||
/>
|
||||
<textbox id="pin2"
|
||||
class="pin"
|
||||
oninput="gSyncSetup.onPINInput(this);"
|
||||
onfocus="this.select();"
|
||||
/>
|
||||
<textbox id="pin3"
|
||||
class="pin"
|
||||
oninput="gSyncSetup.onPINInput(this);"
|
||||
onfocus="this.select();"
|
||||
/>
|
||||
</vbox>
|
||||
<separator class="groove-thin"/>
|
||||
<vbox id="pairDeviceThrobber" align="center" hidden="true">
|
||||
<image/>
|
||||
</vbox>
|
||||
<hbox id="pairDeviceErrorRow" pack="center" hidden="true">
|
||||
<image class="statusIcon" status="error"/>
|
||||
<label class="status"
|
||||
value="&addDevice.dialog.tryAgain.label;"/>
|
||||
</hbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="pickSetupType"
|
||||
label="&syncBrand.fullName.label;"
|
||||
onpageshow="gSyncSetup.onPageShow()">
|
||||
<vbox align="center" flex="1">
|
||||
<description style="padding: 0 7em;">
|
||||
&setup.pickSetupType.description2;
|
||||
</description>
|
||||
<spacer flex="3"/>
|
||||
<button id="newAccount"
|
||||
class="accountChoiceButton"
|
||||
label="&button.createNewAccount.label;"
|
||||
oncommand="gSyncSetup.startNewAccountSetup()"
|
||||
align="center"/>
|
||||
<spacer flex="1"/>
|
||||
</vbox>
|
||||
<separator class="groove"/>
|
||||
<vbox align="center" flex="1">
|
||||
<spacer flex="1"/>
|
||||
<button id="existingAccount"
|
||||
class="accountChoiceButton"
|
||||
label="&button.haveAccount.label;"
|
||||
oncommand="gSyncSetup.useExistingAccount()"/>
|
||||
<spacer flex="3"/>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage label="&setup.newAccountDetailsPage.title.label;"
|
||||
id="newAccountStart"
|
||||
onextra1="gSyncSetup.onSyncOptions()"
|
||||
onpageshow="gSyncSetup.onPageShow();">
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column class="inputColumn" flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row id="emailRow" align="center">
|
||||
<label value="&setup.emailAddress.label;"
|
||||
accesskey="&setup.emailAddress.accesskey;"
|
||||
control="weaveEmail"/>
|
||||
<textbox id="weaveEmail"
|
||||
oninput="gSyncSetup.onEmailInput()"/>
|
||||
</row>
|
||||
<row id="emailFeedbackRow" align="center" hidden="true">
|
||||
<spacer/>
|
||||
<hbox>
|
||||
<image class="statusIcon"/>
|
||||
<label class="status" value=" "/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row id="passwordRow" align="center">
|
||||
<label value="&setup.choosePassword.label;"
|
||||
accesskey="&setup.choosePassword.accesskey;"
|
||||
control="weavePassword"/>
|
||||
<textbox id="weavePassword"
|
||||
type="password"
|
||||
onchange="gSyncSetup.onPasswordChange()"/>
|
||||
</row>
|
||||
<row id="confirmRow" align="center">
|
||||
<label value="&setup.confirmPassword.label;"
|
||||
accesskey="&setup.confirmPassword.accesskey;"
|
||||
control="weavePasswordConfirm"/>
|
||||
<textbox id="weavePasswordConfirm"
|
||||
type="password"
|
||||
onchange="gSyncSetup.onPasswordChange()"/>
|
||||
</row>
|
||||
<row id="passwordFeedbackRow" align="center" hidden="true">
|
||||
<spacer/>
|
||||
<hbox>
|
||||
<image class="statusIcon"/>
|
||||
<label class="status" value=" "/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row align="center">
|
||||
<label control="server"
|
||||
value="&server.label;"/>
|
||||
<menulist id="server"
|
||||
oncommand="gSyncSetup.onServerCommand()"
|
||||
oninput="gSyncSetup.onServerInput()">
|
||||
<menupopup>
|
||||
<menuitem label="&serverType.default.label;"
|
||||
value="main"/>
|
||||
<menuitem label="&serverType.custom2.label;"
|
||||
value="custom"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</row>
|
||||
<row id="serverFeedbackRow" align="center" hidden="true">
|
||||
<spacer/>
|
||||
<hbox>
|
||||
<image class="statusIcon"/>
|
||||
<label class="status" value=" "/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row id="TOSRow" align="center">
|
||||
<spacer/>
|
||||
<hbox align="center">
|
||||
<checkbox id="tos"
|
||||
accesskey="&setup.tosAgree1.accesskey;"
|
||||
oncommand="this.focus(); gSyncSetup.checkFields();"/>
|
||||
<description id="tosDesc"
|
||||
flex="1"
|
||||
onclick="document.getElementById('tos').focus();
|
||||
document.getElementById('tos').click()">
|
||||
&setup.tosAgree1.label;
|
||||
<label class="text-link inline-link"
|
||||
onclick="event.stopPropagation();gSyncUtils.openToS();">
|
||||
&setup.tosLink.label;
|
||||
</label>
|
||||
&setup.tosAgree2.label;
|
||||
<label class="text-link inline-link"
|
||||
onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();">
|
||||
&setup.ppLink.label;
|
||||
</label>
|
||||
&setup.tosAgree3.label;
|
||||
</description>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<spacer flex="1"/>
|
||||
<vbox flex="1" align="center">
|
||||
<browser height="150"
|
||||
width="500"
|
||||
id="captcha"
|
||||
type="content"
|
||||
disablehistory="true"/>
|
||||
<spacer flex="1"/>
|
||||
<hbox id="captchaFeedback">
|
||||
<image class="statusIcon"/>
|
||||
<label class="status" value=" "/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="addDevice"
|
||||
label="&pairDevice.title.label;"
|
||||
onextra1="gSyncSetup.onSyncOptions()"
|
||||
onpageshow="gSyncSetup.onPageShow()">
|
||||
<description>
|
||||
&pairDevice.setup.description.label;
|
||||
<label class="text-link"
|
||||
value="&addDevice.showMeHow.label;"
|
||||
href="http://www.palemoon.org/sync/help/easy-setup.shtml"/>
|
||||
</description>
|
||||
<label value="&addDevice.setup.enterCode.label;"
|
||||
control="easySetupPIN1"/>
|
||||
<spacer flex="1"/>
|
||||
<vbox align="center" flex="1">
|
||||
<textbox id="easySetupPIN1"
|
||||
class="pin"
|
||||
value=""
|
||||
readonly="true"
|
||||
/>
|
||||
<textbox id="easySetupPIN2"
|
||||
class="pin"
|
||||
value=""
|
||||
readonly="true"
|
||||
/>
|
||||
<textbox id="easySetupPIN3"
|
||||
class="pin"
|
||||
value=""
|
||||
readonly="true"
|
||||
/>
|
||||
</vbox>
|
||||
<spacer flex="3"/>
|
||||
<label class="text-link"
|
||||
value="&addDevice.dontHaveDevice.label;"
|
||||
onclick="gSyncSetup.manualSetup();"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="existingAccount"
|
||||
label="&setup.signInPage.title.label;"
|
||||
onextra1="gSyncSetup.onSyncOptions()"
|
||||
onpageshow="gSyncSetup.onPageShow()">
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column class="inputColumn" flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row id="existingAccountRow" align="center">
|
||||
<label id="existingAccountLabel"
|
||||
value="&signIn.account2.label;"
|
||||
accesskey="&signIn.account2.accesskey;"
|
||||
control="existingAccount"/>
|
||||
<textbox id="existingAccountName"
|
||||
oninput="gSyncSetup.checkFields(event)"
|
||||
onchange="gSyncSetup.checkFields(event)"/>
|
||||
</row>
|
||||
<row id="existingPasswordRow" align="center">
|
||||
<label id="existingPasswordLabel"
|
||||
value="&signIn.password.label;"
|
||||
accesskey="&signIn.password.accesskey;"
|
||||
control="existingPassword"/>
|
||||
<textbox id="existingPassword"
|
||||
type="password"
|
||||
onkeyup="gSyncSetup.checkFields(event)"
|
||||
onchange="gSyncSetup.checkFields(event)"/>
|
||||
</row>
|
||||
<row id="existingPasswordFeedbackRow" align="center" hidden="true">
|
||||
<spacer/>
|
||||
<hbox>
|
||||
<image class="statusIcon"/>
|
||||
<label class="status" value=" "/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row align="center">
|
||||
<spacer/>
|
||||
<label class="text-link"
|
||||
value="&resetPassword.label;"
|
||||
onclick="gSyncUtils.resetPassword(); return false;"/>
|
||||
</row>
|
||||
<row align="center">
|
||||
<label control="existingServer"
|
||||
value="&server.label;"/>
|
||||
<menulist id="existingServer"
|
||||
oncommand="gSyncSetup.onExistingServerCommand()"
|
||||
oninput="gSyncSetup.onExistingServerInput()">
|
||||
<menupopup>
|
||||
<menuitem label="&serverType.default.label;"
|
||||
value="main"/>
|
||||
<menuitem label="&serverType.custom2.label;"
|
||||
value="custom"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</row>
|
||||
<row id="existingServerFeedbackRow" align="center" hidden="true">
|
||||
<spacer/>
|
||||
<hbox>
|
||||
<image class="statusIcon"/>
|
||||
<vbox>
|
||||
<label class="status" value=" "/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
|
||||
<groupbox>
|
||||
<label id="existingPassphraseLabel"
|
||||
value="&signIn.recoveryKey.label;"
|
||||
accesskey="&signIn.recoveryKey.accesskey;"
|
||||
control="existingPassphrase"/>
|
||||
<textbox id="existingPassphrase"
|
||||
oninput="gSyncSetup.checkFields()"/>
|
||||
<hbox id="login-throbber" hidden="true">
|
||||
<image/>
|
||||
<label value="&verifying.label;"/>
|
||||
</hbox>
|
||||
<vbox align="left" id="existingPassphraseFeedbackRow" hidden="true">
|
||||
<hbox>
|
||||
<image class="statusIcon"/>
|
||||
<label class="status" value=" "/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
<vbox id="passphraseHelpBox">
|
||||
<description>
|
||||
&existingRecoveryKey.description;
|
||||
<label class="text-link"
|
||||
href="http://www.palemoon.org/sync/help/recoverykey.shtml">
|
||||
&addDevice.showMeHow.label;
|
||||
</label>
|
||||
<spacer id="passphraseHelpSpacer"/>
|
||||
<label class="text-link"
|
||||
onclick="gSyncSetup.resetPassphrase(); return false;">
|
||||
&resetSyncKey.label;
|
||||
</label>
|
||||
</description>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="syncOptionsPage"
|
||||
label="&setup.optionsPage.title;"
|
||||
onpageshow="gSyncSetup.onPageShow()">
|
||||
<groupbox id="syncOptions">
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1" style="-moz-margin-end: 2px"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row align="center">
|
||||
<label value="&syncDeviceName.label;"
|
||||
accesskey="&syncDeviceName.accesskey;"
|
||||
control="syncComputerName"/>
|
||||
<textbox id="syncComputerName" flex="1"
|
||||
onchange="gSyncUtils.changeName(this)"/>
|
||||
</row>
|
||||
<row>
|
||||
<label value="&syncMy.label;" />
|
||||
<vbox>
|
||||
<checkbox label="&engine.addons.label;"
|
||||
accesskey="&engine.addons.accesskey;"
|
||||
id="engine.addons"
|
||||
checked="false"
|
||||
hidden="true"/>
|
||||
<checkbox label="&engine.bookmarks.label;"
|
||||
accesskey="&engine.bookmarks.accesskey;"
|
||||
id="engine.bookmarks"
|
||||
checked="true"/>
|
||||
<checkbox label="&engine.passwords.label;"
|
||||
accesskey="&engine.passwords.accesskey;"
|
||||
id="engine.passwords"
|
||||
checked="true"/>
|
||||
<checkbox label="&engine.prefs.label;"
|
||||
accesskey="&engine.prefs.accesskey;"
|
||||
id="engine.prefs"
|
||||
checked="true"/>
|
||||
<checkbox label="&engine.history.label;"
|
||||
accesskey="&engine.history.accesskey;"
|
||||
id="engine.history"
|
||||
checked="true"/>
|
||||
<checkbox label="&engine.tabs.label;"
|
||||
accesskey="&engine.tabs.accesskey;"
|
||||
id="engine.tabs"
|
||||
checked="true"/>
|
||||
</vbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</groupbox>
|
||||
|
||||
<groupbox id="mergeOptions">
|
||||
<radiogroup id="mergeChoiceRadio" pack="start">
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows flex="1">
|
||||
<row align="center">
|
||||
<radio id="resetClient"
|
||||
class="mergeChoiceButton"
|
||||
aria-labelledby="resetClientLabel"/>
|
||||
<label id="resetClientLabel" control="resetClient">
|
||||
<html:strong>&choice2.merge.recommended.label;</html:strong>
|
||||
&choice2a.merge.main.label;
|
||||
</label>
|
||||
</row>
|
||||
<row align="center">
|
||||
<radio id="wipeClient"
|
||||
class="mergeChoiceButton"
|
||||
aria-labelledby="wipeClientLabel"/>
|
||||
<label id="wipeClientLabel"
|
||||
control="wipeClient">
|
||||
&choice2a.client.main.label;
|
||||
</label>
|
||||
</row>
|
||||
<row align="center">
|
||||
<radio id="wipeRemote"
|
||||
class="mergeChoiceButton"
|
||||
aria-labelledby="wipeRemoteLabel"/>
|
||||
<label id="wipeRemoteLabel"
|
||||
control="wipeRemote">
|
||||
&choice2a.server.main.label;
|
||||
</label>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</radiogroup>
|
||||
</groupbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="syncOptionsConfirm"
|
||||
label="&setup.optionsConfirmPage.title;"
|
||||
onpageshow="gSyncSetup.onPageShow()">
|
||||
<deck id="chosenActionDeck">
|
||||
<vbox id="chosenActionMerge" class="confirm">
|
||||
<description class="normal">
|
||||
&confirm.merge2.label;
|
||||
</description>
|
||||
</vbox>
|
||||
<vbox id="chosenActionWipeClient" class="confirm">
|
||||
<description class="normal">
|
||||
&confirm.client3.label;
|
||||
</description>
|
||||
<separator class="thin"/>
|
||||
<vbox id="dataList">
|
||||
<label class="data indent" id="bookmarkCount"/>
|
||||
<label class="data indent" id="historyCount"/>
|
||||
<label class="data indent" id="passwordCount"/>
|
||||
<label class="data indent" id="addonCount"/>
|
||||
<label class="data indent" id="prefsWipe"
|
||||
value="&engine.prefs.label;"/>
|
||||
</vbox>
|
||||
<separator class="thin"/>
|
||||
<description class="normal">
|
||||
&confirm.client2.moreinfo.label;
|
||||
</description>
|
||||
</vbox>
|
||||
<vbox id="chosenActionWipeServer" class="confirm">
|
||||
<description class="normal">
|
||||
&confirm.server2.label;
|
||||
</description>
|
||||
<separator class="thin"/>
|
||||
<vbox id="clientList">
|
||||
</vbox>
|
||||
</vbox>
|
||||
</deck>
|
||||
</wizardpage>
|
||||
<!-- In terms of the wizard flow shown to the user, the 'syncOptionsConfirm'
|
||||
page above is not the last wizard page. To prevent the wizard binding from
|
||||
assuming that it is, we're inserting this dummy page here. This also means
|
||||
that the wizard needs to always be closed manually via wizardFinish(). -->
|
||||
<wizardpage>
|
||||
</wizardpage>
|
||||
</wizard>
|
||||
|
||||
218
application/basilisk/components/sync/utils.js
Normal file
218
application/basilisk/components/sync/utils.js
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Equivalent to 0o600 permissions; used for saved Sync Recovery Key.
|
||||
// This constant can be replaced when the equivalent values are available to
|
||||
// chrome JS; see Bug 433295 and Bug 757351.
|
||||
const PERMISSIONS_RWUSR = 0x180;
|
||||
|
||||
// Weave should always exist before before this file gets included.
|
||||
var gSyncUtils = {
|
||||
get bundle() {
|
||||
delete this.bundle;
|
||||
return this.bundle = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
|
||||
},
|
||||
|
||||
// opens in a new window if we're in a modal prefwindow world, in a new tab otherwise
|
||||
_openLink: function (url) {
|
||||
let thisDocEl = document.documentElement,
|
||||
openerDocEl = window.opener && window.opener.document.documentElement;
|
||||
if (thisDocEl.id == "accountSetup" && window.opener &&
|
||||
openerDocEl.id == "BrowserPreferences" && !openerDocEl.instantApply)
|
||||
openUILinkIn(url, "window");
|
||||
else if (thisDocEl.id == "BrowserPreferences" && !thisDocEl.instantApply)
|
||||
openUILinkIn(url, "window");
|
||||
else if (document.documentElement.id == "change-dialog")
|
||||
Services.wm.getMostRecentWindow("navigator:browser")
|
||||
.openUILinkIn(url, "tab");
|
||||
else
|
||||
openUILinkIn(url, "tab");
|
||||
},
|
||||
|
||||
changeName: function changeName(input) {
|
||||
// Make sure to update to a modified name, e.g., empty-string -> default
|
||||
Weave.Service.clientsEngine.localName = input.value;
|
||||
input.value = Weave.Service.clientsEngine.localName;
|
||||
},
|
||||
|
||||
openChange: function openChange(type, duringSetup) {
|
||||
// Just re-show the dialog if it's already open
|
||||
let openedDialog = Services.wm.getMostRecentWindow("Sync:" + type);
|
||||
if (openedDialog != null) {
|
||||
openedDialog.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Open up the change dialog
|
||||
let changeXUL = "chrome://browser/content/sync/genericChange.xul";
|
||||
let changeOpt = "centerscreen,chrome,resizable=no";
|
||||
Services.ww.activeWindow.openDialog(changeXUL, "", changeOpt,
|
||||
type, duringSetup);
|
||||
},
|
||||
|
||||
changePassword: function () {
|
||||
if (Weave.Utils.ensureMPUnlocked())
|
||||
this.openChange("ChangePassword");
|
||||
},
|
||||
|
||||
resetPassphrase: function (duringSetup) {
|
||||
if (Weave.Utils.ensureMPUnlocked())
|
||||
this.openChange("ResetPassphrase", duringSetup);
|
||||
},
|
||||
|
||||
updatePassphrase: function () {
|
||||
if (Weave.Utils.ensureMPUnlocked())
|
||||
this.openChange("UpdatePassphrase");
|
||||
},
|
||||
|
||||
resetPassword: function () {
|
||||
this._openLink(Weave.Service.pwResetURL);
|
||||
},
|
||||
|
||||
openToS: function () {
|
||||
this._openLink(Weave.Svc.Prefs.get("termsURL"));
|
||||
},
|
||||
|
||||
openPrivacyPolicy: function () {
|
||||
this._openLink(Weave.Svc.Prefs.get("privacyURL"));
|
||||
},
|
||||
|
||||
openFirstSyncProgressPage: function () {
|
||||
this._openLink("about:sync-progress");
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepare an invisible iframe with the passphrase backup document.
|
||||
* Used by both the print and saving methods.
|
||||
*
|
||||
* @param elid : ID of the form element containing the passphrase.
|
||||
* @param callback : Function called once the iframe has loaded.
|
||||
*/
|
||||
_preparePPiframe: function(elid, callback) {
|
||||
let pp = document.getElementById(elid).value;
|
||||
|
||||
// Create an invisible iframe whose contents we can print.
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("src", "chrome://browser/content/sync/key.xhtml");
|
||||
iframe.collapsed = true;
|
||||
document.documentElement.appendChild(iframe);
|
||||
iframe.contentWindow.addEventListener("load", function() {
|
||||
iframe.contentWindow.removeEventListener("load", arguments.callee, false);
|
||||
|
||||
// Insert the Sync Key into the page.
|
||||
let el = iframe.contentDocument.getElementById("synckey");
|
||||
el.firstChild.nodeValue = pp;
|
||||
|
||||
// Insert the TOS and Privacy Policy URLs into the page.
|
||||
let termsURL = Weave.Svc.Prefs.get("termsURL");
|
||||
el = iframe.contentDocument.getElementById("tosLink");
|
||||
el.setAttribute("href", termsURL);
|
||||
el.firstChild.nodeValue = termsURL;
|
||||
|
||||
let privacyURL = Weave.Svc.Prefs.get("privacyURL");
|
||||
el = iframe.contentDocument.getElementById("ppLink");
|
||||
el.setAttribute("href", privacyURL);
|
||||
el.firstChild.nodeValue = privacyURL;
|
||||
|
||||
callback(iframe);
|
||||
}, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Print passphrase backup document.
|
||||
*
|
||||
* @param elid : ID of the form element containing the passphrase.
|
||||
*/
|
||||
passphrasePrint: function(elid) {
|
||||
this._preparePPiframe(elid, function(iframe) {
|
||||
let webBrowserPrint = iframe.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebBrowserPrint);
|
||||
let printSettings = PrintUtils.getPrintSettings();
|
||||
|
||||
// Display no header/footer decoration except for the date.
|
||||
printSettings.headerStrLeft
|
||||
= printSettings.headerStrCenter
|
||||
= printSettings.headerStrRight
|
||||
= printSettings.footerStrLeft
|
||||
= printSettings.footerStrCenter = "";
|
||||
printSettings.footerStrRight = "&D";
|
||||
|
||||
try {
|
||||
webBrowserPrint.print(printSettings, null);
|
||||
} catch (ex) {
|
||||
// print()'s return codes are expressed as exceptions. Ignore.
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Save passphrase backup document to disk as HTML file.
|
||||
*
|
||||
* @param elid : ID of the form element containing the passphrase.
|
||||
*/
|
||||
passphraseSave: function(elid) {
|
||||
let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
|
||||
let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
|
||||
this._preparePPiframe(elid, function(iframe) {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
let fpCallback = function fpCallback_done(aResult) {
|
||||
if (aResult == Ci.nsIFilePicker.returnOK ||
|
||||
aResult == Ci.nsIFilePicker.returnReplace) {
|
||||
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(fp.file, -1, PERMISSIONS_RWUSR, 0);
|
||||
|
||||
let serializer = new XMLSerializer();
|
||||
let output = serializer.serializeToString(iframe.contentDocument);
|
||||
output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
|
||||
'"DTD/xhtml1-strict.dtd">');
|
||||
output = Weave.Utils.encodeUTF8(output);
|
||||
stream.write(output, output.length);
|
||||
}
|
||||
};
|
||||
|
||||
fp.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterHTML);
|
||||
fp.defaultString = defaultSaveName;
|
||||
fp.open(fpCallback);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* validatePassword
|
||||
*
|
||||
* @param el1 : the first textbox element in the form
|
||||
* @param el2 : the second textbox element, if omitted it's an update form
|
||||
*
|
||||
* returns [valid, errorString]
|
||||
*/
|
||||
validatePassword: function (el1, el2) {
|
||||
let valid = false;
|
||||
let val1 = el1.value;
|
||||
let val2 = el2 ? el2.value : "";
|
||||
let error = "";
|
||||
|
||||
if (!el2)
|
||||
valid = val1.length >= Weave.MIN_PASS_LENGTH;
|
||||
else if (val1 && val1 == Weave.Service.identity.username)
|
||||
error = "change.password.pwSameAsUsername";
|
||||
else if (val1 && val1 == Weave.Service.identity.account)
|
||||
error = "change.password.pwSameAsEmail";
|
||||
else if (val1 && val1 == Weave.Service.identity.basicPassword)
|
||||
error = "change.password.pwSameAsPassword";
|
||||
else if (val1 && val2) {
|
||||
if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
|
||||
valid = true;
|
||||
else if (val1.length < Weave.MIN_PASS_LENGTH)
|
||||
error = "change.password.tooShort";
|
||||
else if (val1 != val2)
|
||||
error = "change.password.mismatch";
|
||||
}
|
||||
let errorString = error ? Weave.Utils.getErrorString(error) : "";
|
||||
return [valid, errorString];
|
||||
}
|
||||
};
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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");
|
||||
}
|
||||
};
|
||||
|
|
@ -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',
|
||||
]
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue