Bug 590805 - Sync UI: Revamp setup wizard (part 1), r=mconnor a=blocking2.0
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Wed, 08 Sep 2010 03:37:46 +0200
changeset 52167 e06b3042151c3f7122df50d7f2a697e2e99daab0
parent 52166 2ef01e996c0198a0115920cc18bcca9cf269e2d5
child 52168 201cc86810962292e4290630cb0fea9b60bbc18c
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconnor, blocking2.0
bugs590805
milestone2.0b6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 590805 - Sync UI: Revamp setup wizard (part 1), r=mconnor a=blocking2.0 Generate Sync Key: Implement key generation and backup (email/print/save)
browser/base/content/syncKey.xhtml
browser/base/content/syncUtils.js
browser/base/jar.mn
browser/locales/en-US/chrome/browser/syncSetup.dtd
browser/locales/en-US/chrome/browser/syncSetup.properties
new file mode 100644
--- /dev/null
+++ b/browser/base/content/syncKey.xhtml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Firefox Sync.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Philipp von Weitershausen <philipp@weitershausen.de>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+<!DOCTYPE html [
+  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+  %htmlDTD;
+  <!ENTITY % syncSetupDTD SYSTEM "chrome://browser/locale/syncSetup.dtd">
+  %syncSetupDTD;
+]>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>&page.syncKeyBackup.title;</title>
+  <style type="text/css">
+    #synckey { font-size: 150% }
+  </style>
+</head>
+
+<body>
+<h1>&page.syncKeyBackup.title;</h1>
+
+<p id="synckey">SYNCKEY</p>
+
+<p>&page.syncKeyBackup.descr;</p>
+
+</body>
+</html>
--- a/browser/base/content/syncUtils.js
+++ b/browser/base/content/syncUtils.js
@@ -17,16 +17,17 @@
  * Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Edward Lee <edilee@mozilla.com>
  *   Mike Connor <mconnor@mozilla.com>
  *   Paul O’Shannessy <paul@oshannessy.com>
+ *   Philipp von Weitershausen <philipp@weitershausen.de>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -34,16 +35,21 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 // Weave should always exist before before this file gets included.
 let gSyncUtils = {
+  get bundle() {
+    delete this.bundle;
+    return this.bundle = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
+  },
+
   // opens in a new window if we're in a modal prefwindow world, in a new tab otherwise
   _openLink: function (url) {
     let thisDocEl = document.documentElement,
         openerDocEl = window.opener && window.opener.document.documentElement;
     if (thisDocEl.id == "accountSetup" && window.opener &&
         openerDocEl.id == "BrowserPreferences" && !openerDocEl.instantApply)
       openUILinkIn(url, "window");
     else if (thisDocEl.id == "BrowserPreferences" && !thisDocEl.instantApply)
@@ -105,16 +111,146 @@ let gSyncUtils = {
   },
 
   openAddedClientFirstrun: function () {
     let url = this._baseURL + "secondrun.html";
     this._openLink(url);
   },
 
   /**
+   * Generate 20 random characters a-z
+   */
+  generatePassphrase: function() {
+    let rng = Cc["@mozilla.org/security/random-generator;1"]
+                .createInstance(Ci.nsIRandomGenerator);
+    let bytes = rng.generateRandomBytes(20);
+    return [String.fromCharCode(97 + Math.floor(byte * 26 / 256))
+            for each (byte in bytes)].join("");
+  },
+
+  /**
+   * Hyphenate a 20 character passphrase in 4 groups of 5.
+   */
+  hyphenatePassphrase: function(passphrase) {
+    return passphrase.slice(0, 5) + '-'
+         + passphrase.slice(5, 10) + '-'
+         + passphrase.slice(10, 15) + '-'
+         + passphrase.slice(15, 20);
+  },
+
+  /**
+   * Remove hyphens as inserted by hyphenatePassphrase().
+   */
+  normalizePassphrase: function(pp) {
+    if (pp.length == 23 && pp[5] == '-' && pp[11] == '-' && pp[17] == '-')
+      return pp.slice(0, 5) + pp.slice(6, 11)
+           + pp.slice(12, 17) + pp.slice(18, 23);
+    return pp;
+  },
+
+  /**
+   * Trigger the mailto protocol handler to send a passphrase backup email.
+   * 
+   * @param elid : ID of the form element containing the passphrase.
+   */
+  passphraseEmail: function(elid) {
+    let pp = document.getElementById(elid).value;
+    let subject = this.bundle.GetStringFromName("email.synckey.subject");
+    let body = this.bundle.formatStringFromName("email.synckey.body", [pp], 1);
+    let uri = Weave.Utils.makeURI("mailto:?subject=" + subject + "&body=" + body);
+    let protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+                     .getService(Ci.nsIExternalProtocolService);
+    protoSvc.loadURI(uri);
+  },
+
+  /**
+   * 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/syncKey.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;
+
+      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.synckey.title");
+    this._preparePPiframe(elid, function(iframe) {
+      let filepicker = Cc["@mozilla.org/filepicker;1"]
+                         .createInstance(Ci.nsIFilePicker);
+      filepicker.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
+      filepicker.appendFilters(Ci.nsIFilePicker.filterHTML);
+      filepicker.defaultString = "Firefox Sync Key.html";
+      let rv = filepicker.show();
+      if (rv == Ci.nsIFilePicker.returnOK
+          || rv == Ci.nsIFilePicker.returnReplace) {
+        let stream = Cc["@mozilla.org/network/file-output-stream;1"]
+                       .createInstance(Ci.nsIFileOutputStream);
+        stream.init(filepicker.file, -1, -1, 0);
+
+        let serializer = new XMLSerializer();
+        let output = serializer.serializeToString(iframe.contentDocument);
+        output = Weave.Utils.encodeUTF8(output);
+        stream.write(output, output.length);
+      }
+      return false;
+    });
+  },
+
+
+  /**
    * validatePassword / validatePassphrase
    *
    * @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]
    */
 
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -61,16 +61,17 @@ browser.jar:
 *       content/browser/aboutSyncTabs.xul             (content/aboutSyncTabs.xul)
         content/browser/aboutSyncTabs.js              (content/aboutSyncTabs.js)
         content/browser/aboutSyncTabs.css             (content/aboutSyncTabs.css)
 *       content/browser/aboutSyncTabs-bindings.xml    (content/aboutSyncTabs-bindings.xml)
 *       content/browser/syncSetup.xul                 (content/syncSetup.xul)
         content/browser/syncSetup.js                  (content/syncSetup.js)
 *       content/browser/syncGenericChange.xul         (content/syncGenericChange.xul)
         content/browser/syncGenericChange.js          (content/syncGenericChange.js)
+*       content/browser/syncKey.xhtml                 (content/syncKey.xhtml)
 *       content/browser/syncNotification.xml          (content/syncNotification.xml)
         content/browser/syncUtils.js                  (content/syncUtils.js)
 #endif
 # XXX: We should exclude this one as well (bug 71895)
 *       content/browser/hiddenWindow.xul              (content/hiddenWindow.xul)
 #ifdef XP_MACOSX
 *       content/browser/macBrowserOverlay.xul         (content/macBrowserOverlay.xul)
 *       content/browser/downloadManagerOverlay.xul    (content/downloadManagerOverlay.xul)
--- a/browser/locales/en-US/chrome/browser/syncSetup.dtd
+++ b/browser/locales/en-US/chrome/browser/syncSetup.dtd
@@ -41,16 +41,19 @@
 <!ENTITY passphraseEntry.accesskey    "S">
 <!ENTITY passphraseConfirm.label      "Confirm">
 <!ENTITY passphraseConfirm.accesskey  "m">
 
 <!-- New Account Page 3: Sync Type Options -->
 <!ENTITY setup.newAccountPrefs2.title.label "Browser Sync Preferences">
 <!ENTITY syncComputerName.label       "Computer Name:">
 <!ENTITY syncComputerName.accesskey   "c">
+
+<!ENTITY page.syncKeyBackup.title     "Your Firefox Sync Key">
+<!ENTITY page.syncKeyBackup.descr     "This secret key is used to encrypt your personal data. It is not uploaded to the server and cannot be recovered. Do not lose it or share with other people.">
 <!ENTITY syncModeSwitchDesc.label     "&brandShortName; will: ">
 <!ENTITY syncModeSwitchDesc.accesskey "w">
 <!ENTITY syncEverything.label         "Sync Everything">
 <!ENTITY customSync.label             "Use my custom settings">
 <!ENTITY syncEverythingDescription.label  "Your bookmarks, history, passwords, preferences, and tabs will be synced.">
 <!ENTITY syncItem.bookmarks.label     "Sync Bookmarks">
 <!ENTITY syncItem.bookmarks.accesskey "m">
 <!ENTITY syncItem.tabs.label          "Sync Tabs">
--- a/browser/locales/en-US/chrome/browser/syncSetup.properties
+++ b/browser/locales/en-US/chrome/browser/syncSetup.properties
@@ -7,10 +7,14 @@ usernameNotAvailable.label  = Already in
 
 # LOCALIZATION NOTE (additionalClients.label, bookmarkCount.label, historyCount.label, passwordCount.label).
 # We'll fix the lack of PluralForms in bug 583661.
 additionalClients.label     = and %S additional devices
 bookmarkCount.label         = %S bookmarks
 historyCount.label          = %S days of history
 passwordCount.label         = %S passwords
 
+email.synckey.subject       = Your Firefox Sync Key
+email.synckey.body          = Congratulations for signing up for Firefox Sync! Your secret Firefox Sync key is %S. Do not lose it or share with other people.
+save.synckey.title = Save Sync Key
+
 # Several other strings are used (via Weave.Status.login), but they come from
 #  /services/sync */