mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
Merge remote-tracking branch 'origin/master' into custom
This commit is contained in:
commit
838b5e4e48
569 changed files with 1183 additions and 62621 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -283,9 +283,11 @@ body[narrow] #restorePreviousSession {
|
|||
content: url("chrome://browser/content/abouthome/addons.png");
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#sync::before {
|
||||
content: url("chrome://browser/content/abouthome/sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
#settings::before {
|
||||
content: url("chrome://browser/content/abouthome/settings.png");
|
||||
|
|
@ -369,9 +371,11 @@ body[narrow] #restorePreviousSession::before {
|
|||
content: url("chrome://browser/content/abouthome/addons@2x.png");
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#sync::before {
|
||||
content: url("chrome://browser/content/abouthome/sync@2x.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
#settings::before {
|
||||
content: url("chrome://browser/content/abouthome/settings@2x.png");
|
||||
|
|
|
|||
|
|
@ -54,7 +54,9 @@
|
|||
<button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button>
|
||||
<button class="launchButton" id="history">&abouthome.historyButton.label;</button>
|
||||
<button class="launchButton" id="addons">&abouthome.addonsButton.label;</button>
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<button class="launchButton" id="sync">&abouthome.syncButton.label;</button>
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
<button class="launchButton" id="settings">&abouthome.preferencesButtonWin.label;</button>
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -261,13 +261,6 @@
|
|||
accesskey="&savePageCmd.accesskey2;"
|
||||
oncommand="gContextMenu.savePageAs();"/>
|
||||
<menuseparator id="context-sep-sendpagetodevice" hidden="true"/>
|
||||
<menu id="context-sendpagetodevice"
|
||||
label="&sendPageToDevice.label;"
|
||||
accesskey="&sendPageToDevice.accesskey;"
|
||||
hidden="true">
|
||||
<menupopup id="context-sendpagetodevice-popup"
|
||||
onpopupshowing="(() => { let browser = gBrowser || getPanelBrowser(); gFxAccounts.populateSendTabToDevicesMenu(event.target, browser.currentURI.spec, browser.contentTitle); })()"/>
|
||||
</menu>
|
||||
<menuseparator id="context-sep-viewbgimage"/>
|
||||
<menuitem id="context-viewbgimage"
|
||||
label="&viewBGImageCmd.label;"
|
||||
|
|
@ -308,13 +301,6 @@
|
|||
<menuitem id="context-searchselect"
|
||||
oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
|
||||
<menuseparator id="context-sep-sendlinktodevice" hidden="true"/>
|
||||
<menu id="context-sendlinktodevice"
|
||||
label="&sendLinkToDevice.label;"
|
||||
accesskey="&sendLinkToDevice.accesskey;"
|
||||
hidden="true">
|
||||
<menupopup id="context-sendlinktodevice-popup"
|
||||
onpopupshowing="gFxAccounts.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
|
||||
</menu>
|
||||
<menuseparator id="frame-sep"/>
|
||||
<menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
|
||||
<menupopup>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
#endif
|
||||
<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
|
||||
%aboutHomeDTD;
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
|
||||
%syncBrandDTD;
|
||||
#endif
|
||||
]>
|
||||
|
||||
|
|
|
|||
|
|
@ -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/>
|
||||
|
|
@ -324,11 +321,13 @@
|
|||
key="key_sanitize"
|
||||
command="Tools:Sanitize"/>
|
||||
<menuseparator id="sanitizeSeparator"/>
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<menuitem id="sync-tabs-menuitem"
|
||||
class="syncTabsMenuItem"
|
||||
label="&syncTabsMenu3.label;"
|
||||
label="&syncTabsMenu2.label;"
|
||||
oncommand="BrowserOpenSyncTabs();"
|
||||
hidden="true"/>
|
||||
disabled="true"/>
|
||||
#endif
|
||||
<menuitem id="historyRestoreLastSession"
|
||||
label="&historyRestoreLastSession.label;"
|
||||
command="Browser:RestoreLastSession"/>
|
||||
|
|
@ -447,11 +446,10 @@
|
|||
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());"
|
||||
>
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
onpopupshowing="gSyncUI.updateUI();"
|
||||
#endif
|
||||
>
|
||||
<menuitem id="menu_openDownloads"
|
||||
label="&downloads.label;"
|
||||
accesskey="&downloads.accesskey;"
|
||||
|
|
@ -462,23 +460,19 @@
|
|||
accesskey="&addons.accesskey;"
|
||||
key="key_openAddons"
|
||||
command="Tools:Addons"/>
|
||||
|
||||
<!-- only one of sync-setup, sync-syncnowitem or sync-reauthitem will be showing at once -->
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<!-- only one of sync-setup or sync-menu will be showing at once -->
|
||||
<menuitem id="sync-setup"
|
||||
label="&syncSignIn.label;"
|
||||
accesskey="&syncSignIn.accesskey;"
|
||||
label="&syncSetup.label;"
|
||||
accesskey="&syncSetup.accesskey;"
|
||||
observes="sync-setup-state"
|
||||
oncommand="gSyncUI.openSetup(null, 'menubar')"/>
|
||||
oncommand="gSyncUI.openSetup()"/>
|
||||
<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');"/>
|
||||
#endif
|
||||
<menuseparator id="devToolsSeparator"/>
|
||||
<menu id="webDeveloperMenu"
|
||||
label="&webDeveloperMenu.label;"
|
||||
|
|
|
|||
|
|
@ -804,18 +804,29 @@ HistoryMenu.prototype = {
|
|||
},
|
||||
|
||||
toggleTabsFromOtherComputers: function PHM_toggleTabsFromOtherComputers() {
|
||||
// This is a no-op if MOZ_SERVICES_SYNC isn't defined
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
// Enable/disable the Tabs From Other Computers menu. Some of the menus handled
|
||||
// by HistoryMenu do not have this menuitem.
|
||||
let menuitem = this._rootElt.getElementsByClassName("syncTabsMenuItem")[0];
|
||||
if (!menuitem)
|
||||
return;
|
||||
|
||||
if (!PlacesUIUtils.shouldShowTabsFromOtherComputersMenuitem()) {
|
||||
// If Sync isn't configured yet, then don't show the menuitem.
|
||||
if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
|
||||
Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
|
||||
menuitem.setAttribute("hidden", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// The tabs engine might never be inited (if services.sync.registerEngines
|
||||
// is modified), so make sure we avoid undefined errors.
|
||||
let enabled = Weave.Service.isLoggedIn &&
|
||||
Weave.Service.engineManager.get("tabs") &&
|
||||
Weave.Service.engineManager.get("tabs").enabled;
|
||||
menuitem.setAttribute("disabled", !enabled);
|
||||
menuitem.setAttribute("hidden", false);
|
||||
#endif
|
||||
},
|
||||
|
||||
_onPopupShowing: function HM__onPopupShowing(aEvent) {
|
||||
|
|
|
|||
|
|
@ -163,16 +163,13 @@
|
|||
<!-- A broadcaster of a number of attributes suitable for "sync now" UI -
|
||||
A 'syncstatus' attribute is set while actively syncing, and the label
|
||||
attribute which changes from "sync now" to "syncing" etc. -->
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<broadcaster id="sync-status"/>
|
||||
<!-- broadcasters of the "hidden" attribute to reflect setup state for
|
||||
menus -->
|
||||
<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');"/>
|
||||
#endif
|
||||
<broadcaster id="workOfflineMenuitemState"/>
|
||||
|
||||
<broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
|
||||
|
|
|
|||
|
|
@ -1,63 +1,37 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
# This 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");
|
||||
|
||||
#ifdef MOZ_SERVICES_CLOUDSYNC
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
|
||||
"resource://gre/modules/CloudSync.jsm");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
const MIN_STATUS_ANIMATION_DURATION = 1600;
|
||||
|
||||
// gSyncUI handles updating the tools menu and displaying notifications.
|
||||
// gSyncUI handles updating the tools menu
|
||||
var gSyncUI = {
|
||||
_obs: ["weave:service:sync:start",
|
||||
"weave:service:sync:finish",
|
||||
"weave:service:sync:error",
|
||||
"weave:service:sync:delayed",
|
||||
"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");
|
||||
|
||||
init: function SUI_init() {
|
||||
// Proceed to set up the UI if Sync has already started up.
|
||||
// Otherwise we'll do it when Sync is firing up.
|
||||
if (this.weaveService.ready) {
|
||||
let xps = Components.classes["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
if (xps.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.
|
||||
|
|
@ -65,7 +39,6 @@ var gSyncUI = {
|
|||
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) {
|
||||
|
|
@ -85,161 +58,130 @@ var gSyncUI = {
|
|||
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();
|
||||
|
||||
if (gBrowser && Weave.Notifications.notifications.length) {
|
||||
this.initNotifications();
|
||||
}
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
initNotifications: function SUI_initNotifications() {
|
||||
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
let notificationbox = document.createElementNS(XULNS, "notificationbox");
|
||||
notificationbox.id = "sync-notifications";
|
||||
notificationbox.setAttribute("flex", "1");
|
||||
|
||||
// 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 bottombox = document.getElementById("browser-bottombox");
|
||||
bottombox.insertBefore(notificationbox, bottombox.firstChild);
|
||||
|
||||
// Force a style flush to ensure that our binding is attached.
|
||||
notificationbox.clientTop;
|
||||
|
||||
// notificationbox will listen to observers from now on.
|
||||
Services.obs.removeObserver(this, "weave:notification:added");
|
||||
},
|
||||
|
||||
_wasDelayed: false,
|
||||
|
||||
_needsSetup: function SUI__needsSetup() {
|
||||
let firstSync = "";
|
||||
try {
|
||||
firstSync = Services.prefs.getCharPref("services.sync.firstSync");
|
||||
} catch (e) { }
|
||||
|
||||
return Promise.resolve(Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
|
||||
firstSync == "notReady");
|
||||
return 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;
|
||||
});
|
||||
}
|
||||
updateUI: function SUI_updateUI() {
|
||||
let needsSetup = this._needsSetup();
|
||||
document.getElementById("sync-setup-state").hidden = !needsSetup;
|
||||
document.getElementById("sync-syncnow-state").hidden = needsSetup;
|
||||
|
||||
// Otherwise we are configured for legacy Sync, which has no verification
|
||||
// concept.
|
||||
return Promise.resolve(false);
|
||||
if (!gBrowser)
|
||||
return;
|
||||
|
||||
let button = document.getElementById("sync-button");
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
button.removeAttribute("status");
|
||||
this._updateLastSyncTime();
|
||||
if (needsSetup)
|
||||
button.removeAttribute("tooltiptext");
|
||||
},
|
||||
|
||||
// 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;
|
||||
|
||||
#ifdef MOZ_SERVICES_CLOUDSYNC
|
||||
if (CloudSync && CloudSync.ready && CloudSync().adapters.count) {
|
||||
document.getElementById("sync-syncnow-state").hidden = false;
|
||||
} else if (loginFailed) {
|
||||
#else
|
||||
if (loginFailed) {
|
||||
#endif
|
||||
// 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() {
|
||||
onActivityStart: function SUI_onActivityStart() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
|
||||
this.log.debug("onActivityStart");
|
||||
let button = document.getElementById("sync-button");
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
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();
|
||||
button.setAttribute("status", "active");
|
||||
},
|
||||
|
||||
_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();
|
||||
onSyncDelay: function SUI_onSyncDelay() {
|
||||
// basically, we want to just inform users that stuff is going to take a while
|
||||
let title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
|
||||
let description = this._stringBundle.GetStringFromName("error.sync.no_node_found");
|
||||
let buttons = [new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
|
||||
function() { gSyncUI.openServerStatus(); return true; }
|
||||
)];
|
||||
let notification = new Weave.Notification(
|
||||
title, description, null, Weave.Notifications.PRIORITY_INFO, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
this._wasDelayed = true;
|
||||
},
|
||||
|
||||
onActivityStop() {
|
||||
if (!gBrowser)
|
||||
return;
|
||||
this.log.debug("onActivityStop");
|
||||
onLoginFinish: function SUI_onLoginFinish() {
|
||||
// Clear out any login failure notifications
|
||||
let title = this._stringBundle.GetStringFromName("error.login.title");
|
||||
this.clearError(title);
|
||||
},
|
||||
|
||||
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();
|
||||
}
|
||||
onSetupComplete: function SUI_onSetupComplete() {
|
||||
this.onLoginFinish();
|
||||
},
|
||||
|
||||
onLoginError: function SUI_onLoginError() {
|
||||
this.log.debug("onLoginError: login=${login}, sync=${sync}", Weave.Status);
|
||||
// if login fails, any other notifications are essentially moot
|
||||
Weave.Notifications.removeAll();
|
||||
|
||||
// We don't show any login errors here; browser-fxaccounts shows them in
|
||||
// the hamburger menu.
|
||||
// if we haven't set up the client, don't show errors
|
||||
if (this._needsSetup()) {
|
||||
this.updateUI();
|
||||
return;
|
||||
}
|
||||
|
||||
let title = this._stringBundle.GetStringFromName("error.login.title");
|
||||
|
||||
let description;
|
||||
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
|
||||
// Convert to days
|
||||
let lastSync =
|
||||
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
|
||||
description =
|
||||
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
|
||||
} else {
|
||||
let reason = Weave.Utils.getErrorString(Weave.Status.login);
|
||||
description =
|
||||
this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
|
||||
}
|
||||
|
||||
let buttons = [];
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.login.prefs.label"),
|
||||
this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
|
||||
function() { gSyncUI.openPrefs(); return true; }
|
||||
));
|
||||
|
||||
let notification = new Weave.Notification(title, description, null,
|
||||
Weave.Notifications.PRIORITY_WARNING, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
|
|
@ -247,6 +189,10 @@ var gSyncUI = {
|
|||
this.updateUI();
|
||||
},
|
||||
|
||||
onStartOver: function SUI_onStartOver() {
|
||||
this.clearError();
|
||||
},
|
||||
|
||||
onQuotaNotice: function onQuotaNotice(subject, data) {
|
||||
let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
|
||||
let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
|
||||
|
|
@ -262,40 +208,26 @@ var gSyncUI = {
|
|||
Weave.Notifications.replaceTitle(notification);
|
||||
},
|
||||
|
||||
_getAppName: function () {
|
||||
let brand = new StringBundle("chrome://branding/locale/brand.properties");
|
||||
return brand.get("brandShortName");
|
||||
openServerStatus: function () {
|
||||
let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
|
||||
window.openUILinkIn(statusURL, "tab");
|
||||
},
|
||||
|
||||
// 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);
|
||||
});
|
||||
doSync: function SUI_doSync() {
|
||||
setTimeout(function() Weave.Service.errorHandler.syncAndReportErrors(), 0);
|
||||
},
|
||||
|
||||
// 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);
|
||||
});
|
||||
handleToolbarButton: function SUI_handleStatusbarButton() {
|
||||
if (this._needsSetup())
|
||||
this.openSetup();
|
||||
else
|
||||
this.doSync();
|
||||
},
|
||||
|
||||
//XXXzpao should be part of syncCommon.js - which we might want to make a module...
|
||||
// To be fixed in a followup (bug 583366)
|
||||
|
||||
/**
|
||||
* Invoke the Sync setup wizard.
|
||||
*
|
||||
|
|
@ -304,11 +236,9 @@ var gSyncUI = {
|
|||
* 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") {
|
||||
openSetup: function SUI_openSetup(wizardType) {
|
||||
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
|
||||
if (win)
|
||||
win.focus();
|
||||
|
|
@ -319,7 +249,6 @@ var gSyncUI = {
|
|||
}
|
||||
},
|
||||
|
||||
// Open the legacy-sync device pairing UI. Note used for FxA Sync.
|
||||
openAddDevice: function () {
|
||||
if (!Weave.Utils.ensureMPUnlocked())
|
||||
return;
|
||||
|
|
@ -332,202 +261,186 @@ var gSyncUI = {
|
|||
"syncAddDevice", "centerscreen,chrome,resizable=no");
|
||||
},
|
||||
|
||||
openPrefs: function (entryPoint) {
|
||||
openPreferences("paneSync", { urlParams: { entrypoint: entryPoint } });
|
||||
openQuotaDialog: function SUI_openQuotaDialog() {
|
||||
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
|
||||
if (win)
|
||||
win.focus();
|
||||
else
|
||||
Services.ww.activeWindow.openDialog(
|
||||
"chrome://browser/content/sync/quota.xul", "",
|
||||
"centerscreen,chrome,dialog,modal");
|
||||
},
|
||||
|
||||
openSignInAgainPage: function (entryPoint = "syncbutton") {
|
||||
gFxAccounts.openSignInAgainPage(entryPoint);
|
||||
openPrefs: function SUI_openPrefs() {
|
||||
openPreferences("paneSync");
|
||||
},
|
||||
|
||||
/* 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* () {
|
||||
// Helpers
|
||||
_updateLastSyncTime: function SUI__updateLastSyncTime() {
|
||||
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)
|
||||
let syncButton = document.getElementById("sync-button");
|
||||
if (!syncButton)
|
||||
return;
|
||||
|
||||
let broadcaster = document.getElementById("sync-status");
|
||||
if (broadcaster) {
|
||||
if (tooltiptext) {
|
||||
broadcaster.setAttribute("tooltiptext", tooltiptext);
|
||||
} else {
|
||||
broadcaster.removeAttribute("tooltiptext");
|
||||
}
|
||||
let lastSync;
|
||||
try {
|
||||
lastSync = Services.prefs.getCharPref("services.sync.lastSync");
|
||||
}
|
||||
catch (e) { };
|
||||
if (!lastSync || this._needsSetup()) {
|
||||
syncButton.removeAttribute("tooltiptext");
|
||||
return;
|
||||
}
|
||||
}),
|
||||
|
||||
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);
|
||||
// Show the day-of-week and time (HH:MM) of last sync
|
||||
let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
|
||||
let lastSyncLabel =
|
||||
this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
|
||||
|
||||
syncButton.setAttribute("tooltiptext", lastSyncLabel);
|
||||
},
|
||||
|
||||
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");
|
||||
}
|
||||
clearError: function SUI_clearError(errorString) {
|
||||
Weave.Notifications.removeAll(errorString);
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
onSyncFinish: function SUI_onSyncFinish() {
|
||||
let title = this._stringBundle.GetStringFromName("error.sync.title");
|
||||
|
||||
// Clear out sync failures on a successful sync
|
||||
this.clearError(title);
|
||||
|
||||
if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
|
||||
title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
|
||||
this.clearError(title);
|
||||
this._wasDelayed = false;
|
||||
}
|
||||
},
|
||||
|
||||
onSyncError: function SUI_onSyncError() {
|
||||
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 =
|
||||
|
||||
if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
|
||||
this.onLoginError();
|
||||
return;
|
||||
}
|
||||
|
||||
let description;
|
||||
if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
|
||||
// Convert to days
|
||||
let lastSync =
|
||||
Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
|
||||
description =
|
||||
this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
|
||||
} else {
|
||||
let error = Weave.Utils.getErrorString(Weave.Status.sync);
|
||||
description =
|
||||
this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
|
||||
}
|
||||
let priority = Weave.Notifications.PRIORITY_WARNING;
|
||||
let buttons = [];
|
||||
|
||||
if (Weave.Status.sync == Weave.OVER_QUOTA) {
|
||||
description = this._stringBundle.GetStringFromName("error.sync.quota.description");
|
||||
// Check if the client is outdated in some way
|
||||
let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
|
||||
for (let [engine, reason] in Iterator(Weave.Status.engines))
|
||||
outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
|
||||
|
||||
if (outdated) {
|
||||
description = this._stringBundle.GetStringFromName(
|
||||
"error.sync.needUpdate.description");
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
|
||||
this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
|
||||
function() {
|
||||
window.openUILinkIn(Services.prefs.getCharPref("services.sync.outdated.url"), "tab");
|
||||
return true;
|
||||
}
|
||||
));
|
||||
}
|
||||
else if (Weave.Status.sync == Weave.OVER_QUOTA) {
|
||||
description = this._stringBundle.GetStringFromName(
|
||||
"error.sync.quota.description");
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName(
|
||||
"error.sync.viewQuotaButton.label"),
|
||||
this._stringBundle.GetStringFromName(
|
||||
"error.sync.viewQuotaButton.accesskey"),
|
||||
function() { gSyncUI.openQuotaDialog(); return true; } )
|
||||
);
|
||||
// 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);
|
||||
}
|
||||
else if (Weave.Status.enforceBackoff) {
|
||||
priority = Weave.Notifications.PRIORITY_INFO;
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
|
||||
function() { gSyncUI.openServerStatus(); return true; }
|
||||
));
|
||||
}
|
||||
else {
|
||||
priority = Weave.Notifications.PRIORITY_INFO;
|
||||
buttons.push(new Weave.NotificationButton(
|
||||
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
|
||||
this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
|
||||
function() { gSyncUI.doSync(); return true; }
|
||||
));
|
||||
}
|
||||
|
||||
let notification =
|
||||
new Weave.Notification(title, description, null, priority, buttons);
|
||||
Weave.Notifications.replaceTitle(notification);
|
||||
|
||||
if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
|
||||
title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
|
||||
Weave.Notifications.removeAll(title);
|
||||
this._wasDelayed = false;
|
||||
}
|
||||
|
||||
this.updateUI();
|
||||
},
|
||||
|
||||
|
||||
observe: function SUI_observe(subject, topic, data) {
|
||||
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.
|
||||
this.onSyncFinish();
|
||||
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();
|
||||
case "weave:service:sync:delayed":
|
||||
this.onSyncDelay();
|
||||
break;
|
||||
case "weave:service:quota:remaining":
|
||||
this.onQuotaNotice();
|
||||
break;
|
||||
case "weave:service:setup-complete":
|
||||
this.onSetupComplete();
|
||||
break;
|
||||
case "weave:service:login:start":
|
||||
this.onActivityStart();
|
||||
break;
|
||||
case "weave:service:login:finish":
|
||||
this.onLoginFinish();
|
||||
break;
|
||||
case "weave:ui:login:error":
|
||||
case "weave:service:login:error":
|
||||
this.onLoginError();
|
||||
break;
|
||||
case "weave:service:logout:finish":
|
||||
this.onLogout();
|
||||
break;
|
||||
case "weave:service:start-over:finish":
|
||||
this.updateUI();
|
||||
case "weave:service:start-over":
|
||||
this.onStartOver();
|
||||
break;
|
||||
case "weave:service:ready":
|
||||
this.initUI();
|
||||
|
|
@ -535,16 +448,8 @@ var gSyncUI = {
|
|||
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);
|
||||
case "weave:ui:clear-error":
|
||||
this.clearError();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
@ -556,19 +461,10 @@ var gSyncUI = {
|
|||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
|
||||
// XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
|
||||
//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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -687,6 +687,18 @@ window[chromehidden~="toolbar"] toolbar:not(#nav-bar):not(#TabsToolbar):not(#pri
|
|||
min-width: 1px;
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
/* Sync notification UI */
|
||||
#sync-notifications {
|
||||
-moz-binding: url("chrome://browser/content/sync/notification.xml#notificationbox");
|
||||
overflow-y: visible !important;
|
||||
}
|
||||
|
||||
#sync-notifications notification {
|
||||
-moz-binding: url("chrome://browser/content/sync/notification.xml#notification");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* History Swipe Animation */
|
||||
|
||||
#historySwipeAnimationContainer {
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
|
|
@ -109,6 +108,12 @@ XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
|
|||
return new tmp.PageMenuParent();
|
||||
});
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
|
||||
"resource://services-sync/main.js");
|
||||
#endif
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
|
||||
|
|
@ -214,6 +219,10 @@ var gInitialPages = [
|
|||
"about:logopage"
|
||||
];
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include browser-syncui.js
|
||||
#endif
|
||||
|
||||
function* browserWindows() {
|
||||
let windows = Services.wm.getEnumerator("navigator:browser");
|
||||
while (windows.hasMoreElements())
|
||||
|
|
@ -1343,13 +1352,14 @@ var gBrowserInit = {
|
|||
FullScreen.init();
|
||||
PointerLock.init();
|
||||
|
||||
// initialize the sync UI
|
||||
gSyncUI.init();
|
||||
gFxAccounts.init();
|
||||
|
||||
if (AppConstants.MOZ_DATA_REPORTING)
|
||||
gDataNotificationInfoBar.init();
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
// initialize the sync UI
|
||||
gSyncUI.init();
|
||||
#endif
|
||||
|
||||
gBrowserThumbnails.init();
|
||||
|
||||
gMenuButtonBadgeManager.init();
|
||||
|
|
@ -1483,8 +1493,6 @@ var gBrowserInit = {
|
|||
|
||||
FullScreen.uninit();
|
||||
|
||||
gFxAccounts.uninit();
|
||||
|
||||
Services.obs.removeObserver(gPluginHandler.NPAPIPluginCrashed, "plugin-crashed");
|
||||
|
||||
try {
|
||||
|
|
@ -1645,8 +1653,10 @@ if (AppConstants.platform == "macosx") {
|
|||
// initialize the private browsing UI
|
||||
gPrivateBrowsingUI.init();
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
// initialize the sync UI
|
||||
gSyncUI.init();
|
||||
#endif
|
||||
};
|
||||
|
||||
gBrowserInit.nonBrowserWindowShutdown = function() {
|
||||
|
|
@ -3222,12 +3232,14 @@ var PrintPreviewListener = {
|
|||
this._chromeState.globalNotificationsOpen = !globalNotificationBox.notificationsHidden;
|
||||
globalNotificationBox.notificationsHidden = true;
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
this._chromeState.syncNotificationsOpen = false;
|
||||
var syncNotifications = document.getElementById("sync-notifications");
|
||||
if (syncNotifications) {
|
||||
this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
|
||||
syncNotifications.notificationsHidden = true;
|
||||
}
|
||||
#endif
|
||||
},
|
||||
_showChrome: function () {
|
||||
if (this._chromeState.notificationsOpen)
|
||||
|
|
@ -3239,8 +3251,10 @@ var PrintPreviewListener = {
|
|||
if (this._chromeState.globalNotificationsOpen)
|
||||
document.getElementById("global-notificationbox").notificationsHidden = false;
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
if (this._chromeState.syncNotificationsOpen)
|
||||
document.getElementById("sync-notifications").notificationsHidden = false;
|
||||
#endif
|
||||
|
||||
if (this._chromeState.sidebarOpen)
|
||||
SidebarUI.show(this._sidebarCommand);
|
||||
|
|
@ -6359,9 +6373,14 @@ function checkEmptyPageOrigin(browser = gBrowser.selectedBrowser,
|
|||
return ssm.isSystemPrincipal(contentPrincipal);
|
||||
}
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
function BrowserOpenSyncTabs() {
|
||||
switchToTabHavingURI("about:sync-tabs", true);
|
||||
if (gSyncUI._needsSetup())
|
||||
gSyncUI.openSetup();
|
||||
else
|
||||
switchToTabHavingURI("about:sync-tabs", true);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Format a URL
|
||||
|
|
@ -7661,8 +7680,6 @@ var TabContextMenu = {
|
|||
|
||||
this.contextTab.addEventListener("TabAttrModified", this, false);
|
||||
aPopupMenu.addEventListener("popuphiding", this, false);
|
||||
|
||||
gFxAccounts.updateTabContextMenu(aPopupMenu);
|
||||
},
|
||||
handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
|
|
|
|||
|
|
@ -101,11 +101,6 @@
|
|||
tbattr="tabbrowser-multiple"
|
||||
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
|
||||
<menuseparator id="context_sendTabToDevice_separator" hidden="true"/>
|
||||
<menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
|
||||
accesskey="&sendTabToDevice.accesskey;" hidden="true">
|
||||
<menupopup id="context_sendTabToDevicePopupMenu"
|
||||
onpopupshowing="gFxAccounts.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab.linkedBrowser.currentURI.spec, TabContextMenu.contextTab.linkedBrowser.contentTitle);"/>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
|
||||
tbattr="tabbrowser-multiple-visible"
|
||||
|
|
@ -391,59 +386,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
|
||||
|
|
@ -972,6 +914,18 @@
|
|||
type="checkbox"
|
||||
label="&fullScreenCmd.label;"
|
||||
tooltip="dynamic-shortcut-tooltip"/>
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<toolbarbutton id="sync-button"
|
||||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
label="&syncToolbarButton.label;"
|
||||
oncommand="gSyncUI.handleToolbarButton();"/>>
|
||||
|
||||
<toolbarbutton id="sync-tabs-button"
|
||||
class="toolbarbutton-1 chromeclass-toolbar-additional"
|
||||
label="&syncTabsToolbarButton.label;"
|
||||
oncommand="BrowserOpenSyncTabs();"/>
|
||||
#endif
|
||||
</toolbarpalette>
|
||||
</toolbox>
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,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"/>
|
||||
|
|
@ -36,4 +35,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,364 +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");
|
||||
|
||||
#ifdef MOZ_SERVICES_CLOUDSYNC
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
|
||||
"resource://gre/modules/CloudSync.jsm");
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_SERVICES_CLOUDSYNC
|
||||
if (CloudSync && CloudSync.ready && CloudSync().tabsReady && CloudSync().tabs.hasRemoteTabs()) {
|
||||
this._generateCloudSyncTabList()
|
||||
.then(complete, complete);
|
||||
} else {
|
||||
complete();
|
||||
}
|
||||
#else
|
||||
complete();
|
||||
#endif
|
||||
},
|
||||
|
||||
_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"/>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue