[Centaury] Remove Firefox Accounts
parent
99ba192493
commit
b75f702283
|
@ -1170,53 +1170,11 @@ pref("browser.uiCustomization.debug", false);
|
|||
// CustomizableUI state of the browser's user interface
|
||||
pref("browser.uiCustomization.state", "");
|
||||
|
||||
// The remote content URL shown for FxA signup. Must use HTTPS.
|
||||
pref("identity.fxaccounts.remote.signup.uri", "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The URL where remote content that forces re-authentication for Firefox Accounts
|
||||
// should be fetched. Must use HTTPS.
|
||||
pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The remote content URL shown for signin in. Must use HTTPS.
|
||||
pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The remote content URL where FxAccountsWebChannel messages originate.
|
||||
pref("identity.fxaccounts.remote.webchannel.uri", "https://accounts.firefox.com/");
|
||||
|
||||
// The value of the context query parameter passed in some fxa requests when config
|
||||
// discovery is enabled.
|
||||
pref("identity.fxaccounts.contextParam", "fx_desktop_v3");
|
||||
|
||||
// The URL we take the user to when they opt to "manage" their Firefox Account.
|
||||
// Note that this will always need to be in the same TLD as the
|
||||
// "identity.fxaccounts.remote.signup.uri" pref.
|
||||
pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The remote URL of the FxA Profile Server
|
||||
pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
|
||||
|
||||
// The remote URL of the FxA OAuth Server
|
||||
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
|
||||
|
||||
// Whether we display profile images in the UI or not.
|
||||
pref("identity.fxaccounts.profile_image.enabled", true);
|
||||
|
||||
// Token server used by the FxA Sync identity.
|
||||
pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
|
||||
|
||||
// URLs for promo links to mobile browsers. Note that consumers are expected to
|
||||
// append a value for utm_campaign.
|
||||
pref("identity.mobilepromo.android", "https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
|
||||
pref("identity.mobilepromo.ios", "https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
|
||||
|
||||
// Migrate any existing Firefox Account data from the default profile to the
|
||||
// Developer Edition profile.
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
pref("identity.fxaccounts.migrateToDevEdition", true);
|
||||
#else
|
||||
pref("identity.fxaccounts.migrateToDevEdition", false);
|
||||
#endif
|
||||
|
||||
// On GTK, we now default to showing the menubar only when alt is pressed:
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
pref("ui.key.menuAccessKeyFocuses", true);
|
||||
|
|
|
@ -1164,53 +1164,11 @@ pref("browser.uiCustomization.debug", false);
|
|||
// CustomizableUI state of the browser's user interface
|
||||
pref("browser.uiCustomization.state", "");
|
||||
|
||||
// The remote content URL shown for FxA signup. Must use HTTPS.
|
||||
pref("identity.fxaccounts.remote.signup.uri", "https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The URL where remote content that forces re-authentication for Firefox Accounts
|
||||
// should be fetched. Must use HTTPS.
|
||||
pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The remote content URL shown for signin in. Must use HTTPS.
|
||||
pref("identity.fxaccounts.remote.signin.uri", "https://accounts.firefox.com/signin?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The remote content URL where FxAccountsWebChannel messages originate.
|
||||
pref("identity.fxaccounts.remote.webchannel.uri", "https://accounts.firefox.com/");
|
||||
|
||||
// The value of the context query parameter passed in some fxa requests when config
|
||||
// discovery is enabled.
|
||||
pref("identity.fxaccounts.contextParam", "fx_desktop_v3");
|
||||
|
||||
// The URL we take the user to when they opt to "manage" their Firefox Account.
|
||||
// Note that this will always need to be in the same TLD as the
|
||||
// "identity.fxaccounts.remote.signup.uri" pref.
|
||||
pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings?service=sync&context=fx_desktop_v3");
|
||||
|
||||
// The remote URL of the FxA Profile Server
|
||||
pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
|
||||
|
||||
// The remote URL of the FxA OAuth Server
|
||||
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
|
||||
|
||||
// Whether we display profile images in the UI or not.
|
||||
pref("identity.fxaccounts.profile_image.enabled", true);
|
||||
|
||||
// Token server used by the FxA Sync identity.
|
||||
pref("identity.sync.tokenserver.uri", "https://token.services.mozilla.com/1.0/sync/1.5");
|
||||
|
||||
// URLs for promo links to mobile browsers. Note that consumers are expected to
|
||||
// append a value for utm_campaign.
|
||||
pref("identity.mobilepromo.android", "https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
|
||||
pref("identity.mobilepromo.ios", "https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=");
|
||||
|
||||
// Migrate any existing Firefox Account data from the default profile to the
|
||||
// Developer Edition profile.
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
pref("identity.fxaccounts.migrateToDevEdition", true);
|
||||
#else
|
||||
pref("identity.fxaccounts.migrateToDevEdition", false);
|
||||
#endif
|
||||
|
||||
// On GTK, we now default to showing the menubar only when alt is pressed:
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
pref("ui.key.menuAccessKeyFocuses", true);
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#remote {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#networkError, #manage, #intro, #stage, #configError {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#oldsync {
|
||||
background: none;
|
||||
border: 0;
|
||||
color: #0095dd;
|
||||
}
|
||||
|
||||
#oldsync:focus {
|
||||
outline: 1px dotted #0095dd;
|
||||
}
|
|
@ -1,543 +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";
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
var fxAccountsCommon = {};
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
|
||||
|
||||
// for master-password utilities
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
|
||||
const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync-setup.ui.showCustomizationDialog";
|
||||
|
||||
const ACTION_URL_PARAM = "action";
|
||||
|
||||
const OBSERVER_TOPICS = [
|
||||
fxAccountsCommon.ONVERIFIED_NOTIFICATION,
|
||||
fxAccountsCommon.ONLOGOUT_NOTIFICATION,
|
||||
];
|
||||
|
||||
function log(msg) {
|
||||
// dump("FXA: " + msg + "\n");
|
||||
}
|
||||
|
||||
function error(msg) {
|
||||
console.log("Firefox Account Error: " + msg + "\n");
|
||||
}
|
||||
|
||||
function getPreviousAccountNameHash() {
|
||||
try {
|
||||
return Services.prefs.getComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString).data;
|
||||
} catch (_) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
function setPreviousAccountNameHash(acctName) {
|
||||
let string = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
string.data = sha256(acctName);
|
||||
Services.prefs.setComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString, string);
|
||||
}
|
||||
|
||||
function needRelinkWarning(acctName) {
|
||||
let prevAcctHash = getPreviousAccountNameHash();
|
||||
return prevAcctHash && prevAcctHash != sha256(acctName);
|
||||
}
|
||||
|
||||
// Given a string, returns the SHA265 hash in base64
|
||||
function sha256(str) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
// Data is an array of bytes.
|
||||
let data = converter.convertToByteArray(str, {});
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(hasher.SHA256);
|
||||
hasher.update(data, data.length);
|
||||
|
||||
return hasher.finish(true);
|
||||
}
|
||||
|
||||
function promptForRelink(acctName) {
|
||||
let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
|
||||
let continueLabel = sb.GetStringFromName("continue.label");
|
||||
let title = sb.GetStringFromName("relinkVerify.title");
|
||||
let description = sb.formatStringFromName("relinkVerify.description",
|
||||
[acctName], 1);
|
||||
let body = sb.GetStringFromName("relinkVerify.heading") +
|
||||
"\n\n" + description;
|
||||
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 pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
|
||||
continueLabel, null, null, null,
|
||||
{});
|
||||
return pressed == 0; // 0 is the "continue" button
|
||||
}
|
||||
|
||||
// If the last fxa account used for sync isn't this account, we display
|
||||
// a modal dialog checking they really really want to do this...
|
||||
// (This is sync-specific, so ideally would be in sync's identity module,
|
||||
// but it's a little more seamless to do here, and sync is currently the
|
||||
// only fxa consumer, so...
|
||||
function shouldAllowRelink(acctName) {
|
||||
return !needRelinkWarning(acctName) || promptForRelink(acctName);
|
||||
}
|
||||
|
||||
function updateDisplayedEmail(user) {
|
||||
let emailDiv = document.getElementById("email");
|
||||
if (emailDiv && user) {
|
||||
emailDiv.textContent = user.email;
|
||||
}
|
||||
}
|
||||
|
||||
var wrapper = {
|
||||
iframe: null,
|
||||
|
||||
init: function (url, urlParams) {
|
||||
// If a master-password is enabled, we want to encourage the user to
|
||||
// unlock it. Things still work if not, but the user will probably need
|
||||
// to re-auth next startup (in which case we will get here again and
|
||||
// re-prompt)
|
||||
Utils.ensureMPUnlocked();
|
||||
|
||||
let iframe = document.getElementById("remote");
|
||||
this.iframe = iframe;
|
||||
this.iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
|
||||
let docShell = this.iframe.frameLoader.docShell;
|
||||
docShell.QueryInterface(Ci.nsIWebProgress);
|
||||
docShell.addProgressListener(this.iframeListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
|
||||
iframe.addEventListener("load", this);
|
||||
|
||||
// Ideally we'd just merge urlParams with new URL(url).searchParams, but our
|
||||
// URLSearchParams implementation doesn't support iteration (bug 1085284).
|
||||
let urlParamStr = urlParams.toString();
|
||||
if (urlParamStr) {
|
||||
url += (url.includes("?") ? "&" : "?") + urlParamStr;
|
||||
}
|
||||
this.url = url;
|
||||
// Set the iframe's location with loadURI/LOAD_FLAGS_REPLACE_HISTORY to
|
||||
// avoid having a new history entry being added. REPLACE_HISTORY is used
|
||||
// to replace the current entry, which is `about:blank`.
|
||||
let webNav = iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY, null, null, null);
|
||||
},
|
||||
|
||||
retry: function () {
|
||||
let webNav = this.iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
webNav.loadURI(this.url, Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY, null, null, null);
|
||||
},
|
||||
|
||||
iframeListener: {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsISupports]),
|
||||
|
||||
onStateChange: function(aWebProgress, aRequest, aState, aStatus) {
|
||||
let failure = false;
|
||||
|
||||
// Captive portals sometimes redirect users
|
||||
if ((aState & Ci.nsIWebProgressListener.STATE_REDIRECTING)) {
|
||||
failure = true;
|
||||
} else if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
|
||||
if (aRequest instanceof Ci.nsIHttpChannel) {
|
||||
try {
|
||||
failure = aRequest.responseStatus != 200;
|
||||
} catch (e) {
|
||||
failure = aStatus != Components.results.NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calling cancel() will raise some OnStateChange notifications by itself,
|
||||
// so avoid doing that more than once
|
||||
if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
|
||||
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
setErrorPage("networkError");
|
||||
}
|
||||
},
|
||||
|
||||
onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
|
||||
if (aRequest && aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
|
||||
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
|
||||
setErrorPage("networkError");
|
||||
}
|
||||
},
|
||||
|
||||
onProgressChange: function() {},
|
||||
onStatusChange: function() {},
|
||||
onSecurityChange: function() {},
|
||||
},
|
||||
|
||||
handleEvent: function (evt) {
|
||||
switch (evt.type) {
|
||||
case "load":
|
||||
this.iframe.contentWindow.addEventListener("FirefoxAccountsCommand", this);
|
||||
this.iframe.removeEventListener("load", this);
|
||||
break;
|
||||
case "FirefoxAccountsCommand":
|
||||
this.handleRemoteCommand(evt);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* onLogin handler receives user credentials from the jelly after a
|
||||
* sucessful login and stores it in the fxaccounts service
|
||||
*
|
||||
* @param accountData the user's account data and credentials
|
||||
*/
|
||||
onLogin: function (accountData) {
|
||||
log("Received: 'login'. Data:" + JSON.stringify(accountData));
|
||||
|
||||
if (accountData.customizeSync) {
|
||||
Services.prefs.setBoolPref(PREF_SYNC_SHOW_CUSTOMIZATION, true);
|
||||
}
|
||||
delete accountData.customizeSync;
|
||||
// sessionTokenContext is erroneously sent by the content server.
|
||||
// https://github.com/mozilla/fxa-content-server/issues/2766
|
||||
// To avoid having the FxA storage manager not knowing what to do with
|
||||
// it we delete it here.
|
||||
delete accountData.sessionTokenContext;
|
||||
|
||||
// We need to confirm a relink - see shouldAllowRelink for more
|
||||
let newAccountEmail = accountData.email;
|
||||
// The hosted code may have already checked for the relink situation
|
||||
// by sending the can_link_account command. If it did, then
|
||||
// it will indicate we don't need to ask twice.
|
||||
if (!accountData.verifiedCanLinkAccount && !shouldAllowRelink(newAccountEmail)) {
|
||||
// we need to tell the page we successfully received the message, but
|
||||
// then bail without telling fxAccounts
|
||||
this.injectData("message", { status: "login" });
|
||||
// after a successful login we return to preferences
|
||||
openPrefs();
|
||||
return;
|
||||
}
|
||||
delete accountData.verifiedCanLinkAccount;
|
||||
|
||||
// Remember who it was so we can log out next time.
|
||||
setPreviousAccountNameHash(newAccountEmail);
|
||||
|
||||
// A sync-specific hack - we want to ensure sync has been initialized
|
||||
// before we set the signed-in user.
|
||||
let xps = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
xps.whenLoaded().then(() => {
|
||||
updateDisplayedEmail(accountData);
|
||||
return fxAccounts.setSignedInUser(accountData);
|
||||
}).then(() => {
|
||||
// If the user data is verified, we want it to immediately look like
|
||||
// they are signed in without waiting for messages to bounce around.
|
||||
if (accountData.verified) {
|
||||
openPrefs();
|
||||
}
|
||||
this.injectData("message", { status: "login" });
|
||||
// until we sort out a better UX, just leave the jelly page in place.
|
||||
// If the account email is not yet verified, it will tell the user to
|
||||
// go check their email, but then it will *not* change state after
|
||||
// the verification completes (the browser will begin syncing, but
|
||||
// won't notify the user). If the email has already been verified,
|
||||
// the jelly will say "Welcome! You are successfully signed in as
|
||||
// EMAIL", but it won't then say "syncing started".
|
||||
}, (err) => this.injectData("message", { status: "error", error: err })
|
||||
);
|
||||
},
|
||||
|
||||
onCanLinkAccount: function(accountData) {
|
||||
// We need to confirm a relink - see shouldAllowRelink for more
|
||||
let ok = shouldAllowRelink(accountData.email);
|
||||
this.injectData("message", { status: "can_link_account", data: { ok: ok } });
|
||||
},
|
||||
|
||||
/**
|
||||
* onSignOut handler erases the current user's session from the fxaccounts service
|
||||
*/
|
||||
onSignOut: function () {
|
||||
log("Received: 'sign_out'.");
|
||||
|
||||
fxAccounts.signOut().then(
|
||||
() => this.injectData("message", { status: "sign_out" }),
|
||||
(err) => this.injectData("message", { status: "error", error: err })
|
||||
);
|
||||
},
|
||||
|
||||
handleRemoteCommand: function (evt) {
|
||||
log('command: ' + evt.detail.command);
|
||||
let data = evt.detail.data;
|
||||
|
||||
switch (evt.detail.command) {
|
||||
case "login":
|
||||
this.onLogin(data);
|
||||
break;
|
||||
case "can_link_account":
|
||||
this.onCanLinkAccount(data);
|
||||
break;
|
||||
case "sign_out":
|
||||
this.onSignOut(data);
|
||||
break;
|
||||
default:
|
||||
log("Unexpected remote command received: " + evt.detail.command + ". Ignoring command.");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
injectData: function (type, content) {
|
||||
return fxAccounts.promiseAccountsSignUpURI().then(authUrl => {
|
||||
let data = {
|
||||
type: type,
|
||||
content: content
|
||||
};
|
||||
this.iframe.contentWindow.postMessage(data, authUrl);
|
||||
})
|
||||
.catch(e => {
|
||||
console.log("Failed to inject data", e);
|
||||
setErrorPage("configError");
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// Button onclick handlers
|
||||
function handleOldSync() {
|
||||
let chromeWin = window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
|
||||
chromeWin.switchToTabHavingURI(url, true);
|
||||
}
|
||||
|
||||
function getStarted() {
|
||||
show("remote");
|
||||
}
|
||||
|
||||
function retry() {
|
||||
show("remote");
|
||||
wrapper.retry();
|
||||
}
|
||||
|
||||
function openPrefs() {
|
||||
// Bug 1199303 calls for this tab to always be replaced with Preferences
|
||||
// rather than it opening in a different tab.
|
||||
window.location = "about:preferences#sync";
|
||||
}
|
||||
|
||||
function init() {
|
||||
fxAccounts.getSignedInUser().then(user => {
|
||||
// tests in particular might cause the window to start closing before
|
||||
// getSignedInUser has returned.
|
||||
if (window.closed) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
updateDisplayedEmail(user);
|
||||
|
||||
// Ideally we'd use new URL(document.URL).searchParams, but for about: URIs,
|
||||
// searchParams is empty.
|
||||
let urlParams = new URLSearchParams(document.URL.split("?")[1] || "");
|
||||
let action = urlParams.get(ACTION_URL_PARAM);
|
||||
urlParams.delete(ACTION_URL_PARAM);
|
||||
|
||||
switch (action) {
|
||||
case "signin":
|
||||
if (user) {
|
||||
// asking to sign-in when already signed in just shows manage.
|
||||
show("stage", "manage");
|
||||
} else {
|
||||
return fxAccounts.promiseAccountsSignInURI().then(url => {
|
||||
show("remote");
|
||||
wrapper.init(url, urlParams);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "signup":
|
||||
if (user) {
|
||||
// asking to sign-up when already signed in just shows manage.
|
||||
show("stage", "manage");
|
||||
} else {
|
||||
return fxAccounts.promiseAccountsSignUpURI().then(url => {
|
||||
show("remote");
|
||||
wrapper.init(url, urlParams);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "reauth":
|
||||
// ideally we would only show this when we know the user is in a
|
||||
// "must reauthenticate" state - but we don't.
|
||||
// As the email address will be included in the URL returned from
|
||||
// promiseAccountsForceSigninURI, just always show it.
|
||||
return fxAccounts.promiseAccountsForceSigninURI().then(url => {
|
||||
show("remote");
|
||||
wrapper.init(url, urlParams);
|
||||
});
|
||||
default:
|
||||
// No action specified.
|
||||
if (user) {
|
||||
show("stage", "manage");
|
||||
} else {
|
||||
// Attempt a migration if enabled or show the introductory page
|
||||
// otherwise.
|
||||
return migrateToDevEdition(urlParams).then(migrated => {
|
||||
if (!migrated) {
|
||||
show("stage", "intro");
|
||||
// load the remote frame in the background
|
||||
return fxAccounts.promiseAccountsSignUpURI().then(uri =>
|
||||
wrapper.init(uri, urlParams));
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Promise.resolve();
|
||||
}).catch(err => {
|
||||
console.log("Configuration or sign in error", err);
|
||||
setErrorPage("configError");
|
||||
});
|
||||
}
|
||||
|
||||
function setErrorPage(errorType) {
|
||||
show("stage", errorType);
|
||||
}
|
||||
|
||||
// Causes the "top-level" element with |id| to be shown - all other top-level
|
||||
// elements are hidden. Optionally, ensures that only 1 "second-level" element
|
||||
// inside the top-level one is shown.
|
||||
function show(id, childId) {
|
||||
// top-level items are either <div> or <iframe>
|
||||
let allTop = document.querySelectorAll("body > div, iframe");
|
||||
for (let elt of allTop) {
|
||||
if (elt.getAttribute("id") == id) {
|
||||
elt.style.display = 'block';
|
||||
} else {
|
||||
elt.style.display = 'none';
|
||||
}
|
||||
}
|
||||
if (childId) {
|
||||
// child items are all <div>
|
||||
let allSecond = document.querySelectorAll("#" + id + " > div");
|
||||
for (let elt of allSecond) {
|
||||
if (elt.getAttribute("id") == childId) {
|
||||
elt.style.display = 'block';
|
||||
} else {
|
||||
elt.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate sync data from the default profile to the dev-edition profile.
|
||||
// Returns a promise of a true value if migration succeeded, or false if it
|
||||
// failed.
|
||||
function migrateToDevEdition(urlParams) {
|
||||
let defaultProfilePath;
|
||||
try {
|
||||
defaultProfilePath = window.getDefaultProfilePath();
|
||||
} catch (e) {} // no default profile.
|
||||
let migrateSyncCreds = false;
|
||||
if (defaultProfilePath) {
|
||||
try {
|
||||
migrateSyncCreds = Services.prefs.getBoolPref("identity.fxaccounts.migrateToDevEdition");
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (!migrateSyncCreds) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
let fxAccountsStorage = OS.Path.join(defaultProfilePath, fxAccountsCommon.DEFAULT_STORAGE_FILENAME);
|
||||
return OS.File.read(fxAccountsStorage, { encoding: "utf-8" }).then(text => {
|
||||
let accountData = JSON.parse(text).accountData;
|
||||
updateDisplayedEmail(accountData);
|
||||
return fxAccounts.setSignedInUser(accountData);
|
||||
}).then(() => {
|
||||
return fxAccounts.promiseAccountsForceSigninURI().then(url => {
|
||||
show("remote");
|
||||
wrapper.init(url, urlParams);
|
||||
});
|
||||
}).then(null, error => {
|
||||
log("Failed to migrate FX Account: " + error);
|
||||
show("stage", "intro");
|
||||
// load the remote frame in the background
|
||||
fxAccounts.promiseAccountsSignUpURI().then(uri => {
|
||||
wrapper.init(uri, urlParams)
|
||||
}).catch(e => {
|
||||
console.log("Failed to load signup page", e);
|
||||
setErrorPage("configError");
|
||||
});
|
||||
}).then(() => {
|
||||
// Reset the pref after migration.
|
||||
Services.prefs.setBoolPref("identity.fxaccounts.migrateToDevEdition", false);
|
||||
return true;
|
||||
}).then(null, err => {
|
||||
Cu.reportError("Failed to reset the migrateToDevEdition pref: " + err);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function that returns the path of the default profile on disk. Will be
|
||||
// overridden in tests.
|
||||
function getDefaultProfilePath() {
|
||||
let defaultProfile = Cc["@mozilla.org/toolkit/profile-service;1"]
|
||||
.getService(Ci.nsIToolkitProfileService)
|
||||
.defaultProfile;
|
||||
return defaultProfile.rootDir.path;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function onload() {
|
||||
document.removeEventListener("DOMContentLoaded", onload, true);
|
||||
init();
|
||||
var buttonGetStarted = document.getElementById('buttonGetStarted');
|
||||
buttonGetStarted.addEventListener('click', getStarted);
|
||||
|
||||
var buttonRetry = document.getElementById('buttonRetry');
|
||||
buttonRetry.addEventListener('click', retry);
|
||||
|
||||
var oldsync = document.getElementById('oldsync');
|
||||
oldsync.addEventListener('click', handleOldSync);
|
||||
|
||||
var buttonOpenPrefs = document.getElementById('buttonOpenPrefs')
|
||||
buttonOpenPrefs.addEventListener('click', openPrefs);
|
||||
}, true);
|
||||
|
||||
function initObservers() {
|
||||
function observe(subject, topic, data) {
|
||||
log("about:accounts observed " + topic);
|
||||
if (topic == fxAccountsCommon.ONLOGOUT_NOTIFICATION) {
|
||||
// All about:account windows get changed to action=signin on logout.
|
||||
window.location = "about:accounts?action=signin";
|
||||
return;
|
||||
}
|
||||
|
||||
// must be onverified - we want to open preferences.
|
||||
openPrefs();
|
||||
}
|
||||
|
||||
for (let topic of OBSERVER_TOPICS) {
|
||||
Services.obs.addObserver(observe, topic, false);
|
||||
}
|
||||
window.addEventListener("unload", function(event) {
|
||||
log("about:accounts unloading")
|
||||
for (let topic of OBSERVER_TOPICS) {
|
||||
Services.obs.removeObserver(observe, topic);
|
||||
}
|
||||
});
|
||||
}
|
||||
initObservers();
|
|
@ -1,112 +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 [
|
||||
<!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
|
||||
%htmlDTD;
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
<!ENTITY % aboutAccountsDTD SYSTEM "chrome://browser/locale/aboutAccounts.dtd">
|
||||
%aboutAccountsDTD;
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
%syncBrandDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" dir="&locale.dir;">
|
||||
<head>
|
||||
<title>&syncBrand.fullName.label;</title>
|
||||
<meta name="viewport" content="width=device-width"/>
|
||||
|
||||
|
||||
<link rel="icon" type="image/png" id="favicon"
|
||||
href="chrome://branding/content/icon32.png"/>
|
||||
<link rel="stylesheet"
|
||||
href="chrome://browser/content/aboutaccounts/normalize.css"
|
||||
type="text/css" />
|
||||
<link rel="stylesheet"
|
||||
href="chrome://browser/content/aboutaccounts/main.css"
|
||||
type="text/css" />
|
||||
<link rel="stylesheet"
|
||||
href="chrome://browser/content/aboutaccounts/aboutaccounts.css"
|
||||
type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="stage">
|
||||
|
||||
<div id="manage">
|
||||
<header>
|
||||
<h1>&aboutAccounts.connected;</h1>
|
||||
<div id="email"></div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="graphic graphic-sync-intro"> </div>
|
||||
|
||||
<div class="button-row">
|
||||
<button id="buttonOpenPrefs" class="button" href="#" tabindex="0">&aboutAccountsConfig.syncPreferences.label;</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="intro">
|
||||
<header>
|
||||
<h1>&aboutAccounts.welcome;</h1>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="graphic graphic-sync-intro"> </div>
|
||||
|
||||
<div class="description">&aboutAccountsConfig.description;</div>
|
||||
|
||||
<div class="button-row">
|
||||
<button id="buttonGetStarted" class="button" tabindex="1">&aboutAccountsConfig.startButton.label;</button>
|
||||
</div>
|
||||
|
||||
<div class="links">
|
||||
<button id="oldsync" tabindex="2">&aboutAccountsConfig.useOldSync.label;</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="networkError">
|
||||
<header>
|
||||
<h1>&aboutAccounts.noConnection.title;</h1>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="graphic graphic-sync-intro"> </div>
|
||||
|
||||
<div class="description">&aboutAccounts.noConnection.description;</div>
|
||||
|
||||
<div class="button-row">
|
||||
<button id="buttonRetry" class="button" tabindex="3">&aboutAccounts.noConnection.retry;</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="configError">
|
||||
<header>
|
||||
<h1>&aboutAccounts.badConfig.title;</h1>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="graphic graphic-sync-intro"> </div>
|
||||
|
||||
<div class="description">&aboutAccounts.badConfig.description;</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<iframe mozframetype="content" id="remote" />
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://browser/content/utilityOverlay.js"/>
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://browser/content/aboutaccounts/aboutaccounts.js" />
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
|
@ -1,166 +0,0 @@
|
|||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: #F2F2F2;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #424f59;
|
||||
font: message-box;
|
||||
font-size: 14px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0095dd;
|
||||
cursor: pointer; /* Use the correct cursor for anchors without an href */
|
||||
}
|
||||
|
||||
a:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline: 1px dotted #0095dd;
|
||||
}
|
||||
|
||||
|
||||
a.no-underline {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#stage {
|
||||
background:#fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.25);
|
||||
margin: 0 auto;
|
||||
min-height: 300px;
|
||||
padding: 60px 40px 40px 40px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
top: 80px;
|
||||
width: 420px;
|
||||
}
|
||||
|
||||
header h1
|
||||
{
|
||||
font-size: 24px;
|
||||
font-weight: 200;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
#intro header h1 {
|
||||
margin: 0 0 32px 0;
|
||||
}
|
||||
|
||||
#manage header h1 {
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
#manage header #email {
|
||||
margin-bottom: 23px;
|
||||
color: rgb(138, 155, 168);
|
||||
font-size: 19px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
margin-top: 45px;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
.button-row button,
|
||||
.button-row a.button {
|
||||
background: #0095dd;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
padding: 15px 0;
|
||||
transition-duration: 150ms;
|
||||
transition-property: background-color;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-row a.button {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-row a.button:active,
|
||||
.button-row a.button:hover,
|
||||
.button-row a.button:focus,
|
||||
.button-row button:active,
|
||||
.button-row button:hover,
|
||||
.button-row button:focus {
|
||||
background: #08c;
|
||||
}
|
||||
|
||||
|
||||
.graphic-sync-intro {
|
||||
background-image: url(images/graphic_sync_intro.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 150px 195px;
|
||||
height: 195px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
text-indent: 100%;
|
||||
white-space: nowrap;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.description,
|
||||
.button-row {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.links {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
html {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#stage {
|
||||
box-shadow: none;
|
||||
margin: 30px auto 0 auto;
|
||||
min-height: none;
|
||||
min-width: 320px;
|
||||
padding: 0 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.button-row button,
|
||||
.button-row a.button {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Retina */
|
||||
@media
|
||||
only screen and (min-device-pixel-ratio: 2),
|
||||
only screen and ( min-resolution: 192dpi),
|
||||
only screen and ( min-resolution: 2dppx) {
|
||||
.graphic-sync-intro {
|
||||
background-image: url(images/graphic_sync_intro@2x.png);
|
||||
}
|
||||
}
|
|
@ -1,402 +0,0 @@
|
|||
/*! normalize.css v2.1.3 | MIT License | git.io/normalize */
|
||||
|
||||
/* ==========================================================================
|
||||
HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Correct `block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct `inline-block` display not defined in IE 8/9.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `[hidden]` styling not present in IE 8/9.
|
||||
* Hide the `template` element in IE, Safari, and Firefox < 22.
|
||||
*/
|
||||
|
||||
[hidden],
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Base
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Set default font family to sans-serif.
|
||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-family: sans-serif; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default margin.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background color from active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `outline` inconsistency between Chrome and other browsers.
|
||||
*/
|
||||
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Typography
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address variable `h1` font-size and margin within `section` and `article`
|
||||
* contexts in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address differences between Firefox and other browsers.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct font family set oddly in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability of pre-formatted text in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set consistent quote types.
|
||||
*/
|
||||
|
||||
q {
|
||||
quotes: "\201C" "\201D" "\2018" "\2019";
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove border when inside `a` element in IE 8/9.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct overflow displayed oddly in IE 9.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Figures
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address margin not present in IE 8/9 and Safari 5.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct `color` not being inherited in IE 8/9.
|
||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct font family not being inherited in all browsers.
|
||||
* 2. Correct font size not being inherited in all browsers.
|
||||
* 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 2 */
|
||||
margin: 0; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||
* All other form control elements do not inherit `text-transform` values.
|
||||
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
|
||||
* Correct `select` style inheritance in Firefox 4+ and Opera.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Correct inability to style clickable `input` types in iOS.
|
||||
* 3. Improve usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
||||
* 2. Remove excess padding in IE 8/9/10.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
box-sizing: content-box; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and search cancel button in Safari 5 and Chrome
|
||||
* on OS X.
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and border in Firefox 4+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove default vertical scrollbar in IE 8/9.
|
||||
* 2. Improve readability and alignment in all browsers.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto; /* 1 */
|
||||
vertical-align: top; /* 2 */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tables
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
|
@ -1,314 +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/. */
|
||||
|
||||
var gFxAccounts = {
|
||||
|
||||
_initialized: false,
|
||||
_inCustomizationMode: false,
|
||||
_cachedProfile: null,
|
||||
|
||||
get weave() {
|
||||
delete this.weave;
|
||||
return this.weave = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
},
|
||||
|
||||
get topics() {
|
||||
// Do all this dance to lazy-load FxAccountsCommon.
|
||||
delete this.topics;
|
||||
return this.topics = [
|
||||
"weave:service:ready",
|
||||
"weave:service:login:change",
|
||||
"weave:service:setup-complete",
|
||||
"weave:service:sync:error",
|
||||
"weave:ui:login:error",
|
||||
this.FxAccountsCommon.ONLOGIN_NOTIFICATION,
|
||||
this.FxAccountsCommon.ONLOGOUT_NOTIFICATION,
|
||||
this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION,
|
||||
];
|
||||
},
|
||||
|
||||
get panelUIFooter() {
|
||||
delete this.panelUIFooter;
|
||||
return this.panelUIFooter = document.getElementById("PanelUI-footer-fxa");
|
||||
},
|
||||
|
||||
get panelUIStatus() {
|
||||
delete this.panelUIStatus;
|
||||
return this.panelUIStatus = document.getElementById("PanelUI-fxa-status");
|
||||
},
|
||||
|
||||
get panelUIAvatar() {
|
||||
delete this.panelUIAvatar;
|
||||
return this.panelUIAvatar = document.getElementById("PanelUI-fxa-avatar");
|
||||
},
|
||||
|
||||
get panelUILabel() {
|
||||
delete this.panelUILabel;
|
||||
return this.panelUILabel = document.getElementById("PanelUI-fxa-label");
|
||||
},
|
||||
|
||||
get panelUIIcon() {
|
||||
delete this.panelUIIcon;
|
||||
return this.panelUIIcon = document.getElementById("PanelUI-fxa-icon");
|
||||
},
|
||||
|
||||
get strings() {
|
||||
delete this.strings;
|
||||
return this.strings = Services.strings.createBundle(
|
||||
"chrome://browser/locale/accounts.properties"
|
||||
);
|
||||
},
|
||||
|
||||
get loginFailed() {
|
||||
// Referencing Weave.Service will implicitly initialize sync, and we don't
|
||||
// want to force that - so first check if it is ready.
|
||||
let service = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
if (!service.ready) {
|
||||
return false;
|
||||
}
|
||||
// LOGIN_FAILED_LOGIN_REJECTED explicitly means "you must log back in".
|
||||
// All other login failures are assumed to be transient and should go
|
||||
// away by themselves, so aren't reflected here.
|
||||
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
// Bail out if we're already initialized and for pop-up windows.
|
||||
if (this._initialized || !window.toolbar.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let topic of this.topics) {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
}
|
||||
|
||||
gNavToolbox.addEventListener("customizationstarting", this);
|
||||
gNavToolbox.addEventListener("customizationending", this);
|
||||
|
||||
EnsureFxAccountsWebChannel();
|
||||
this._initialized = true;
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
if (!this._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let topic of this.topics) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
}
|
||||
|
||||
this._initialized = false;
|
||||
},
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
switch (topic) {
|
||||
case this.FxAccountsCommon.ON_PROFILE_CHANGE_NOTIFICATION:
|
||||
this._cachedProfile = null;
|
||||
// Fallthrough intended
|
||||
default:
|
||||
this.updateUI();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function (event) {
|
||||
this._inCustomizationMode = event.type == "customizationstarting";
|
||||
this.updateAppMenuItem();
|
||||
},
|
||||
|
||||
updateUI: function () {
|
||||
// It's possible someone signed in to FxA after seeing our notification
|
||||
// about "Legacy Sync migration" (which now is actually "Legacy Sync
|
||||
// auto-disconnect") so kill that notification if it still exists.
|
||||
let nb = window.document.getElementById("global-notificationbox");
|
||||
let n = nb.getNotificationWithValue(this.SYNC_MIGRATION_NOTIFICATION_TITLE);
|
||||
if (n) {
|
||||
nb.removeNotification(n, true);
|
||||
}
|
||||
|
||||
this.updateAppMenuItem();
|
||||
},
|
||||
|
||||
// Note that updateAppMenuItem() returns a Promise that's only used by tests.
|
||||
updateAppMenuItem: function () {
|
||||
let profileInfoEnabled = false;
|
||||
try {
|
||||
profileInfoEnabled = Services.prefs.getBoolPref("identity.fxaccounts.profile_image.enabled");
|
||||
} catch (e) { }
|
||||
|
||||
this.panelUIFooter.hidden = false;
|
||||
|
||||
// Make sure the button is disabled in customization mode.
|
||||
if (this._inCustomizationMode) {
|
||||
this.panelUIStatus.setAttribute("disabled", "true");
|
||||
this.panelUILabel.setAttribute("disabled", "true");
|
||||
this.panelUIAvatar.setAttribute("disabled", "true");
|
||||
this.panelUIIcon.setAttribute("disabled", "true");
|
||||
} else {
|
||||
this.panelUIStatus.removeAttribute("disabled");
|
||||
this.panelUILabel.removeAttribute("disabled");
|
||||
this.panelUIAvatar.removeAttribute("disabled");
|
||||
this.panelUIIcon.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
let defaultLabel = this.panelUIStatus.getAttribute("defaultlabel");
|
||||
let errorLabel = this.panelUIStatus.getAttribute("errorlabel");
|
||||
let unverifiedLabel = this.panelUIStatus.getAttribute("unverifiedlabel");
|
||||
let settingslabel = this.panelUIStatus.getAttribute("settingslabel");
|
||||
// The localization string is for the signed in text, but it's the default text as well
|
||||
let defaultTooltiptext = this.panelUIStatus.getAttribute("signedinTooltiptext");
|
||||
|
||||
let updateWithUserData = (userData) => {
|
||||
// Window might have been closed while fetching data.
|
||||
if (window.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the button to its original state.
|
||||
this.panelUILabel.setAttribute("label", defaultLabel);
|
||||
this.panelUIStatus.setAttribute("tooltiptext", defaultTooltiptext);
|
||||
this.panelUIFooter.removeAttribute("fxastatus");
|
||||
this.panelUIFooter.removeAttribute("fxaprofileimage");
|
||||
this.panelUIAvatar.style.removeProperty("list-style-image");
|
||||
|
||||
if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED) {
|
||||
// Leave the default state
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.loginFailed) {
|
||||
this.panelUIFooter.setAttribute("fxastatus", "error");
|
||||
this.panelUILabel.setAttribute("label", errorLabel);
|
||||
} else {
|
||||
this.panelUIFooter.setAttribute("fxastatus", "signedin");
|
||||
this.panelUILabel.setAttribute("label", settingslabel);
|
||||
this.panelUIStatus.setAttribute("tooltiptext", "");
|
||||
}
|
||||
}
|
||||
|
||||
let updateWithProfile = (profile) => {
|
||||
if (profileInfoEnabled) {
|
||||
if (profile.displayName) {
|
||||
this.panelUILabel.setAttribute("label", profile.displayName);
|
||||
}
|
||||
if (profile.avatar) {
|
||||
this.panelUIFooter.setAttribute("fxaprofileimage", "set");
|
||||
let bgImage = "url(\"" + profile.avatar + "\")";
|
||||
this.panelUIAvatar.style.listStyleImage = bgImage;
|
||||
|
||||
let img = new Image();
|
||||
img.onerror = () => {
|
||||
// Clear the image if it has trouble loading. Since this callback is asynchronous
|
||||
// we check to make sure the image is still the same before we clear it.
|
||||
if (this.panelUIAvatar.style.listStyleImage === bgImage) {
|
||||
this.panelUIFooter.removeAttribute("fxaprofileimage");
|
||||
this.panelUIAvatar.style.removeProperty("list-style-image");
|
||||
}
|
||||
};
|
||||
img.src = profile.avatar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fxAccounts.getSignedInUser().then(userData => {
|
||||
// userData may be null here when the user is not signed-in, but that's expected
|
||||
updateWithUserData(userData);
|
||||
// unverified users cause us to spew log errors fetching an OAuth token
|
||||
// to fetch the profile, so don't even try in that case.
|
||||
if (!userData || !userData.verified || !profileInfoEnabled) {
|
||||
return null; // don't even try to grab the profile.
|
||||
}
|
||||
if (this._cachedProfile) {
|
||||
return this._cachedProfile;
|
||||
}
|
||||
return fxAccounts.getSignedInUserProfile().catch(err => {
|
||||
// Not fetching the profile is sad but the FxA logs will already have noise.
|
||||
return null;
|
||||
});
|
||||
}).then(profile => {
|
||||
if (!profile) {
|
||||
return;
|
||||
}
|
||||
updateWithProfile(profile);
|
||||
this._cachedProfile = profile; // Try to avoid fetching the profile on every UI update
|
||||
}).catch(error => {
|
||||
// This is most likely in tests, were we quickly log users in and out.
|
||||
// The most likely scenario is a user logged out, so reflect that.
|
||||
// Bug 995134 calls for better errors so we could retry if we were
|
||||
// sure this was the failure reason.
|
||||
this.FxAccountsCommon.log.error("Error updating FxA account info", error);
|
||||
updateWithUserData(null);
|
||||
});
|
||||
},
|
||||
|
||||
onMenuPanelCommand: function () {
|
||||
|
||||
switch (this.panelUIFooter.getAttribute("fxastatus")) {
|
||||
case "signedin":
|
||||
this.openPreferences();
|
||||
break;
|
||||
case "error":
|
||||
if (this.panelUIFooter.getAttribute("unverified")) {
|
||||
this.openPreferences();
|
||||
} else {
|
||||
this.openSignInAgainPage("menupanel");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.openPreferences();
|
||||
break;
|
||||
}
|
||||
|
||||
PanelUI.hide();
|
||||
},
|
||||
|
||||
openPreferences: function () {
|
||||
openPreferences("paneSync", { urlParams: { entrypoint: "menupanel" } });
|
||||
},
|
||||
|
||||
openAccountsPage: function (action, urlParams={}) {
|
||||
let params = new URLSearchParams();
|
||||
if (action) {
|
||||
params.set("action", action);
|
||||
}
|
||||
for (let name in urlParams) {
|
||||
if (urlParams[name] !== undefined) {
|
||||
params.set(name, urlParams[name]);
|
||||
}
|
||||
}
|
||||
let url = "about:accounts?" + params;
|
||||
switchToTabHavingURI(url, true, {
|
||||
replaceQueryString: true
|
||||
});
|
||||
},
|
||||
|
||||
openSignInAgainPage: function (entryPoint) {
|
||||
this.openAccountsPage("reauth", { entrypoint: entryPoint });
|
||||
},
|
||||
|
||||
updateTabContextMenu: function (aPopupMenu) {
|
||||
// STUB
|
||||
},
|
||||
|
||||
initPageContextMenu: function (contextMenu) {
|
||||
// STUB
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gFxAccounts, "FxAccountsCommon", function () {
|
||||
return Cu.import("resource://gre/modules/FxAccountsCommon.js", {});
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(gFxAccounts, "fxaMigrator",
|
||||
"resource://services-sync/FxaMigrator.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "EnsureFxAccountsWebChannel",
|
||||
"resource://gre/modules/FxAccountsWebChannel.jsm");
|
|
@ -202,9 +202,6 @@
|
|||
key="key_gotoHistory"
|
||||
observes="viewHistorySidebar"
|
||||
label="&historyButton.label;"/>
|
||||
<menuitem id="menu_tabsSidebar"
|
||||
observes="viewTabsSidebar"
|
||||
label="&syncedTabs.sidebar.label;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
|
@ -446,12 +443,7 @@
|
|||
label="&toolsMenu.label;"
|
||||
accesskey="&toolsMenu.accesskey;"
|
||||
onpopupshowing="mirrorShow(this)">
|
||||
<menupopup id="menu_ToolsPopup"
|
||||
# We have to use setTimeout() here to avoid a flickering menu bar when opening
|
||||
# the Tools menu, see bug 970769. This can be removed once we got rid of the
|
||||
# event loop spinning in Weave.Status._authManager.
|
||||
onpopupshowing="setTimeout(() => gSyncUI.updateUI());"
|
||||
>
|
||||
<menupopup id="menu_ToolsPopup">
|
||||
<menuitem id="menu_openDownloads"
|
||||
label="&downloads.label;"
|
||||
accesskey="&downloads.accesskey;"
|
||||
|
@ -462,23 +454,6 @@
|
|||
accesskey="&addons.accesskey;"
|
||||
key="key_openAddons"
|
||||
command="Tools:Addons"/>
|
||||
|
||||
<!-- only one of sync-setup, sync-syncnowitem or sync-reauthitem will be showing at once -->
|
||||
<menuitem id="sync-setup"
|
||||
label="&syncSignIn.label;"
|
||||
accesskey="&syncSignIn.accesskey;"
|
||||
observes="sync-setup-state"
|
||||
oncommand="gSyncUI.openSetup(null, 'menubar')"/>
|
||||
<menuitem id="sync-syncnowitem"
|
||||
label="&syncSyncNowItem.label;"
|
||||
accesskey="&syncSyncNowItem.accesskey;"
|
||||
observes="sync-syncnow-state"
|
||||
oncommand="gSyncUI.doSync(event);"/>
|
||||
<menuitem id="sync-reauthitem"
|
||||
label="&syncReAuthItem.label;"
|
||||
accesskey="&syncReAuthItem.accesskey;"
|
||||
observes="sync-reauth-state"
|
||||
oncommand="gSyncUI.openSignInAgainPage('menubar');"/>
|
||||
<menuseparator id="devToolsSeparator"/>
|
||||
<menu id="webDeveloperMenu"
|
||||
label="&webDeveloperMenu.label;"
|
||||
|
|
|
@ -169,10 +169,6 @@
|
|||
<broadcaster id="sync-setup-state"/>
|
||||
<broadcaster id="sync-syncnow-state" hidden="true"/>
|
||||
<broadcaster id="sync-reauth-state" hidden="true"/>
|
||||
<broadcaster id="viewTabsSidebar" autoCheck="false" sidebartitle="&syncedTabs.sidebar.label;"
|
||||
type="checkbox" group="sidebar"
|
||||
sidebarurl="chrome://browser/content/syncedtabs/sidebar.xhtml"
|
||||
oncommand="SidebarUI.toggle('viewTabsSidebar');"/>
|
||||
<broadcaster id="workOfflineMenuitemState"/>
|
||||
|
||||
<broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
|
||||
|
|
|
@ -1,563 +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/. */
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
const MIN_STATUS_ANIMATION_DURATION = 1600;
|
||||
|
||||
// gSyncUI handles updating the tools menu and displaying notifications.
|
||||
var gSyncUI = {
|
||||
_obs: ["weave:service:sync:start",
|
||||
"weave:service:sync:finish",
|
||||
"weave:service:sync:error",
|
||||
"weave:service:quota:remaining",
|
||||
"weave:service:setup-complete",
|
||||
"weave:service:login:start",
|
||||
"weave:service:login:finish",
|
||||
"weave:service:login:error",
|
||||
"weave:service:logout:finish",
|
||||
"weave:service:start-over",
|
||||
"weave:service:start-over:finish",
|
||||
"weave:ui:login:error",
|
||||
"weave:ui:sync:error",
|
||||
"weave:ui:sync:finish",
|
||||
"weave:ui:clear-error",
|
||||
"weave:engine:sync:finish"
|
||||
],
|
||||
|
||||
_unloaded: false,
|
||||
// The last sync start time. Used to calculate the leftover animation time
|
||||
// once syncing completes (bug 1239042).
|
||||
_syncStartTime: 0,
|
||||
_syncAnimationTimer: 0,
|
||||
|
||||
init: function () {
|
||||
Cu.import("resource://services-common/stringbundle.js");
|
||||
|
||||
// Proceed to set up the UI if Sync has already started up.
|
||||
// Otherwise we'll do it when Sync is firing up.
|
||||
if (this.weaveService.ready) {
|
||||
this.initUI();
|
||||
return;
|
||||
}
|
||||
|
||||
// Sync isn't ready yet, but we can still update the UI with an initial
|
||||
// state - we haven't called initUI() yet, but that's OK - that's more
|
||||
// about observers for state changes, and will be called once Sync is
|
||||
// ready to start sending notifications.
|
||||
this.updateUI();
|
||||
|
||||
Services.obs.addObserver(this, "weave:service:ready", true);
|
||||
Services.obs.addObserver(this, "quit-application", true);
|
||||
|
||||
// Remove the observer if the window is closed before the observer
|
||||
// was triggered.
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
gSyncUI._unloaded = true;
|
||||
window.removeEventListener("unload", onUnload, false);
|
||||
Services.obs.removeObserver(gSyncUI, "weave:service:ready");
|
||||
Services.obs.removeObserver(gSyncUI, "quit-application");
|
||||
|
||||
if (Weave.Status.ready) {
|
||||
gSyncUI._obs.forEach(function(topic) {
|
||||
Services.obs.removeObserver(gSyncUI, topic);
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
|
||||
initUI: function SUI_initUI() {
|
||||
// If this is a browser window?
|
||||
if (gBrowser) {
|
||||
this._obs.push("weave:notification:added");
|
||||
}
|
||||
|
||||
this._obs.forEach(function(topic) {
|
||||
Services.obs.addObserver(this, topic, true);
|
||||
}, this);
|
||||
|
||||
// initial label for the sync buttons.
|
||||
let broadcaster = document.getElementById("sync-status");
|
||||
broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncnow.label"));
|
||||
|
||||
this.maybeMoveSyncedTabsButton();
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
|
||||
// Returns a promise that resolves with true if Sync needs to be configured,
|
||||
// false otherwise.
|
||||
_needsSetup() {
|
||||
// If Sync is configured for FxAccounts then we do that promise-dance.
|
||||
if (this.weaveService.fxAccountsEnabled) {
|
||||
return fxAccounts.getSignedInUser().then(user => {
|
||||
// We want to treat "account needs verification" as "needs setup".
|
||||
return !(user && user.verified);
|
||||
});
|
||||
}
|
||||
// We are using legacy sync - check that.
|
||||
let firstSync = "";
|
||||
try {
|
||||
firstSync = Services.prefs.getCharPref("services.sync.firstSync");
|
||||
} catch (e) { }
|
||||
|
||||
return Promise.resolve(Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
|
||||
firstSync == "notReady");
|
||||
},
|
||||
|
||||
// Returns a promise that resolves with true if the user currently signed in
|
||||
// to Sync needs to be verified, false otherwise.
|
||||
_needsVerification() {
|
||||
// For callers who care about the distinction between "needs setup" and
|
||||
// "needs verification"
|
||||
if (this.weaveService.fxAccountsEnabled) {
|
||||
return fxAccounts.getSignedInUser().then(user => {
|
||||
// If there is no user, they can't be in a "needs verification" state.
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
return !user.verified;
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise we are configured for legacy Sync, which has no verification
|
||||
// concept.
|
||||
return Promise.resolve(false);
|
||||
},
|
||||
|
||||
// Note that we don't show login errors in a notification bar here, but do
|
||||
// still need to track a login-failed state so the "Tools" menu updates
|
||||
// with the correct state.
|
||||
_loginFailed: function () {
|
||||
// If Sync isn't already ready, we don't want to force it to initialize
|
||||
// by referencing Weave.Status - and it isn't going to be accurate before
|
||||
// Sync is ready anyway.
|
||||
if (!this.weaveService.ready) {
|
||||
this.log.debug("_loginFailed has sync not ready, so returning false");
|
||||
return false;
|
||||
}
|
||||
this.log.debug("_loginFailed has sync state=${sync}",
|
||||
{ sync: Weave.Status.login});
|
||||
return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
|
||||
},
|
||||
|
||||
// Kick off an update of the UI - does *not* return a promise.
|
||||
updateUI() {
|
||||
this._promiseUpdateUI().catch(err => {
|
||||
this.log.error("updateUI failed", err);
|
||||
})
|
||||
},
|
||||
|
||||
// Updates the UI - returns a promise.
|
||||
_promiseUpdateUI() {
|
||||
return this._needsSetup().then(needsSetup => {
|
||||
if (!gBrowser)
|
||||
return Promise.resolve();
|
||||
|
||||
let loginFailed = this._loginFailed();
|
||||
|
||||
// Start off with a clean slate
|
||||
document.getElementById("sync-reauth-state").hidden = true;
|
||||
document.getElementById("sync-setup-state").hidden = true;
|
||||
document.getElementById("sync-syncnow-state").hidden = true;
|
||||
|
||||
if (loginFailed) {
|
||||
// unhiding this element makes the menubar show the login failure state.
|
||||
document.getElementById("sync-reauth-state").hidden = false;
|
||||
} else if (needsSetup) {
|
||||
document.getElementById("sync-setup-state").hidden = false;
|
||||
} else {
|
||||
document.getElementById("sync-syncnow-state").hidden = false;
|
||||
}
|
||||
|
||||
return this._updateSyncButtonsTooltip();
|
||||
});
|
||||
},
|
||||
|
||||
// Functions called by observers
|
||||
onActivityStart() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
|
||||
this.log.debug("onActivityStart");
|
||||
|
||||
clearTimeout(this._syncAnimationTimer);
|
||||
this._syncStartTime = Date.now();
|
||||
|
||||
let broadcaster = document.getElementById("sync-status");
|
||||
broadcaster.setAttribute("syncstatus", "active");
|
||||
broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncing2.label"));
|
||||
broadcaster.setAttribute("disabled", "true");
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
_updateSyncStatus() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
let broadcaster = document.getElementById("sync-status");
|
||||
broadcaster.removeAttribute("syncstatus");
|
||||
broadcaster.removeAttribute("disabled");
|
||||
broadcaster.setAttribute("label", this._stringBundle.GetStringFromName("syncnow.label"));
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
onActivityStop() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
this.log.debug("onActivityStop");
|
||||
|
||||
let now = Date.now();
|
||||
let syncDuration = now - this._syncStartTime;
|
||||
|
||||
if (syncDuration < MIN_STATUS_ANIMATION_DURATION) {
|
||||
let animationTime = MIN_STATUS_ANIMATION_DURATION - syncDuration;
|
||||
clearTimeout(this._syncAnimationTimer);
|
||||
this._syncAnimationTimer = setTimeout(() => this._updateSyncStatus(), animationTime);
|
||||
} else {
|
||||
this._updateSyncStatus();
|
||||
}
|
||||
},
|
||||
|
||||
onLoginError: function SUI_onLoginError() {
|
||||
this.log.debug("onLoginError: login=${login}, sync=${sync}", Weave.Status);
|
||||
|
||||
// We don't show any login errors here; browser-fxaccounts shows them in
|
||||
// the hamburger menu.
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
onLogout: function SUI_onLogout() {
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
onQuotaNotice: function onQuotaNotice(subject, data) {
|
||||
let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
|
||||
let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
|
||||
let buttons = [];
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
|
||||
function() { gSyncUI.openQuotaDialog(); return true; }
|
||||
));
|
||||
|
||||
let notification = new Weave.Notification(
|
||||
title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
},
|
||||
|
||||
_getAppName: function () {
|
||||
let brand = new StringBundle("chrome://branding/locale/brand.properties");
|
||||
return brand.get("brandShortName");
|
||||
},
|
||||
|
||||
// Commands
|
||||
// doSync forces a sync - it *does not* return a promise as it is called
|
||||
// via the various UI components.
|
||||
doSync() {
|
||||
this._needsSetup().then(needsSetup => {
|
||||
if (!needsSetup) {
|
||||
setTimeout(() => Weave.Service.errorHandler.syncAndReportErrors(), 0);
|
||||
}
|
||||
Services.obs.notifyObservers(null, "cloudsync:user-sync", null);
|
||||
}).catch(err => {
|
||||
this.log.error("Failed to force a sync", err);
|
||||
});
|
||||
},
|
||||
|
||||
// Handle clicking the toolbar button - which either opens the Sync setup
|
||||
// pages or forces a sync now. Does *not* return a promise as it is called
|
||||
// via the UI.
|
||||
handleToolbarButton() {
|
||||
this._needsSetup().then(needsSetup => {
|
||||
if (needsSetup || this._loginFailed()) {
|
||||
this.openSetup();
|
||||
} else {
|
||||
this.doSync();
|
||||
}
|
||||
}).catch(err => {
|
||||
this.log.error("Failed to handle toolbar button command", err);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoke the Sync setup wizard.
|
||||
*
|
||||
* @param wizardType
|
||||
* Indicates type of wizard to launch:
|
||||
* null -- regular set up wizard
|
||||
* "pair" -- pair a device first
|
||||
* "reset" -- reset sync
|
||||
* @param entryPoint
|
||||
* Indicates the entrypoint from where this method was called.
|
||||
*/
|
||||
|
||||
openSetup: function SUI_openSetup(wizardType, entryPoint = "syncbutton") {
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
// Open the legacy-sync device pairing UI. Note used for FxA Sync.
|
||||
openAddDevice: function () {
|
||||
if (!Weave.Utils.ensureMPUnlocked())
|
||||
return;
|
||||
|
||||
let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
|
||||
if (win)
|
||||
win.focus();
|
||||
else
|
||||
window.openDialog("chrome://browser/content/sync/addDevice.xul",
|
||||
"syncAddDevice", "centerscreen,chrome,resizable=no");
|
||||
},
|
||||
|
||||
openPrefs: function (entryPoint) {
|
||||
openPreferences("paneSync", { urlParams: { entrypoint: entryPoint } });
|
||||
},
|
||||
|
||||
openSignInAgainPage: function (entryPoint = "syncbutton") {
|
||||
gFxAccounts.openSignInAgainPage(entryPoint);
|
||||
},
|
||||
|
||||
/* After Sync is initialized we perform a once-only check for the sync
|
||||
button being in "customize purgatory" and if so, move it to the panel.
|
||||
This is done primarily for profiles created before SyncedTabs landed,
|
||||
where the button defaulted to being in that purgatory.
|
||||
We use a preference to ensure we only do it once, so people can still
|
||||
customize it away and have it stick.
|
||||
*/
|
||||
maybeMoveSyncedTabsButton() {
|
||||
const prefName = "browser.migrated-sync-button";
|
||||
let migrated = false;
|
||||
try {
|
||||
migrated = Services.prefs.getBoolPref(prefName);
|
||||
} catch (_) {}
|
||||
if (migrated) {
|
||||
return;
|
||||
}
|
||||
if (!CustomizableUI.getPlacementOfWidget("sync-button")) {
|
||||
CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL);
|
||||
}
|
||||
Services.prefs.setBoolPref(prefName, true);
|
||||
},
|
||||
|
||||
/* Update the tooltip for the sync-status broadcaster (which will update the
|
||||
Sync Toolbar button and the Sync spinner in the FxA hamburger area.)
|
||||
If Sync is configured, the tooltip is when the last sync occurred,
|
||||
otherwise the tooltip reflects the fact that Sync needs to be
|
||||
(re-)configured.
|
||||
*/
|
||||
_updateSyncButtonsTooltip: Task.async(function* () {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
|
||||
let email;
|
||||
try {
|
||||
email = Services.prefs.getCharPref("services.sync.username");
|
||||
} catch (ex) {}
|
||||
|
||||
let needsSetup = yield this._needsSetup();
|
||||
let needsVerification = yield this._needsVerification();
|
||||
let loginFailed = this._loginFailed();
|
||||
// This is a little messy as the Sync buttons are 1/2 Sync related and
|
||||
// 1/2 FxA related - so for some strings we use Sync strings, but for
|
||||
// others we reach into gFxAccounts for strings.
|
||||
let tooltiptext;
|
||||
if (needsVerification) {
|
||||
// "needs verification"
|
||||
tooltiptext = gFxAccounts.strings.formatStringFromName("verifyDescription", [email], 1);
|
||||
} else if (needsSetup) {
|
||||
// "needs setup".
|
||||
tooltiptext = this._stringBundle.GetStringFromName("signInToSync.description");
|
||||
} else if (loginFailed) {
|
||||
// "need to reconnect/re-enter your password"
|
||||
tooltiptext = gFxAccounts.strings.formatStringFromName("reconnectDescription", [email], 1);
|
||||
} else {
|
||||
// Sync appears configured - format the "last synced at" time.
|
||||
try {
|
||||
let lastSync = new Date(Services.prefs.getCharPref("services.sync.lastSync"));
|
||||
tooltiptext = this.formatLastSyncDate(lastSync);
|
||||
}
|
||||
catch (e) {
|
||||
// pref doesn't exist (which will be the case until we've seen the
|
||||
// first successful sync) or is invalid (which should be impossible!)
|
||||
// Just leave tooltiptext as the empty string in these cases, which
|
||||
// will cause the tooltip to be removed below.
|
||||
}
|
||||
}
|
||||
|
||||
// We've done all our promise-y work and ready to update the UI - make
|
||||
// sure it hasn't been torn down since we started.
|
||||
if (!gBrowser)
|
||||
return;
|
||||
|
||||
let broadcaster = document.getElementById("sync-status");
|
||||
if (broadcaster) {
|
||||
if (tooltiptext) {
|
||||
broadcaster.setAttribute("tooltiptext", tooltiptext);
|
||||
} else {
|
||||
broadcaster.removeAttribute("tooltiptext");
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
formatLastSyncDate: function(date) {
|
||||
let dateFormat;
|
||||
let sixDaysAgo = (() => {
|
||||
let date = new Date();
|
||||
date.setDate(date.getDate() - 6);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
})();
|
||||
// It may be confusing for the user to see "Last Sync: Monday" when the last sync was a indeed a Monday but 3 weeks ago
|
||||
if (date < sixDaysAgo) {
|
||||
dateFormat = {month: 'long', day: 'numeric'};
|
||||
} else {
|
||||
dateFormat = {weekday: 'long', hour: 'numeric', minute: 'numeric'};
|
||||
}
|
||||
let lastSyncDateString = date.toLocaleDateString(undefined, dateFormat);
|
||||
return this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDateString], 1);
|
||||
},
|
||||
|
||||
onClientsSynced: function() {
|
||||
let broadcaster = document.getElementById("sync-syncnow-state");
|
||||
if (broadcaster) {
|
||||
if (Weave.Service.clientsEngine.stats.numClients > 1) {
|
||||
broadcaster.setAttribute("devices-status", "multi");
|
||||
} else {
|
||||
broadcaster.setAttribute("devices-status", "single");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSyncError: function SUI_onSyncError() {
|
||||
this.log.debug("onSyncError: login=${login}, sync=${sync}", Weave.Status);
|
||||
let title = this._stringBundle.GetStringFromName("error.sync.title");
|
||||
let error = Weave.Utils.getErrorString(Weave.Status.sync);
|
||||
let description =
|
||||
this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
|
||||
let priority = Weave.Notifications.PRIORITY_WARNING;
|
||||
let buttons = [];
|
||||
|
||||
if (Weave.Status.sync == Weave.OVER_QUOTA) {
|
||||
description = this._stringBundle.GetStringFromName("error.sync.quota.description");
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
|
||||
function() { gSyncUI.openQuotaDialog(); return true; } )
|
||||
);
|
||||
// Only show the notification bar on Quota error. the panel will show the rest.
|
||||
let notification =
|
||||
new Weave.Notification(title, description, null, priority, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
}
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
|
||||
observe: function SUI_observe(subject, topic, data) {
|
||||
this.log.debug("observed", topic);
|
||||
if (this._unloaded) {
|
||||
Cu.reportError("SyncUI observer called after unload: " + topic);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unwrap, just like Svc.Obs, but without pulling in that dependency.
|
||||
if (subject && typeof subject == "object" &&
|
||||
("wrappedJSObject" in subject) &&
|
||||
("observersModuleSubjectWrapper" in subject.wrappedJSObject)) {
|
||||
subject = subject.wrappedJSObject.object;
|
||||
}
|
||||
|
||||
// First handle "activity" only.
|
||||
switch (topic) {
|
||||
case "weave:service:sync:start":
|
||||
this.onActivityStart();
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
case "weave:service:sync:error":
|
||||
this.onActivityStop();
|
||||
break;
|
||||
}
|
||||
// Now non-activity state (eg, enabled, errors, etc)
|
||||
// Note that sync uses the ":ui:" notifications for errors because sync.
|
||||
switch (topic) {
|
||||
case "weave:ui:sync:finish":
|
||||
// Do nothing.
|
||||
break;
|
||||
case "weave:ui:sync:error":
|
||||
this.onSyncError();
|
||||
break;
|
||||
case "weave:service:setup-complete":
|
||||
case "weave:service:login:finish":
|
||||
case "weave:service:login:start":
|
||||
case "weave:service:start-over":
|
||||
this.updateUI();
|
||||
break;
|
||||
case "weave:service:quota:remaining":
|
||||
this.onQuotaNotice();
|
||||
break;
|
||||
case "weave:ui:login:error":
|
||||
case "weave:service:login:error":
|
||||
this.onLoginError();
|
||||
break;
|
||||
case "weave:service:logout:finish":
|
||||
this.onLogout();
|
||||
break;
|
||||
case "weave:service:start-over:finish":
|
||||
this.updateUI();
|
||||
break;
|
||||
case "weave:service:ready":
|
||||
this.initUI();
|
||||
break;
|
||||
case "weave:notification:added":
|
||||
this.initNotifications();
|
||||
break;
|
||||
case "weave:engine:sync:finish":
|
||||
if (data != "clients") {
|
||||
return;
|
||||
}
|
||||
this.onClientsSynced();
|
||||
break;
|
||||
case "quit-application":
|
||||
// Stop the animation timer on shutdown, since we can't update the UI
|
||||
// after this.
|
||||
clearTimeout(this._syncAnimationTimer);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference
|
||||
])
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
|
||||
// XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
|
||||
// but for now just make it work
|
||||
return Cc["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Ci.nsIStringBundleService).
|
||||
createBundle("chrome://weave/locale/services/sync.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
|
||||
return Log.repository.getLogger("browserwindow.syncui");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "weaveService", function() {
|
||||
return Components.classes["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
});
|
|
@ -47,7 +47,6 @@ Cu.import("resource://gre/modules/NotificationDB.jsm");
|
|||
["Task", "resource://gre/modules/Task.jsm"],
|
||||
["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
|
||||
["Weave", "resource://services-sync/main.js"],
|
||||
["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
|
||||
#ifdef MOZ_DEVTOOLS
|
||||
// Note: Do not delete! It is used for: base/content/nsContextMenu.js
|
||||
["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"],
|
||||
|
@ -1337,10 +1336,6 @@ var gBrowserInit = {
|
|||
FullScreen.init();
|
||||
PointerLock.init();
|
||||
|
||||
// initialize the sync UI
|
||||
gSyncUI.init();
|
||||
gFxAccounts.init();
|
||||
|
||||
if (AppConstants.MOZ_DATA_REPORTING)
|
||||
gDataNotificationInfoBar.init();
|
||||
|
||||
|
@ -1477,8 +1472,6 @@ var gBrowserInit = {
|
|||
|
||||
FullScreen.uninit();
|
||||
|
||||
gFxAccounts.uninit();
|
||||
|
||||
Services.obs.removeObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");
|
||||
|
||||
try {
|
||||
|
@ -1636,9 +1629,6 @@ if (AppConstants.platform == "macosx") {
|
|||
|
||||
// initialize the private browsing UI
|
||||
gPrivateBrowsingUI.init();
|
||||
|
||||
// initialize the sync UI
|
||||
gSyncUI.init();
|
||||
};
|
||||
|
||||
gBrowserInit.nonBrowserWindowShutdown = function() {
|
||||
|
@ -7629,8 +7619,6 @@ var TabContextMenu = {
|
|||
|
||||
this.contextTab.addEventListener("TabAttrModified", this, false);
|
||||
aPopupMenu.addEventListener("popuphiding", this, false);
|
||||
|
||||
gFxAccounts.updateTabContextMenu(aPopupMenu);
|
||||
},
|
||||
handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
|
|
|
@ -391,59 +391,6 @@
|
|||
|
||||
<tooltip id="dynamic-shortcut-tooltip"
|
||||
onpopupshowing="UpdateDynamicShortcutTooltipText(this);"/>
|
||||
|
||||
<menupopup id="SyncedTabsSidebarContext">
|
||||
<menuitem label="&syncedTabs.context.open.label;"
|
||||
accesskey="&syncedTabs.context.open.accesskey;"
|
||||
id="syncedTabsOpenSelected" where="current"/>
|
||||
<menuitem label="&syncedTabs.context.openInNewTab.label;"
|
||||
accesskey="&syncedTabs.context.openInNewTab.accesskey;"
|
||||
id="syncedTabsOpenSelectedInTab" where="tab"/>
|
||||
<menuitem label="&syncedTabs.context.openInNewWindow.label;"
|
||||
accesskey="&syncedTabs.context.openInNewWindow.accesskey;"
|
||||
id="syncedTabsOpenSelectedInWindow" where="window"/>
|
||||
<menuitem label="&syncedTabs.context.openInNewPrivateWindow.label;"
|
||||
accesskey="&syncedTabs.context.openInNewPrivateWindow.accesskey;"
|
||||
id="syncedTabsOpenSelectedInPrivateWindow" where="window" private="true"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&syncedTabs.context.bookmarkSingleTab.label;"
|
||||
accesskey="&syncedTabs.context.bookmarkSingleTab.accesskey;"
|
||||
id="syncedTabsBookmarkSelected"/>
|
||||
<menuitem label="&syncedTabs.context.copy.label;"
|
||||
accesskey="&syncedTabs.context.copy.accesskey;"
|
||||
id="syncedTabsCopySelected"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&syncSyncNowItem.label;"
|
||||
accesskey="&syncSyncNowItem.accesskey;"
|
||||
id="syncedTabsRefresh"/>
|
||||
</menupopup>
|
||||
<menupopup id="SyncedTabsSidebarTabsFilterContext"
|
||||
class="textbox-contextmenu">
|
||||
<menuitem label="&undoCmd.label;"
|
||||
accesskey="&undoCmd.accesskey;"
|
||||
cmd="cmd_undo"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&cutCmd.label;"
|
||||
accesskey="&cutCmd.accesskey;"
|
||||
cmd="cmd_cut"/>
|
||||
<menuitem label="©Cmd.label;"
|
||||
accesskey="©Cmd.accesskey;"
|
||||
cmd="cmd_copy"/>
|
||||
<menuitem label="&pasteCmd.label;"
|
||||
accesskey="&pasteCmd.accesskey;"
|
||||
cmd="cmd_paste"/>
|
||||
<menuitem label="&deleteCmd.label;"
|
||||
accesskey="&deleteCmd.accesskey;"
|
||||
cmd="cmd_delete"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&selectAllCmd.label;"
|
||||
accesskey="&selectAllCmd.accesskey;"
|
||||
cmd="cmd_selectAll"/>
|
||||
<menuseparator/>
|
||||
<menuitem label="&syncSyncNowItem.label;"
|
||||
accesskey="&syncSyncNowItem.accesskey;"
|
||||
id="syncedTabsRefreshFilter"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
<script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
|
||||
#endif
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-syncui.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-thumbnails.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-trackingprotection.js"/>
|
||||
|
@ -35,4 +34,3 @@
|
|||
<script type="application/javascript" src="chrome://browser/content/browser-data-submission-info-bar.js"/>
|
||||
#endif
|
||||
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-fxaccounts.js"/>
|
||||
|
|
|
@ -113,7 +113,6 @@ nsContextMenu.prototype = {
|
|||
this.initLeaveDOMFullScreenItems();
|
||||
this.initClickToPlayItems();
|
||||
this.initPasswordManagerItems();
|
||||
this.initSyncItems();
|
||||
},
|
||||
|
||||
initPageMenuSeparator: function CM_initPageMenuSeparator() {
|
||||
|
@ -540,10 +539,6 @@ nsContextMenu.prototype = {
|
|||
popup.insertBefore(fragment, insertBeforeElement);
|
||||
},
|
||||
|
||||
initSyncItems: function() {
|
||||
gFxAccounts.initPageContextMenu(this);
|
||||
},
|
||||
|
||||
openPasswordManager: function() {
|
||||
LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
|
||||
},
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?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>
|
|
@ -1,11 +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/. */
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,350 +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/. */
|
||||
|
||||
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");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.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);
|
||||
|
||||
Services.obs.addObserver(this, "cloudsync:tabs:update", 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");
|
||||
|
||||
Services.obs.removeObserver(this, "cloudsync:tabs:update");
|
||||
},
|
||||
|
||||
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 (forceSync) {
|
||||
if (this._waitingForBuildList) {
|
||||
this._buildListRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this._waitingForBuildList = true;
|
||||
this._buildListRequested = false;
|
||||
|
||||
this._clearTabList();
|
||||
|
||||
if (Weave.Service.isLoggedIn) {
|
||||
this._refetchTabs(forceSync);
|
||||
this._generateWeaveTabList();
|
||||
} else {
|
||||
// XXXzpao We should say something about not being logged in & not having data
|
||||
// or tell the appropriate condition. (bug 583344)
|
||||
}
|
||||
|
||||
let 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 [, client] of Object.entries(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);
|
||||
}
|
||||
},
|
||||
|
||||
_generateCloudSyncTabList: function () {
|
||||
let updateTabList = function (remoteTabs) {
|
||||
let list = this._tabsList;
|
||||
|
||||
for (let client of remoteTabs) {
|
||||
let clientAttrs = {
|
||||
type: "client",
|
||||
clientName: client.name,
|
||||
};
|
||||
|
||||
let clientEnt = this.createItem(clientAttrs);
|
||||
list.appendChild(clientEnt);
|
||||
|
||||
for (let tab of client.tabs) {
|
||||
let tabAttrs = {
|
||||
type: "tab",
|
||||
title: tab.title,
|
||||
url: tab.url,
|
||||
icon: this.getIcon(tab.icon),
|
||||
};
|
||||
let tabEnt = this.createItem(tabAttrs);
|
||||
list.appendChild(tabEnt);
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
return CloudSync().tabs.getRemoteTabs()
|
||||
.then(updateTabList, Promise.reject.bind(Promise));
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Ask Sync to just do the tabs engine if it can.
|
||||
Weave.Service.sync(["tabs"]);
|
||||
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":
|
||||
// A login has finished, which means that a Sync is about to start and
|
||||
// we will eventually get to the "tabs" engine - but try and force the
|
||||
// tab engine to sync first by passing |true| for the forceSync param.
|
||||
this.buildList(true);
|
||||
break;
|
||||
case "weave:engine:sync:finish":
|
||||
if (data == "tabs") {
|
||||
// The tabs engine just finished, so re-build the list without
|
||||
// forcing a new sync of the tabs engine.
|
||||
this.buildList(false);
|
||||
}
|
||||
break;
|
||||
case "cloudsync:tabs:update":
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +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/. -->
|
||||
|
||||
<?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>
|
||||
|
|
@ -1,157 +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/. */
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
|
@ -1,129 +0,0 @@
|
|||
<?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="https://services.mozilla.com/sync/help/add-device"/>
|
||||
</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>
|
|
@ -1,28 +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/. */
|
||||
|
||||
:root {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
#sync-customize-pane {
|
||||
padding-inline-start: 74px;
|
||||
background: top left url(chrome://browser/skin/sync-128.png) no-repeat;
|
||||
background-size: 64px;
|
||||
}
|
||||
|
||||
#sync-customize-title {
|
||||
margin-inline-start: 0;
|
||||
padding-bottom: 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sync-customize-subtitle {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
checkbox {
|
||||
margin: 0;
|
||||
padding: 0.5em 0 0;
|
||||
}
|
|
@ -1,25 +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";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
addEventListener("dialogaccept", function () {
|
||||
let pane = document.getElementById("sync-customize-pane");
|
||||
// First determine what the preference for the "global" sync enabled pref
|
||||
// should be based on the engines selected.
|
||||
let prefElts = pane.querySelectorAll("preferences > preference");
|
||||
let syncEnabled = false;
|
||||
for (let elt of prefElts) {
|
||||
if (elt.name.startsWith("services.sync.") && elt.value) {
|
||||
syncEnabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Services.prefs.setBoolPref("services.sync.enabled", syncEnabled);
|
||||
// and write the individual prefs.
|
||||
pane.writePreferences(true);
|
||||
window.arguments[0].accepted = true;
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
<?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/content/sync/customize.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog [
|
||||
<!ENTITY % syncCustomizeDTD SYSTEM "chrome://browser/locale/syncCustomize.dtd">
|
||||
%syncCustomizeDTD;
|
||||
]>
|
||||
<dialog id="sync-customize"
|
||||
windowtype="Sync:Customize"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&syncCustomize.dialog.title;"
|
||||
buttonlabelaccept="&syncCustomize.acceptButton.label;"
|
||||
buttons="accept">
|
||||
|
||||
<prefpane id="sync-customize-pane">
|
||||
<preferences>
|
||||
<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.passwords" name="services.sync.engine.passwords" type="bool"/>
|
||||
<preference id="engine.addons" name="services.sync.engine.addons" type="bool"/>
|
||||
<preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/>
|
||||
</preferences>
|
||||
|
||||
<label id="sync-customize-title" value="&syncCustomize.title;"/>
|
||||
<description id="sync-customize-subtitle"
|
||||
value="&syncCustomize.description;"/>
|
||||
|
||||
<vbox align="start">
|
||||
<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"/>
|
||||
<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>
|
||||
|
||||
</prefpane>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/customize.js" />
|
||||
|
||||
</dialog>
|
|
@ -1,233 +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/. */
|
||||
|
||||
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 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;
|
||||
this._passphraseBox.setAttribute("readonly", "true");
|
||||
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();
|
||||
case "ChangePassword":
|
||||
return this.doChangePassword();
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
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 {
|
||||
if (!this._updatingPassphrase)
|
||||
return;
|
||||
|
||||
valid = this._passphraseBox.value != "";
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
|
@ -1,123 +0,0 @@
|
|||
<?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"
|
||||
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="https://services.mozilla.com/sync/help/manual-setup">
|
||||
&addDevice.showMeHow.label;
|
||||
</label>
|
||||
</description>
|
||||
</vbox>
|
||||
|
||||
<spacer id="passphraseSpacer"
|
||||
flex="1"
|
||||
hidden="true"/>
|
||||
|
||||
<description id="warningText" class="data">
|
||||
</description>
|
||||
|
||||
<spacer flex="1"/>
|
||||
</wizardpage>
|
||||
</wizard>
|
|
@ -1,54 +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 [
|
||||
<!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="https://services.mozilla.com">https://services.mozilla.com</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,247 +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/. */
|
||||
|
||||
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) {}
|
||||
|
||||
};
|
|
@ -1,65 +0,0 @@
|
|||
<?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>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,490 +0,0 @@
|
|||
<?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="https://services.mozilla.com/sync/help/add-device"/>
|
||||
</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"
|
||||
onclick="event.stopPropagation();gSyncUtils.openToS();">
|
||||
&setup.tosLink.label;
|
||||
</label>
|
||||
&setup.tosAgree2.label;
|
||||
<label class="text-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="https://services.mozilla.com/sync/help/easy-setup"/>
|
||||
</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="https://services.mozilla.com/sync/help/manual-setup">
|
||||
&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="margin-inline-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="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>
|
||||
|
|
@ -1,231 +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/. */
|
||||
|
||||
// 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");
|
||||
},
|
||||
|
||||
get fxAccountsEnabled() {
|
||||
let service = Components.classes["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
return service.fxAccountsEnabled;
|
||||
},
|
||||
|
||||
// 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);
|
||||
},
|
||||
|
||||
get tosURL() {
|
||||
let root = this.fxAccountsEnabled ? "fxa." : "";
|
||||
return Weave.Svc.Prefs.get(root + "termsURL");
|
||||
},
|
||||
|
||||
openToS: function () {
|
||||
this._openLink(this.tosURL);
|
||||
},
|
||||
|
||||
get privacyPolicyURL() {
|
||||
let root = this.fxAccountsEnabled ? "fxa." : "";
|
||||
return Weave.Svc.Prefs.get(root + "privacyURL");
|
||||
},
|
||||
|
||||
openPrivacyPolicy: function () {
|
||||
this._openLink(this.privacyPolicyURL);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
};
|
|
@ -23,7 +23,6 @@
|
|||
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/browser-fxaccounts.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/web-panels.js"/>
|
||||
|
||||
|
|
|
@ -49,16 +49,6 @@ browser.jar:
|
|||
content/browser/abouthealthreport/abouthealth.js (content/abouthealthreport/abouthealth.js)
|
||||
content/browser/abouthealthreport/abouthealth.css (content/abouthealthreport/abouthealth.css)
|
||||
#endif
|
||||
content/browser/aboutaccounts/aboutaccounts.xhtml (content/aboutaccounts/aboutaccounts.xhtml)
|
||||
content/browser/aboutaccounts/aboutaccounts.js (content/aboutaccounts/aboutaccounts.js)
|
||||
content/browser/aboutaccounts/aboutaccounts.css (content/aboutaccounts/aboutaccounts.css)
|
||||
content/browser/aboutaccounts/main.css (content/aboutaccounts/main.css)
|
||||
content/browser/aboutaccounts/normalize.css (content/aboutaccounts/normalize.css)
|
||||
content/browser/aboutaccounts/images/fox.png (content/aboutaccounts/images/fox.png)
|
||||
content/browser/aboutaccounts/images/graphic_sync_intro.png (content/aboutaccounts/images/graphic_sync_intro.png)
|
||||
content/browser/aboutaccounts/images/graphic_sync_intro@2x.png (content/aboutaccounts/images/graphic_sync_intro@2x.png)
|
||||
|
||||
|
||||
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
|
||||
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
|
||||
content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css)
|
||||
|
@ -75,7 +65,6 @@ browser.jar:
|
|||
content/browser/browser-feeds.js (content/browser-feeds.js)
|
||||
content/browser/browser-fullScreenAndPointerLock.js (content/browser-fullScreenAndPointerLock.js)
|
||||
content/browser/browser-fullZoom.js (content/browser-fullZoom.js)
|
||||
content/browser/browser-fxaccounts.js (content/browser-fxaccounts.js)
|
||||
content/browser/browser-gestureSupport.js (content/browser-gestureSupport.js)
|
||||
* content/browser/browser-media.js (content/browser-media.js)
|
||||
content/browser/browser-places.js (content/browser-places.js)
|
||||
|
@ -85,7 +74,6 @@ browser.jar:
|
|||
content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js)
|
||||
#endif
|
||||
content/browser/browser-sidebar.js (content/browser-sidebar.js)
|
||||
* content/browser/browser-syncui.js (content/browser-syncui.js)
|
||||
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
content/browser/browser-tabsintitlebar.js (content/browser-tabsintitlebar.js)
|
||||
|
@ -134,23 +122,6 @@ browser.jar:
|
|||
content/browser/pageinfo/feeds.xml (content/pageinfo/feeds.xml)
|
||||
content/browser/pageinfo/permissions.js (content/pageinfo/permissions.js)
|
||||
content/browser/pageinfo/security.js (content/pageinfo/security.js)
|
||||
content/browser/sync/aboutSyncTabs.xul (content/sync/aboutSyncTabs.xul)
|
||||
* content/browser/sync/aboutSyncTabs.js (content/sync/aboutSyncTabs.js)
|
||||
content/browser/sync/aboutSyncTabs.css (content/sync/aboutSyncTabs.css)
|
||||
content/browser/sync/aboutSyncTabs-bindings.xml (content/sync/aboutSyncTabs-bindings.xml)
|
||||
content/browser/sync/setup.xul (content/sync/setup.xul)
|
||||
content/browser/sync/addDevice.js (content/sync/addDevice.js)
|
||||
content/browser/sync/addDevice.xul (content/sync/addDevice.xul)
|
||||
content/browser/sync/setup.js (content/sync/setup.js)
|
||||
content/browser/sync/genericChange.xul (content/sync/genericChange.xul)
|
||||
content/browser/sync/genericChange.js (content/sync/genericChange.js)
|
||||
content/browser/sync/key.xhtml (content/sync/key.xhtml)
|
||||
content/browser/sync/utils.js (content/sync/utils.js)
|
||||
content/browser/sync/customize.xul (content/sync/customize.xul)
|
||||
content/browser/sync/customize.js (content/sync/customize.js)
|
||||
content/browser/sync/customize.css (content/sync/customize.css)
|
||||
content/browser/sync/quota.xul (content/sync/quota.xul)
|
||||
content/browser/sync/quota.js (content/sync/quota.js)
|
||||
content/browser/safeMode.css (content/safeMode.css)
|
||||
content/browser/safeMode.js (content/safeMode.js)
|
||||
content/browser/safeMode.xul (content/safeMode.xul)
|
||||
|
|
|
@ -127,10 +127,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 |
|
||||
|
|
|
@ -103,7 +103,6 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
|||
{ 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,7 +20,6 @@ DIRS += [
|
|||
'sessionstore',
|
||||
'shell',
|
||||
'selfsupport',
|
||||
'syncedtabs',
|
||||
'translation',
|
||||
]
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -92,19 +92,7 @@ var gSyncPane = {
|
|||
} 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
|
||||
} else {
|
||||
this.page = PAGE_HAS_ACCOUNT;
|
||||
}
|
||||
},
|
||||
|
@ -388,9 +376,6 @@ var gSyncPane = {
|
|||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
|
||||
if (service.fxAccountsEnabled) {
|
||||
this._openAboutAccounts();
|
||||
} else {
|
||||
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
|
||||
if (win)
|
||||
win.focus();
|
||||
|
@ -399,7 +384,6 @@ var gSyncPane = {
|
|||
"weaveSetup", "centerscreen,chrome,resizable=no",
|
||||
wizardType);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openContentInBrowser: function(url, options) {
|
||||
|
|
|
@ -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,7 +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/.
|
||||
|
||||
browser.jar:
|
||||
content/browser/syncedtabs/sidebar.xhtml
|
||||
content/browser/syncedtabs/sidebar.js
|
|
@ -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;
|
||||
}
|
|
@ -211,13 +211,11 @@ chrome/toolkit/skin/classic/mozapps/update/buttons.png
|
|||
chrome/toolkit/skin/classic/mozapps/update/downloadButtons.png
|
||||
chrome/toolkit/skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png
|
||||
|
||||
components/FxAccountsPush.js
|
||||
crashreporter.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
|
||||
crashreporter.app/Contents/Resources/English.lproj/MainMenuRTL.nib/classes.nib
|
||||
# firefox/firefox-bin is bug 658850
|
||||
@MOZ_APP_NAME@
|
||||
@MOZ_APP_NAME@-bin
|
||||
modules/FxAccountsPush.js
|
||||
modules/commonjs/index.js
|
||||
modules/commonjs/sdk/ui/button/view/events.js
|
||||
modules/commonjs/sdk/ui/state/events.js
|
||||
|
|
|
@ -486,8 +486,6 @@
|
|||
#endif
|
||||
@RESPATH@/components/SyncComponents.manifest
|
||||
@RESPATH@/components/Weave.js
|
||||
@RESPATH@/components/FxAccountsComponents.manifest
|
||||
@RESPATH@/components/FxAccountsPush.js
|
||||
@RESPATH@/components/servicesComponents.manifest
|
||||
@RESPATH@/components/cryptoComponents.manifest
|
||||
@RESPATH@/components/TelemetryStartup.js
|
||||
|
|
|
@ -1,16 +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/. -->
|
||||
|
||||
<!ENTITY aboutAccounts.welcome "Welcome to &syncBrand.shortName.label;">
|
||||
<!ENTITY aboutAccounts.connected "Account connected">
|
||||
|
||||
<!ENTITY aboutAccountsConfig.description "Sign in to sync your tabs, bookmarks, passwords & more.">
|
||||
<!ENTITY aboutAccountsConfig.startButton.label "Get started">
|
||||
<!ENTITY aboutAccountsConfig.useOldSync.label "Using an older version of Sync?">
|
||||
<!ENTITY aboutAccountsConfig.syncPreferences.label "Sync preferences">
|
||||
<!ENTITY aboutAccounts.noConnection.title "No connection">
|
||||
<!ENTITY aboutAccounts.noConnection.description "You must be connected to the Internet to sign in.">
|
||||
<!ENTITY aboutAccounts.noConnection.retry "Try again">
|
||||
<!ENTITY aboutAccounts.badConfig.title "Bad configuration">
|
||||
<!ENTITY aboutAccounts.badConfig.description "Unable to determine your Firefox Account server configuration. Please try again later.">
|
|
@ -726,31 +726,6 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
|||
<!-- LOCALIZATION NOTE (syncTabsMenu3.label): This appears in the history menu -->
|
||||
<!ENTITY syncTabsMenu3.label "Synced Tabs">
|
||||
|
||||
<!ENTITY syncedTabs.sidebar.label "Synced Tabs">
|
||||
<!ENTITY syncedTabs.sidebar.noclients.label "Sign in to Firefox from your other devices to view their tabs here.">
|
||||
<!ENTITY syncedTabs.sidebar.noclients.title "No synced tabs… yet!">
|
||||
<!ENTITY syncedTabs.sidebar.noclients.subtitle "Want to see your tabs from other devices here?">
|
||||
<!ENTITY syncedTabs.sidebar.notsignedin.label "Sign in to view a list of tabs from your other devices.">
|
||||
<!ENTITY syncedTabs.sidebar.notabs.label "No open tabs">
|
||||
<!ENTITY syncedTabs.sidebar.openprefs.label "Open &syncBrand.shortName.label; Preferences">
|
||||
<!-- LOCALIZATION NOTE (syncedTabs.sidebar.tabsnotsyncing.label): This is shown
|
||||
when Sync is configured but syncing tabs is disabled. -->
|
||||
<!ENTITY syncedTabs.sidebar.tabsnotsyncing.label "Turn on tab syncing to view a list of tabs from your other devices.">
|
||||
|
||||
<!ENTITY syncedTabs.context.open.label "Open">
|
||||
<!ENTITY syncedTabs.context.open.accesskey "O">
|
||||
<!ENTITY syncedTabs.context.openInNewTab.label "Open in a New Tab">
|
||||
<!ENTITY syncedTabs.context.openInNewTab.accesskey "w">
|
||||
<!ENTITY syncedTabs.context.openInNewWindow.label "Open in a New Window">
|
||||
<!ENTITY syncedTabs.context.openInNewWindow.accesskey "N">
|
||||
<!ENTITY syncedTabs.context.openInNewPrivateWindow.label "Open in a New Private Window">
|
||||
<!ENTITY syncedTabs.context.openInNewPrivateWindow.accesskey "P">
|
||||
<!ENTITY syncedTabs.context.bookmarkSingleTab.label "Bookmark This Tab…">
|
||||
<!ENTITY syncedTabs.context.bookmarkSingleTab.accesskey "B">
|
||||
<!ENTITY syncedTabs.context.copy.label "Copy">
|
||||
<!ENTITY syncedTabs.context.copy.accesskey "C">
|
||||
|
||||
|
||||
<!ENTITY syncBrand.shortName.label "Sync">
|
||||
|
||||
<!ENTITY syncSignIn.label "Sign In To &syncBrand.shortName.label;…">
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
@AB_CD@.jar:
|
||||
% locale browser @AB_CD@ %locale/browser/
|
||||
* locale/browser/bookmarks.html (generic/profile/bookmarks.html.in)
|
||||
locale/browser/aboutAccounts.dtd (%chrome/browser/aboutAccounts.dtd)
|
||||
locale/browser/aboutDialog.dtd (%chrome/browser/aboutDialog.dtd)
|
||||
locale/browser/aboutPrivateBrowsing.dtd (%chrome/browser/aboutPrivateBrowsing.dtd)
|
||||
locale/browser/aboutPrivateBrowsing.properties (%chrome/browser/aboutPrivateBrowsing.properties)
|
||||
|
|
|
@ -17,8 +17,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
|||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AutoMigrate",
|
||||
"resource:///modules/AutoMigrate.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
|
|
|
@ -9,7 +9,6 @@ browser.jar:
|
|||
skin/classic/browser/sanitizeDialog.css
|
||||
skin/classic/browser/aboutSessionRestore-window-icon.png
|
||||
skin/classic/browser/aboutSyncTabs.css
|
||||
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/devedition.css
|
||||
|
|
|
@ -1,69 +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/. */
|
||||
|
||||
%include ../../shared/syncedtabs/sidebar.inc.css
|
||||
|
||||
/* These styles are intended to mimic XUL trees and the XUL search box. */
|
||||
|
||||
html {
|
||||
border: 1px solid ThreeDShadow;
|
||||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
margin: 1px 0 0;
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
|
||||
|
||||
.search-box {
|
||||
-moz-appearance: textfield;
|
||||
cursor: text;
|
||||
margin: 2px 4px;
|
||||
border: 2px solid;
|
||||
-moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
|
||||
-moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
|
||||
-moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
|
||||
-moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
|
||||
padding: 2px 2px 3px;
|
||||
padding-inline-start: 4px;
|
||||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
}
|
||||
|
||||
.textbox-search-clear {
|
||||
background-image: url(moz-icon://stock/gtk-clear?size=menu);
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.textbox-search-icon {
|
||||
background-image: url(moz-icon://stock/gtk-find?size=menu);
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.textbox-search-icon[searchbutton]:not([disabled]) ,
|
||||
.textbox-search-clear:not([disabled]) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container {
|
||||
-moz-appearance: treetwistyopen;
|
||||
margin-top: 3px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container {
|
||||
-moz-appearance: treetwisty;
|
||||
}
|
|
@ -8,7 +8,6 @@ browser.jar:
|
|||
skin/classic/browser/sanitizeDialog.css
|
||||
skin/classic/browser/aboutSessionRestore-window-icon.png
|
||||
skin/classic/browser/aboutSyncTabs.css
|
||||
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
skin/classic/browser/actionicon-tab@2x.png
|
||||
* skin/classic/browser/browser.css
|
||||
|
|
|
@ -1,154 +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/. */
|
||||
|
||||
%include ../../shared/syncedtabs/sidebar.inc.css
|
||||
|
||||
/* These styles are intended to mimic XUL trees and the XUL search box. */
|
||||
|
||||
.content-container {
|
||||
-moz-appearance: -moz-mac-source-list;
|
||||
}
|
||||
|
||||
.item {
|
||||
color: -moz-DialogText;
|
||||
}
|
||||
|
||||
.item-title-container {
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.item.selected > .item-title-container {
|
||||
color: HighlightText;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item.selected > .item-title-container {
|
||||
-moz-appearance: -moz-mac-source-list-selection;
|
||||
}
|
||||
|
||||
.item.selected:focus > .item-title-container {
|
||||
-moz-appearance: -moz-mac-active-source-list-selection;
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container {
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
|
||||
}
|
||||
|
||||
@media not all and (-moz-mac-yosemite-theme) {
|
||||
.item.client.selected .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
|
||||
}
|
||||
|
||||
.item.client.selected.closed .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
|
||||
}
|
||||
|
||||
.item.client.selected .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
|
||||
}
|
||||
|
||||
.item.client.selected.closed .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
|
||||
}
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed");
|
||||
}
|
||||
|
||||
.item.client.selected:focus .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
|
||||
}
|
||||
|
||||
.item.client.selected.closed:focus .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted");
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded");
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-rtl");
|
||||
}
|
||||
|
||||
.item.client.selected:focus .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-expanded-inverted");
|
||||
}
|
||||
|
||||
.item.client.selected.closed:focus .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/arrow-disclosure.svg#arrow-disclosure-collapsed-inverted-rtl");
|
||||
}
|
||||
|
||||
@media (-moz-mac-yosemite-theme) {
|
||||
.item.selected > .item-title-container {
|
||||
color: -moz-dialogtext;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item.selected:focus > .item-title-container {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-search-container {
|
||||
border-bottom: 1px solid #bdbdbd;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
-moz-appearance: searchfield;
|
||||
padding: 1px;
|
||||
font-size: 12px;
|
||||
cursor: text;
|
||||
margin: 4px 8px 10px;
|
||||
border-width: 3px;
|
||||
border-style: solid;
|
||||
border-color: currentcolor;
|
||||
border-image: none;
|
||||
-moz-border-top-colors: transparent #888 #000;
|
||||
-moz-border-right-colors: transparent #FFF #000;
|
||||
-moz-border-bottom-colors: transparent #FFF #000;
|
||||
-moz-border-left-colors: transparent #888 #000;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
background-color: #FFF;
|
||||
color: #000;
|
||||
-moz-user-select: text;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.search-box.compact > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
|
||||
background-image: url(chrome://global/skin/icons/searchfield-cancel.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 11px 11px;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.search-box.compact > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-box[focused="true"] {
|
||||
-moz-border-top-colors: -moz-mac-focusring -moz-mac-focusring #000000;
|
||||
-moz-border-right-colors: -moz-mac-focusring -moz-mac-focusring #000000;
|
||||
-moz-border-bottom-colors: -moz-mac-focusring -moz-mac-focusring #000000;
|
||||
-moz-border-left-colors: -moz-mac-focusring -moz-mac-focusring #000000;
|
||||
}
|
||||
|
||||
.search-box.compact {
|
||||
padding: 0px;
|
||||
/* font size is in px because the XUL it was copied from uses px */
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.textbox-search-clear,
|
||||
.textbox-search-icon {
|
||||
margin-top: 1px;
|
||||
}
|
|
@ -61,8 +61,7 @@
|
|||
height: 13px;
|
||||
}
|
||||
|
||||
#PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge,
|
||||
#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
|
||||
#PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
|
||||
box-shadow: none;
|
||||
filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
|
||||
}
|
||||
|
@ -86,13 +85,7 @@
|
|||
background: #D90000;
|
||||
}
|
||||
|
||||
#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
|
||||
height: 13px;
|
||||
background: transparent url(chrome://browser/skin/warning.svg) no-repeat center;
|
||||
}
|
||||
|
||||
#PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
|
||||
#PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
|
||||
#PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
|
@ -381,9 +374,6 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
|
|||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]),
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-update-status,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-label,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-icon,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > toolbarseparator,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-customize,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-inner > #PanelUI-help:not([panel-multiview-anchor="true"]) {
|
||||
|
@ -481,26 +471,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
#main-window[customizing] #PanelUI-footer-fxa {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa:not([fxastatus="signedin"]) > toolbarseparator,
|
||||
#PanelUI-footer-fxa:not([fxastatus="signedin"]) > #PanelUI-fxa-icon,
|
||||
#PanelUI-footer-fxa:not([fxaprofileimage]) > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status::after {
|
||||
content: url(chrome://browser/skin/warning.svg);
|
||||
filter: drop-shadow(0 1px 0 hsla(206,50%,10%,.15));
|
||||
width: 47px;
|
||||
padding-top: 1px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: 25%;
|
||||
}
|
||||
|
||||
#PanelUI-update-status[update-status]::after {
|
||||
content: "";
|
||||
|
@ -523,40 +493,28 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
background-color: #D90000;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
#PanelUI-footer-inner,
|
||||
#PanelUI-footer-fxa:not([hidden]) {
|
||||
#PanelUI-footer-inner {
|
||||
display: flex;
|
||||
border-top: 1px solid var(--panel-separator-color);
|
||||
}
|
||||
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-footer-inner,
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-footer-fxa {
|
||||
#PanelUI-multiView[viewtype="subview"] #PanelUI-footer-inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#PanelUI-footer-inner > toolbarseparator,
|
||||
#PanelUI-footer-fxa > toolbarseparator {
|
||||
#PanelUI-footer-inner > toolbarseparator {
|
||||
border: 0;
|
||||
border-left: 1px solid var(--panel-separator-color);
|
||||
margin: 7px 0 7px;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-inner:hover > toolbarseparator,
|
||||
#PanelUI-footer-fxa:hover > toolbarseparator {
|
||||
#PanelUI-footer-inner:hover > toolbarseparator {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#PanelUI-update-status,
|
||||
#PanelUI-help,
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon,
|
||||
#PanelUI-customize,
|
||||
#PanelUI-quit {
|
||||
margin: 0;
|
||||
|
@ -590,7 +548,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
}
|
||||
|
||||
#PanelUI-update-status > .toolbarbutton-text,
|
||||
#PanelUI-fxa-label > .toolbarbutton-text,
|
||||
#PanelUI-customize > .toolbarbutton-text {
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
|
@ -598,37 +555,23 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
}
|
||||
|
||||
#PanelUI-help > .toolbarbutton-text,
|
||||
#PanelUI-quit > .toolbarbutton-text,
|
||||
#PanelUI-fxa-avatar > .toolbarbutton-text {
|
||||
#PanelUI-quit > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-update-status > .toolbarbutton-icon,
|
||||
#PanelUI-fxa-label > .toolbarbutton-icon,
|
||||
#PanelUI-fxa-icon > .toolbarbutton-icon,
|
||||
#PanelUI-customize > .toolbarbutton-icon,
|
||||
#PanelUI-help > .toolbarbutton-icon,
|
||||
#PanelUI-quit > .toolbarbutton-icon {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-icon {
|
||||
padding-inline-start: 15px;
|
||||
padding-inline-end: 15px;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-customize {
|
||||
flex: 1;
|
||||
padding-inline-start: 15px;
|
||||
border-inline-start-style: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxaprofileimage="set"] > #PanelUI-fxa-status > #PanelUI-fxa-label,
|
||||
#PanelUI-footer-fxa[fxaprofileimage="enabled"]:not([fxastatus="error"]) > #PanelUI-fxa-status > #PanelUI-fxa-label {
|
||||
padding-inline-start: 0px;
|
||||
}
|
||||
|
||||
#PanelUI-update-status {
|
||||
width: calc(@menuPanelWidth@ + 30px);
|
||||
padding-inline-start: 15px;
|
||||
|
@ -639,130 +582,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
list-style-image: url(chrome://branding/content/icon16.png);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon {
|
||||
list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
|
||||
}
|
||||
|
||||
#PanelUI-remotetabs {
|
||||
--panel-ui-sync-illustration-height: 157.5px;
|
||||
}
|
||||
|
||||
.PanelUI-remotetabs-instruction-title,
|
||||
.PanelUI-remotetabs-instruction-label,
|
||||
#PanelUI-remotetabs-mobile-promo {
|
||||
/* If you change the margin here, the min-height of the synced tabs panel
|
||||
(e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
|
||||
need adjusting (see bug 1248506) */
|
||||
margin: 15px;
|
||||
text-align: center;
|
||||
text-shadow: none;
|
||||
max-width: 15em;
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
.PanelUI-remotetabs-instruction-title {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
/* The boxes with "instructions" get extra top and bottom padding for space
|
||||
around the illustration and buttons */
|
||||
.PanelUI-remotetabs-instruction-box {
|
||||
/* If you change the padding here, the min-height of the synced tabs panel
|
||||
(e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
|
||||
need adjusting (see bug 1248506) */
|
||||
padding-bottom: 30px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.PanelUI-remotetabs-prefs-button {
|
||||
-moz-appearance: none;
|
||||
background-color: #0096dd;
|
||||
/* !important for the color as an OSX specific rule when a lightweight theme
|
||||
is used for buttons in the toolbox overrides. See bug 1238531 for details */
|
||||
color: white !important;
|
||||
border-radius: 2px;
|
||||
/* If you change the margin or padding below, the min-height of the synced tabs
|
||||
panel (e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync,
|
||||
etc) may need adjusting (see bug 1248506) */
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
text-shadow: none;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.PanelUI-remotetabs-prefs-button:hover,
|
||||
.PanelUI-remotetabs-prefs-button:hover:active {
|
||||
background-color: #018acb;
|
||||
}
|
||||
|
||||
.remotetabs-promo-link {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.PanelUI-remotetabs-notabsforclient-label {
|
||||
color: GrayText;
|
||||
/* This margin is to line this label up with the labels in toolbarbuttons. */
|
||||
margin-left: 28px;
|
||||
}
|
||||
|
||||
.fxaSyncIllustration {
|
||||
height: var(--panel-ui-sync-illustration-height);
|
||||
list-style-image: url(chrome://browser/skin/fxa/sync-illustration.svg);
|
||||
}
|
||||
|
||||
.PanelUI-remotetabs-prefs-button > .toolbarbutton-text {
|
||||
/* !important to override ".cui-widget-panel toolbarbutton > .toolbarbutton-text" above. */
|
||||
text-align: center !important;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
#PanelUI-remotetabs[mainview] { /* panel anchored to toolbar button might be too skinny */
|
||||
min-width: 19em;
|
||||
}
|
||||
|
||||
/* Work around bug 1224412 - these boxes will cause scrollbars to appear when
|
||||
the panel is anchored to a toolbar button.
|
||||
*/
|
||||
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync,
|
||||
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-reauthsync,
|
||||
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-nodevicespane,
|
||||
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-tabsdisabledpane {
|
||||
min-height: calc(var(--panel-ui-sync-illustration-height) +
|
||||
20px + /* margin of .PanelUI-remotetabs-prefs-button */
|
||||
16px + /* padding of .PanelUI-remotetabs-prefs-button */
|
||||
30px + /* margin of .PanelUI-remotetabs-instruction-label */
|
||||
30px + 15px + /* padding of .PanelUI-remotetabs-instruction-box */
|
||||
11em);
|
||||
}
|
||||
|
||||
#PanelUI-remotetabs-tabslist > label[itemtype="client"] {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
/* Collapse the non-active vboxes in the remotetabs deck to use only the
|
||||
height the active box needs */
|
||||
#PanelUI-remotetabs-deck:not([selectedIndex="1"]) > #PanelUI-remotetabs-tabsdisabledpane,
|
||||
#PanelUI-remotetabs-deck:not([selectedIndex="2"]) > #PanelUI-remotetabs-fetching,
|
||||
#PanelUI-remotetabs-deck:not([selectedIndex="3"]) > #PanelUI-remotetabs-nodevicespane {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#PanelUI-remotetabs-main[devices-status="single"] > #PanelUI-remotetabs-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-icon[syncstatus="active"]:not([disabled]) {
|
||||
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar.png);
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="migrate-signup"] > #PanelUI-fxa-status > #PanelUI-fxa-label,
|
||||
#PanelUI-footer-fxa[fxastatus="migrate-verify"] > #PanelUI-fxa-status > #PanelUI-fxa-label {
|
||||
list-style-image: url(chrome://browser/skin/warning.svg);
|
||||
-moz-image-region: auto;
|
||||
}
|
||||
|
||||
#PanelUI-customize {
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-customize.png);
|
||||
}
|
||||
|
@ -780,46 +599,12 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
list-style-image: url(chrome://browser/skin/menuPanel-exit.png);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon,
|
||||
#PanelUI-customize,
|
||||
#PanelUI-help,
|
||||
#PanelUI-quit {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="signedin"] > #PanelUI-fxa-status > #PanelUI-fxa-label > .toolbarbutton-icon,
|
||||
#PanelUI-footer-fxa[fxastatus="error"][fxaprofileimage="set"] > #PanelUI-fxa-status > #PanelUI-fxa-label > .toolbarbutton-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"]:not([fxaprofileimage="set"]) > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status[disabled],
|
||||
#PanelUI-fxa-icon[disabled] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 0;
|
||||
background-size: contain;
|
||||
align-self: center;
|
||||
margin: 0px 7px;
|
||||
padding: 0px;
|
||||
border: 0px none;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxaprofileimage="enabled"] > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
|
||||
list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
|
||||
}
|
||||
|
||||
#PanelUI-customize:hover,
|
||||
#PanelUI-help:not([disabled]):hover,
|
||||
#PanelUI-quit:not([disabled]):hover {
|
||||
|
@ -837,16 +622,10 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
}
|
||||
|
||||
#PanelUI-help[disabled],
|
||||
#PanelUI-quit[disabled],
|
||||
#PanelUI-fxa-icon[disabled],
|
||||
#PanelUI-fxa-avatar[disabled],
|
||||
#PanelUI-fxa-label[disabled] > .toolbarbutton-icon,
|
||||
#PanelUI-fxa-status::after {
|
||||
#PanelUI-quit[disabled] {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status:not([disabled]):hover,
|
||||
#PanelUI-fxa-icon:not([disabled]):hover,
|
||||
#PanelUI-help:not([disabled]):hover,
|
||||
#PanelUI-customize:hover,
|
||||
#PanelUI-quit:not([disabled]):hover {
|
||||
|
@ -854,8 +633,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
background-color: var(--arrowpanel-dimmed);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status:not([disabled]):hover:active,
|
||||
#PanelUI-fxa-icon:not([disabled]):hover:active,
|
||||
#PanelUI-help:not([disabled]):hover:active,
|
||||
#PanelUI-customize:hover:active,
|
||||
#PanelUI-quit:not([disabled]):hover:active {
|
||||
|
@ -864,27 +641,6 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
|||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
#PanelUI-fxa-status:not([disabled]):hover,
|
||||
#PanelUI-fxa-status:not([disabled]):hover:active,
|
||||
#PanelUI-fxa-icon:not([disabled]):hover,
|
||||
#PanelUI-fxa-icon:not([disabled]):hover:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] {
|
||||
background-color: hsl(42,94%,88%);
|
||||
border-top: 1px solid hsl(42,94%,70%);
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover {
|
||||
background-color: hsl(42,94%,85%);
|
||||
}
|
||||
|
||||
#PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover:active {
|
||||
background-color: hsl(42,94%,82%);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
#PanelUI-update-status {
|
||||
color: black;
|
||||
}
|
||||
|
@ -1150,19 +906,16 @@ menuitem.panel-subview-footer@menuStateActive@,
|
|||
color: GrayText;
|
||||
}
|
||||
|
||||
#PanelUI-remotetabs-tabslist > toolbarbutton,
|
||||
#PanelUI-historyItems > toolbarbutton {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
#PanelUI-remotetabs-tabslist > toolbarbutton,
|
||||
#PanelUI-historyItems > toolbarbutton {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
#PanelUI-remotetabs-tabslist > toolbarbutton > .toolbarbutton-icon,
|
||||
#PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
|
||||
#PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
|
||||
#PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
|
||||
|
@ -1616,15 +1369,6 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
|
|||
list-style-image: url(chrome://branding/content/icon32.png);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon {
|
||||
list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-icon[syncstatus="active"]:not([disabled]) {
|
||||
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
|
||||
}
|
||||
|
||||
#PanelUI-customize {
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-customize@2x.png);
|
||||
}
|
||||
|
@ -1641,8 +1385,6 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
|
|||
list-style-image: url(chrome://browser/skin/menuPanel-exit@2x.png);
|
||||
}
|
||||
|
||||
#PanelUI-fxa-label,
|
||||
#PanelUI-fxa-icon,
|
||||
#PanelUI-customize,
|
||||
#PanelUI-help,
|
||||
#PanelUI-quit {
|
||||
|
@ -1650,8 +1392,6 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
|
|||
}
|
||||
|
||||
#PanelUI-update-status > .toolbarbutton-icon,
|
||||
#PanelUI-fxa-label > .toolbarbutton-icon,
|
||||
#PanelUI-fxa-icon > .toolbarbutton-icon,
|
||||
#PanelUI-customize > .toolbarbutton-icon,
|
||||
#PanelUI-help > .toolbarbutton-icon,
|
||||
#PanelUI-quit > .toolbarbutton-icon {
|
||||
|
|
|
@ -1,234 +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/.
|
||||
|
||||
/* These styles are intended to mimic XUL trees and the XUL search box. */
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font: message-box;
|
||||
color: #333333;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
/* The content-container holds the non-scrollable header and the scrollable
|
||||
content area.
|
||||
*/
|
||||
.content-container {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* The content header is not scrollable */
|
||||
.content-header {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
/* The main content area is scrollable and fills the rest of the area */
|
||||
.content-scrollable {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.emptyListInfo {
|
||||
cursor: default;
|
||||
padding: 3em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.list,
|
||||
.item-tabs-list {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.item.client {
|
||||
opacity: 1;
|
||||
max-height: unset;
|
||||
display: unset;
|
||||
}
|
||||
|
||||
.item.client.closed .item-tabs-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: inline-block;
|
||||
opacity: 1;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
color: -moz-FieldText;
|
||||
}
|
||||
|
||||
.item.selected > .item-title-container {
|
||||
background-color: -moz-cellhighlight;
|
||||
color: -moz-cellhighlighttext;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item.selected:focus > .item-title-container {
|
||||
background-color: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
.client .item.tab > .item-title-container {
|
||||
padding-inline-start: 35px;
|
||||
}
|
||||
|
||||
.item.tab > .item-title-container {
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
|
||||
.item.client.device-image-desktop > .item-title-container > .item-icon-container {
|
||||
background-image: url("chrome://browser/skin/sync-desktopIcon.svg#icon");
|
||||
}
|
||||
|
||||
.item.client.device-image-desktop.selected:focus > .item-title-container > .item-icon-container {
|
||||
background-image: url("chrome://browser/skin/sync-desktopIcon.svg#icon-inverted");
|
||||
}
|
||||
|
||||
.item.client.device-image-mobile > .item-title-container > .item-icon-container {
|
||||
background-image: url("chrome://browser/skin/sync-mobileIcon.svg#icon");
|
||||
}
|
||||
|
||||
.item.client.device-image-mobile.selected:focus > .item-title-container > .item-icon-container {
|
||||
background-image: url("chrome://browser/skin/sync-mobileIcon.svg#icon-inverted");
|
||||
}
|
||||
|
||||
.item.tab > .item-title-container > .item-icon-container {
|
||||
background-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.item.tab > .item-title-container > .item-icon-container {
|
||||
background-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
.item-icon-container {
|
||||
min-width: 16px;
|
||||
max-width: 16px;
|
||||
min-height: 16px;
|
||||
max-height: 16px;
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
background-size: 16px 16px;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.item-title-container {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
padding: 1px 0px 1px 0px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0px;
|
||||
line-height: 1.3;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.item[hidden] {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
transition: opacity 150ms ease-in-out, max-height 150ms ease-in-out 150ms;
|
||||
}
|
||||
|
||||
.item.empty .item-title-container {
|
||||
color: #aeaeae;
|
||||
}
|
||||
|
||||
.client .item.empty > .item-title-container {
|
||||
padding-inline-start: 35px;
|
||||
}
|
||||
|
||||
.text-input-box {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
.textbox-input-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tabsFilter {
|
||||
flex: 1;
|
||||
/* min-width of anything to override the implicit "-moz-min-content" value.
|
||||
0px is safe as the sidebar itself has a constrained size meaning we will
|
||||
never actually hit this minimum
|
||||
*/
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.sync-state > p {
|
||||
padding-inline-end: 10px;
|
||||
padding-inline-start: 10px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.text-link {
|
||||
color: rgb(0, 149, 221);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.text-link,
|
||||
.text-link:focus {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.deck .sync-state {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity 1.5s;
|
||||
border-top: 1px solid #bdbdbd;
|
||||
}
|
||||
|
||||
.deck .sync-state.tabs-container {
|
||||
border-top: 0px;
|
||||
}
|
||||
|
||||
.deck .sync-state.selected {
|
||||
display: unset;
|
||||
opacity: 100;
|
||||
}
|
||||
|
||||
.sidebar-search-container.tabs-container:not(.selected) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.textbox-search-clear:not([disabled]) {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.textbox-search-icons .textbox-search-clear,
|
||||
.filtered .textbox-search-icons .textbox-search-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filtered .textbox-search-icons .textbox-search-clear {
|
||||
display: block;
|
||||
}
|
|
@ -8,7 +8,6 @@ browser.jar:
|
|||
skin/classic/browser/sanitizeDialog.css
|
||||
skin/classic/browser/aboutSessionRestore-window-icon.png
|
||||
skin/classic/browser/aboutSyncTabs.css
|
||||
* skin/classic/browser/syncedtabs/sidebar.css (syncedtabs/sidebar.css)
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
skin/classic/browser/actionicon-tab@2x.png
|
||||
skin/classic/browser/actionicon-tab-win7.png
|
||||
|
|
|
@ -1,132 +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/. */
|
||||
|
||||
%include ../../shared/syncedtabs/sidebar.inc.css
|
||||
|
||||
/* These styles are intended to mimic XUL trees and the XUL search box. */
|
||||
|
||||
html {
|
||||
background-color: #EEF3FA;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
margin: 1px 0 0;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
margin-inline-end: 6px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
-moz-appearance: textfield;
|
||||
cursor: text;
|
||||
margin: 2px 4px;
|
||||
padding: 2px 2px 3px;
|
||||
padding-inline-start: 4px;
|
||||
color: -moz-FieldText;
|
||||
}
|
||||
|
||||
.textbox-search-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(chrome://global/skin/icons/Search-glass.png);
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.textbox-search-icon:-moz-locale-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.textbox-search-icon[searchbutton]:not([disabled]) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.textbox-search-clear {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-image: url(chrome://global/skin/icons/Search-close.png);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.textbox-search-clear:not([disabled]) {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.textbox-search-icon:not([disabled]) {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.textbox-search-clear:not([disabled]):hover ,
|
||||
.textbox-search-icon:not([disabled]):hover {
|
||||
background-position: -16px 0;
|
||||
}
|
||||
|
||||
.textbox-search-clear:not([disabled]):hover:active ,
|
||||
.textbox-search-icon:not([disabled]):hover:active {
|
||||
background-position: -32px 0;
|
||||
}
|
||||
|
||||
.client .item.tab > .item-title-container {
|
||||
padding-inline-start: 26px;
|
||||
}
|
||||
.item.tab > .item-title-container {
|
||||
padding-inline-start: 14px;
|
||||
}
|
||||
|
||||
.item-icon-container {
|
||||
min-width: 16px;
|
||||
max-width: 16px;
|
||||
min-height: 16px;
|
||||
max-height: 16px;
|
||||
margin-right: 5px;
|
||||
background-size: 16px 16px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.item-twisty-container {
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
padding-top: 5px;
|
||||
min-width: 9px; /* The image's width is 9 pixels */
|
||||
height: 9px;
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#open");
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#clsd");
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container:hover {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#open-hover");
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container:hover {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#clsd-hover");
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#open-rtl");
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#clsd-rtl");
|
||||
}
|
||||
|
||||
.item.client .item-twisty-container:hover:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#open-hover-rtl");
|
||||
}
|
||||
|
||||
.item.client.closed .item-twisty-container:hover:dir(rtl) {
|
||||
background-image: url("chrome://global/skin/tree/twisty.svg#clsd-hover-rtl");
|
||||
}
|
Loading…
Reference in New Issue