Merge last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 17 May 2012 23:25:38 -0400
changeset 94317 e794cef56df66afbfd1f6f974c93fb08397899e7
parent 94291 e34babb3039307fc7904fc13982874b3838e3a7c (current diff)
parent 94262 c70b34f9477da6c841e6dffbbad82f0614606f74 (diff)
child 94318 20fbb51c9462b71e7f105343bb96782bb654e89b
push id9568
push userMs2ger@gmail.com
push dateFri, 18 May 2012 11:33:04 +0000
treeherdermozilla-inbound@1d43642295b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone15.0a1
first release with
nightly linux32
e794cef56df6 / 15.0a1 / 20120518030516 / files
nightly linux64
e794cef56df6 / 15.0a1 / 20120518030516 / files
nightly mac
e794cef56df6 / 15.0a1 / 20120518030516 / files
nightly win32
e794cef56df6 / 15.0a1 / 20120518030516 / files
nightly win64
e794cef56df6 / 15.0a1 / 20120518030516 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge last PGO-green inbound changeset to m-c.
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -56,16 +56,19 @@ var gAdvancedPane = {
     if (extraArgs && extraArgs["advancedTab"]){
       advancedPrefs.selectedTab = document.getElementById(extraArgs["advancedTab"]);
     } else {
       var preference = document.getElementById("browser.preferences.advanced.selectedTabIndex");
       if (preference.value !== null)
         advancedPrefs.selectedIndex = preference.value;
     }
 
+#ifdef HAVE_SHELL_SERVICE
+    this.updateSetDefaultBrowser();
+#endif
 #ifdef MOZ_UPDATER
     this.updateReadPrefs();
 #endif
     this.updateOfflineApps();
 #ifdef MOZ_CRASHREPORTER
     this.initSubmitCrashes();
 #endif
     this.updateActualCacheSize("disk");
@@ -699,42 +702,31 @@ var gAdvancedPane = {
    * Preferences:
    *
    * browser.shell.checkDefault
    * - true if a default-browser check (and prompt to make it so if necessary)
    *   occurs at startup, false otherwise
    */
 
   /**
-   * Checks whether the browser is currently registered with the operating
-   * system as the default browser.  If the browser is not currently the
-   * default browser, the user is given the option of making it the default;
-   * otherwise, the user is informed that this browser already is the browser.
+   * Show button for setting browser as default browser or information that
+   * browser is already the default browser.
    */
-  checkNow: function ()
+  updateSetDefaultBrowser: function()
   {
     var shellSvc = Components.classes["@mozilla.org/browser/shell-service;1"]
                              .getService(Components.interfaces.nsIShellService);
-    var brandBundle = document.getElementById("bundleBrand");
-    var shellBundle = document.getElementById("bundleShell");
-    var brandShortName = brandBundle.getString("brandShortName");
-    var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
-    var promptMessage;
-    const IPS = Components.interfaces.nsIPromptService;
-    var psvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                         .getService(IPS);
-    if (!shellSvc.isDefaultBrowser(false)) {
-      promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", 
-                                                     [brandShortName]);
-      var rv = psvc.confirmEx(window, promptTitle, promptMessage, 
-                              IPS.STD_YES_NO_BUTTONS,
-                              null, null, null, null, { });
-      if (rv == 0)
-        shellSvc.setDefaultBrowser(true, false);
-    }
-    else {
-      promptMessage = shellBundle.getFormattedString("alreadyDefaultBrowser",
-                                                     [brandShortName]);
-      psvc.alert(window, promptTitle, promptMessage);
-    }
+    let selectedIndex = shellSvc.isDefaultBrowser(false) ? 1 : 0;
+    document.getElementById("setDefaultPane").selectedIndex = selectedIndex;
+  },
+
+  /**
+   * Set browser as the operating system default browser.
+   */
+  setDefaultBrowser: function()
+  {
+    var shellSvc = Components.classes["@mozilla.org/browser/shell-service;1"]
+                             .getService(Components.interfaces.nsIShellService);
+    shellSvc.setDefaultBrowser(true, false);
+    document.getElementById("setDefaultPane").selectedIndex = 1;
   }
 #endif
 };
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -199,24 +199,27 @@
                       preference="layout.spellcheckDefault"/>
           </groupbox>
 
           <!-- System Defaults -->
           <groupbox id="systemDefaultsGroup" orient="vertical">
             <caption label="&systemDefaults.label;"/>
 
 #ifdef HAVE_SHELL_SERVICE
-            <hbox id="checkDefaultBox" align="center" flex="1">      
-              <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
-                        label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
-                        flex="1"/>
-              <button id="checkDefaultButton"
-                      label="&checkNow.label;" accesskey="&checkNow.accesskey;"
-                      oncommand="gAdvancedPane.checkNow()"
-                      preference="pref.general.disable_button.default_browser"/>
+            <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
+                      label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
+                      flex="1"/>
+            <hbox class="indent">
+              <deck id="setDefaultPane">
+                <button id="setDefaultButton"
+                        label="&setDefault.label;" accesskey="&setDefault.accesskey;"
+                        oncommand="gAdvancedPane.setDefaultBrowser();"
+                        preference="pref.general.disable_button.default_browser"/>
+                <description>&isDefault.label;</description>
+              </deck>
             </hbox>
 #ifdef MOZ_CRASHREPORTER
             <checkbox id="submitCrashesBox" flex="1"
                       oncommand="gAdvancedPane.updateSubmitCrashes();"
                       label="&submitCrashes.label;" accesskey="&submitCrashes.accesskey;"/>
 #endif
 #endif
             <checkbox id="submitTelemetryBox" flex="1"
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -15,16 +15,19 @@ var gAdvancedPane = {
   {
     this._inited = true;
     var advancedPrefs = document.getElementById("advancedPrefs");
 
     var preference = document.getElementById("browser.preferences.advanced.selectedTabIndex");
     if (preference.value !== null)
         advancedPrefs.selectedIndex = preference.value;
 
+#ifdef HAVE_SHELL_SERVICE
+    this.updateSetDefaultBrowser();
+#endif
 #ifdef MOZ_UPDATER
     this.updateReadPrefs();
 #endif
     this.updateOfflineApps();
 #ifdef MOZ_CRASHREPORTER
     this.initSubmitCrashes();
 #endif
     this.updateActualCacheSize("disk");
@@ -662,42 +665,31 @@ var gAdvancedPane = {
    * Preferences:
    *
    * browser.shell.checkDefault
    * - true if a default-browser check (and prompt to make it so if necessary)
    *   occurs at startup, false otherwise
    */
 
   /**
-   * Checks whether the browser is currently registered with the operating
-   * system as the default browser.  If the browser is not currently the
-   * default browser, the user is given the option of making it the default;
-   * otherwise, the user is informed that this browser already is the browser.
+   * Show button for setting browser as default browser or information that
+   * browser is already the default browser.
    */
-  checkNow: function ()
+  updateSetDefaultBrowser: function()
   {
     var shellSvc = Components.classes["@mozilla.org/browser/shell-service;1"]
                              .getService(Components.interfaces.nsIShellService);
-    var brandBundle = document.getElementById("bundleBrand");
-    var shellBundle = document.getElementById("bundleShell");
-    var brandShortName = brandBundle.getString("brandShortName");
-    var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
-    var promptMessage;
-    const IPS = Components.interfaces.nsIPromptService;
-    var psvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
-                         .getService(IPS);
-    if (!shellSvc.isDefaultBrowser(false)) {
-      promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
-                                                     [brandShortName]);
-      var rv = psvc.confirmEx(window, promptTitle, promptMessage,
-                              IPS.STD_YES_NO_BUTTONS,
-                              null, null, null, null, { });
-      if (rv == 0)
-        shellSvc.setDefaultBrowser(true, false);
-    }
-    else {
-      promptMessage = shellBundle.getFormattedString("alreadyDefaultBrowser",
-                                                     [brandShortName]);
-      psvc.alert(window, promptTitle, promptMessage);
-    }
+    let selectedIndex = shellSvc.isDefaultBrowser(false) ? 1 : 0;
+    document.getElementById("setDefaultPane").selectedIndex = selectedIndex;
+  },
+
+  /**
+   * Set browser as the operating system default browser.
+   */
+  setDefaultBrowser: function()
+  {
+    var shellSvc = Components.classes["@mozilla.org/browser/shell-service;1"]
+                             .getService(Components.interfaces.nsIShellService);
+    shellSvc.setDefaultBrowser(true, false);
+    document.getElementById("setDefaultPane").selectedIndex = 1;
   }
 #endif
 };
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -184,24 +184,27 @@
                   onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
                   preference="layout.spellcheckDefault"/>
       </groupbox>
       <!-- System Defaults -->
       <groupbox id="systemDefaultsGroup" orient="vertical">
         <caption label="&systemDefaults.label;"/>
 
 #ifdef HAVE_SHELL_SERVICE
-        <hbox id="checkDefaultBox" align="center" flex="1">
-          <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
-                    label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
-                    flex="1"/>
-          <button id="checkDefaultButton"
-                  label="&checkNow.label;" accesskey="&checkNow.accesskey;"
-                  oncommand="gAdvancedPane.checkNow()"
-                  preference="pref.general.disable_button.default_browser"/>
+        <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
+                      label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
+                      flex="1"/>
+        <hbox class="indent">
+          <deck id="setDefaultPane">
+            <button id="setDefaultButton"
+                    label="&setDefault.label;" accesskey="&setDefault.accesskey;"
+                    oncommand="gAdvancedPane.setDefaultBrowser();"
+                    preference="pref.general.disable_button.default_browser"/>
+            <description>&isDefault.label;</description>
+          </deck>
         </hbox>
 #ifdef MOZ_CRASHREPORTER
         <checkbox id="submitCrashesBox" flex="1"
                   oncommand="gAdvancedPane.updateSubmitCrashes();"
                   label="&submitCrashes.label;" accesskey="&submitCrashes.accesskey;"/>
 #endif
 #endif
         <checkbox id="submitTelemetryBox" flex="1"
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -20,18 +20,19 @@
 <!ENTITY allowHWAccel.label              "Use hardware acceleration when available">
 <!ENTITY allowHWAccel.accesskey          "r">
 <!ENTITY checkSpelling.label             "Check my spelling as I type">
 <!ENTITY checkSpelling.accesskey         "t">
 
 <!ENTITY systemDefaults.label            "System Defaults">
 <!ENTITY alwaysCheckDefault.label        "Always check to see if &brandShortName; is the default browser on startup"><!--XXX-->
 <!ENTITY alwaysCheckDefault.accesskey    "w">
-<!ENTITY checkNow.label                  "Check Now">
-<!ENTITY checkNow.accesskey              "N">
+<!ENTITY setDefault.label                "Make &brandShortName; the default browser">
+<!ENTITY setDefault.accesskey            "d">
+<!ENTITY isDefault.label                 "&brandShortName; is currently your default browser">
 <!ENTITY submitCrashes.label             "Submit crash reports">
 <!ENTITY submitCrashes.accesskey         "S">
 <!ENTITY submitTelemetry.label           "Submit performance data">
 <!ENTITY submitTelemetry.accesskey       "P">
 
 <!ENTITY networkTab.label                "Network">
 
 <!ENTITY connection.label                "Connection">
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -454,17 +454,17 @@ def wrapCommand(cmd):
   return cmd
 
 class ShutdownLeakLogger(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
-  MAX_LEAK_COUNT = 10
+  MAX_LEAK_COUNT = 7
 
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -43,17 +43,18 @@ def run_in(path, args):
 
 def patch(patch, plevel, srcdir):
     patch = os.path.realpath(patch)
     check_run(['patch', '-d', srcdir, '-p%s' % plevel, '-i', patch, '--fuzz=0',
                '-s'])
 
 def build_package(package_source_dir, package_build_dir, configure_args,
                    make = old_make):
-    os.mkdir(package_build_dir)
+    if not os.path.exists(package_build_dir):
+        os.mkdir(package_build_dir)
     run_in(package_build_dir,
            ["%s/configure" % package_source_dir] + configure_args)
     run_in(package_build_dir, [make, "-j8"])
     run_in(package_build_dir, [make, "install"])
 
 def build_aux_tools(base_dir):
     make_build_dir = base_dir + '/make_build'
     build_package(make_source_dir, make_build_dir,
@@ -156,16 +157,22 @@ def build_one_stage_aux(stage_dir, is_st
 
     tool_inst_dir = stage_dir + '/inst'
     os.mkdir(tool_inst_dir)
     os.mkdir(tool_inst_dir + '/lib64')
     os.symlink('lib64', tool_inst_dir + '/lib')
 
     build_linux_headers(tool_inst_dir)
 
+    # zlib's configure only works if run from the source dir, copy the source
+    zlib_build_dir = stage_dir + '/zlib'
+    shutil.copytree(zlib_source_dir, zlib_build_dir)
+    build_package(zlib_build_dir, zlib_build_dir,
+                  ["--prefix=%s" % tool_inst_dir])
+
     binutils_build_dir = stage_dir + '/binutils'
     build_package(binutils_source_dir, binutils_build_dir,
                   ["--prefix=%s" % tool_inst_dir,
                    "--without-zlib"])
 
     # During stage one we have to build gcc first, this glibc doesn't even
     # build with gcc 4.6. During stage two, we have to build glibc first.
     # The problem is that libstdc++ is built with xgcc and if glibc has
@@ -192,16 +199,17 @@ def build_source_dir(prefix, version):
 binutils_version = "2.21.1"
 glibc_version = "2.5.1"
 linux_version = "2.6.18"
 tar_version = "1.26"
 gawk_version = "3.1.5"
 make_version = "3.81"
 gcc_version = "4.5.2"
 mpfr_version = "2.4.2"
+zlib_version = "1.2.3"
 gmp_version = "5.0.1"
 mpc_version = "0.8.1"
 unifdef_version = "2.6"
 
 binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \
     binutils_version
 glibc_source_uri = "http://ftp.gnu.org/gnu/glibc/glibc-%s.tar.bz2" % \
     glibc_version
@@ -214,41 +222,44 @@ gawk_source_uri = "http://ftp.gnu.org/gn
 make_source_uri = "http://ftp.gnu.org/gnu/make/make-%s.tar.bz2" % \
     make_version
 unifdef_source_uri = "http://dotat.at/prog/unifdef/unifdef-%s.tar.gz" % \
     unifdef_version
 gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \
     (gcc_version, gcc_version)
 mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \
     (mpfr_version, mpfr_version)
+zlib_source_uri = "http://iweb.dl.sourceforge.net/project/libpng/zlib/%s/zlib-%s.tar.bz2" % (zlib_version, zlib_version)
 gmp_source_uri = "http://ftp.gnu.org/gnu/gmp/gmp-%s.tar.bz2" % gmp_version
 mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \
     mpc_version
 
 binutils_source_tar = download_uri(binutils_source_uri)
 glibc_source_tar = download_uri(glibc_source_uri)
 linux_source_tar = download_uri(linux_source_uri)
 tar_source_tar = download_uri(tar_source_uri)
 gawk_source_tar = download_uri(gawk_source_uri)
 make_source_tar = download_uri(make_source_uri)
 unifdef_source_tar = download_uri(unifdef_source_uri)
 mpc_source_tar = download_uri(mpc_source_uri)
 mpfr_source_tar = download_uri(mpfr_source_uri)
+zlib_source_tar = download_uri(zlib_source_uri)
 gmp_source_tar = download_uri(gmp_source_uri)
 gcc_source_tar = download_uri(gcc_source_uri)
 
 binutils_source_dir  = build_source_dir('binutils-', binutils_version)
 glibc_source_dir  = build_source_dir('glibc-', glibc_version)
 linux_source_dir  = build_source_dir('linux-', linux_version)
 tar_source_dir  = build_source_dir('tar-', tar_version)
 gawk_source_dir  = build_source_dir('gawk-', gawk_version)
 make_source_dir  = build_source_dir('make-', make_version)
 unifdef_source_dir  = build_source_dir('unifdef-', unifdef_version)
 mpc_source_dir  = build_source_dir('mpc-', mpc_version)
 mpfr_source_dir = build_source_dir('mpfr-', mpfr_version)
+zlib_source_dir = build_source_dir('zlib-', zlib_version)
 gmp_source_dir  = build_source_dir('gmp-', gmp_version)
 gcc_source_dir  = build_source_dir('gcc-', gcc_version)
 
 if not os.path.exists(source_dir):
     os.makedirs(source_dir)
     extract(binutils_source_tar, source_dir)
     patch('binutils-deterministic.patch', 1, binutils_source_dir)
     extract(glibc_source_tar, source_dir)
@@ -256,16 +267,17 @@ if not os.path.exists(source_dir):
     patch('glibc-deterministic.patch', 1, glibc_source_dir)
     run_in(glibc_source_dir, ["autoconf"])
     extract(tar_source_tar, source_dir)
     extract(gawk_source_tar, source_dir)
     extract(make_source_tar, source_dir)
     extract(unifdef_source_tar, source_dir)
     extract(mpc_source_tar, source_dir)
     extract(mpfr_source_tar, source_dir)
+    extract(zlib_source_tar, source_dir)
     extract(gmp_source_tar, source_dir)
     extract(gcc_source_tar, source_dir)
     patch('plugin_finish_decl.diff', 0, gcc_source_dir)
     patch('libtool-74c8993c178a1386ea5e2363a01d919738402f30.patch', 1, gcc_source_dir)
     patch('pr49911.diff', 1, gcc_source_dir)
     patch('r159628-r163231-r171807.patch', 1, gcc_source_dir)
     patch('gcc-fixinc.patch', 1, gcc_source_dir)
     patch('gcc-include.patch', 1, gcc_source_dir)
@@ -279,26 +291,21 @@ build_aux_tools(build_dir)
 basic_path = aux_inst_dir + "/bin:/bin:/usr/bin"
 
 stage1_dir = build_dir + '/stage1'
 build_one_stage({"PATH"   : basic_path,
                  "CC"     : "gcc",
                  "CXX"    : "g++" },
                 stage1_dir, True)
 
-stage1_tool_inst_dir = stage1_dir + '/inst'
-stage2_dir = build_dir + '/stage2'
-build_one_stage({"PATH"   : stage1_tool_inst_dir + "/bin:" + basic_path,
-                 "CC"     : "gcc -fgnu89-inline",
-                 "CXX"    : "g++",
-                 "RANLIB" : "true" },
-                stage2_dir, False)
+for stage_num in range(2, 4):
+    prev_stage_dir = build_dir + '/stage' + str(stage_num - 1)
+    prev_stage_inst_dir = prev_stage_dir + '/inst'
+    cur_stage_dir = build_dir + '/stage' + str(stage_num)
+    build_one_stage({"PATH"   : prev_stage_inst_dir + "/bin:" + basic_path,
+                     "CC"     : "gcc -fgnu89-inline",
+                     "CXX"    : "g++",
+                     "RANLIB" : "true" },
+                    cur_stage_dir, False)
 
-stage2_tool_inst_dir = stage2_dir + '/inst'
 stage3_dir = build_dir + '/stage3'
-build_one_stage({"PATH"   : stage2_tool_inst_dir + "/bin:" + basic_path,
-                 "CC"     : "gcc -fgnu89-inline",
-                 "CXX"    : "g++",
-                 "RANLIB" : "true" },
-                stage3_dir, False)
-
 build_tar_package(aux_inst_dir + "/bin/tar",
                   "toolchain.tar", stage3_dir, "inst")
--- a/content/base/test/file_websocket_wsh.py
+++ b/content/base/test/file_websocket_wsh.py
@@ -26,16 +26,29 @@ def web_socket_do_extra_handshake(reques
   elif request.ws_protocol == "test-22":
     # The timeout is 5 seconds
     time.sleep(13)
   elif request.ws_protocol == "test-41b":
     request.sts = "max-age=100"
   else:
     pass
 
+# Behave according to recommendation of RFC 6455, section # 5.5.1:
+#  "When sending a Close frame in response, the endpoint typically echos the
+#   status code it received."  
+# - Without this, pywebsocket replies with 1000 to any close code.
+#
+#  Note that this function is only called when the client initiates the close
+def web_socket_passive_closing_handshake(request):
+  if request.ws_close_code == 1005:
+    return None, None
+  else:
+    return request.ws_close_code, request.ws_close_reason
+
+
 def web_socket_transfer_data(request):
   if request.ws_protocol == "test-2.1" or request.ws_protocol == "test-2.2":
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-6":
     resp = "wrong message"
     if msgutil.receive_message(request) == "1":
       resp = "2"
     msgutil.send_message(request, resp.decode('utf-8'))
@@ -52,17 +65,16 @@ def web_socket_transfer_data(request):
     msgutil.send_message(request, "test-7 data")
   elif request.ws_protocol == "test-10":
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-11":
     resp = "wrong message"
     if msgutil.receive_message(request) == "client data":
       resp = "server data"
     msgutil.send_message(request, resp.decode('utf-8'))
-    msgutil.close_connection(request)
   elif request.ws_protocol == "test-12":
     msgutil.close_connection(request)
   elif request.ws_protocol == "test-13":
     # first one binary message containing the byte 0x61 ('a')
     request.connection.write('\xff\x01\x61')
     # after a bad utf8 message
     request.connection.write('\x01\x61\xff')
     msgutil.close_connection(request)
--- a/content/base/test/test_websocket.html
+++ b/content/base/test/test_websocket.html
@@ -20,18 +20,18 @@
  * tests:
  *  1. client tries to connect to a http scheme location;
  *  2. assure serialization of the connections;
  *  3. client tries to connect to an non-existent ws server;
  *  4. client tries to connect using a relative url;
  *  5. client uses an invalid protocol value;
  *  6. counter and encoding check;
  *  7. onmessage event origin property check
- *  8. client calls close() and the server sends the close frame in
- *     acknowledgement;
+ *  8. client calls close() and the server sends the close frame (with no code
+ *     or reason) in acknowledgement;
  *  9. client closes the connection before the ws connection is established;
  * 10. client sends a message before the ws connection is established;
  * 11. a simple hello echo;
  * 12. client sends a message with bad bytes;
  * 13. server sends an invalid message;
  * 14. server sends the close frame, it doesn't close the tcp connection and
  *     it keeps sending normal ws messages;
  * 15. server closes the tcp connection, but it doesn't send the close frame;
@@ -50,17 +50,17 @@
  * 27. ctor with invalid sub-protocol array containing an empty element in list
  * 28. ctor using valid 1 element sub-protocol array
  * 29. ctor using all valid 5 element sub-protocol array
  * 30. ctor using valid 1 element sub-protocol array with element server will
  *     reject
  * 31. ctor using valid 2 element sub-protocol array with 1 element server
  *     will reject and one server will accept.
  * 32. ctor using invalid sub-protocol array that contains duplicate items
- * 33. default close code test
+ * 33. test for sending/receiving custom close code (but no close reason)
  * 34. test for receiving custom close code and reason
  * 35. test for sending custom close code and reason
  * 36. negative test for sending out of range close code
  * 37. negative test for too long of a close reason
  * 38. ensure extensions attribute is defined
  * 39. a basic wss:// connectivity test
  * 40. negative test for wss:// with no cert
  * 41. HSTS
@@ -382,16 +382,20 @@ function test8()
   ws.onopen = function()
   {
     ok(ws.protocol == "test-8", "test-8 subprotocol selection");
     ws.close();
   }
   ws.onclose = function(e)
   {
     shouldCloseCleanly(e);
+    // We called close() with no close code: so pywebsocket will also send no
+    // close code, which translates to code 1005 
+    ok(e.code == 1005, "test-8 close code has wrong value:" + e.code);
+    ok(e.reason == "", "test-8 close reason has wrong value:" + e.reason);
     doTest(9);
   };
 }
 
 var waitTest9 = false;
 
 function test9()
 {
@@ -456,26 +460,28 @@ function test11()
   ws.onopen = function()
   {
     ok(ws.readyState == 1, "open bad readyState in test-11!");
     ws.send("client data");
   }
   ws.onmessage = function(e)
   {
     ok(e.data == "server data", "bad received message in test-11!");
-    ws.close();
+    ws.close(1000, "Have a nice day");
 
 // this ok() is disabled due to a race condition - it state may have
 // advanced through 2 (closing) and into 3 (closed) before it is evald
 //    ok(ws.readyState == 2, "onmessage bad readyState in test-11!");
   }
   ws.onclose = function(e)
   {
     ok(ws.readyState == 3, "onclose bad readyState in test-11!");
     shouldCloseCleanly(e);
+    ok(e.code == 1000, "test 11 got wrong close code: " + e.code);
+    ok(e.reason == "Have a nice day", "test 11 got wrong close reason: " + e.reason);
     doTest(12);
   }
 }
 
 function test12()
 {
  ok(true,"test 12");
 
@@ -937,24 +943,25 @@ function test32()
 function test33()
 {
   var prots=["test33"];
 
   var ws = CreateTestWS("ws://mochi.test:8888/tests/content/base/test/file_websocket", prots);
   ws.onopen = function(e)
   {
     ok(true, "test 33 open");
-    ws.close();
+    ws.close(3131);   // pass code but not reason
   };
 
   ws.onclose = function(e)
   {
     ok(true, "test 33 close");
-    ok(e.wasClean, "test 33 closed cleanly");
-    ok(e.code == 1000, "test 33 had normal 1000 error code");
+    shouldCloseCleanly(e);
+    ok(e.code == 3131, "test 33 got wrong close code: " + e.code);
+    ok(e.reason === "", "test 33 got wrong close reason: " + e.reason);
     doTest(34);
   };
 }
 
 function test34()
 {
   var prots=["test-34"];
 
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -1121,18 +1121,17 @@ void nsBuiltinDecoderStateMachine::Audio
     }
 
     PRInt64 framesWritten = 0;
     if (missingFrames.value() > 0) {
       // The next audio chunk begins some time after the end of the last chunk
       // we pushed to the audio hardware. We must push silence into the audio
       // hardware so that the next audio chunk begins playback at the correct
       // time.
-      missingFrames = NS_MIN(static_cast<PRInt64>(PR_UINT32_MAX),
-                             missingFrames.value());
+      missingFrames = NS_MIN<int64_t>(UINT32_MAX, missingFrames.value());
       LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of silence",
                          mDecoder.get(), PRInt32(missingFrames.value())));
       framesWritten = PlaySilence(static_cast<PRUint32>(missingFrames.value()),
                                   channels, playedFrames.value());
     } else {
       framesWritten = PlayFromAudioQueue(sampleTime.value(), channels);
     }
     audioDuration += framesWritten;
--- a/dom/sms/interfaces/nsIDOMSmsFilter.idl
+++ b/dom/sms/interfaces/nsIDOMSmsFilter.idl
@@ -1,47 +1,15 @@
-/* ***** 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 mozilla.org.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, builtinclass, uuid(7da08e45-ee81-4293-912b-2f2fea5b6935)]
+[scriptable, builtinclass, uuid(ce055c33-553d-4b0e-9bd8-91d93a057c90)]
 interface nsIDOMMozSmsFilter : nsISupports
 {
   // A date that can return null.
   [implicit_jscontext]
   attribute jsval startDate;
 
   // A date that can return null.
   [implicit_jscontext]
@@ -49,9 +17,13 @@ interface nsIDOMMozSmsFilter : nsISuppor
 
   // An array of DOMString that can return null.
   [implicit_jscontext]
   attribute jsval numbers;
 
   // A DOMString that can return and be set to "sent", "received" or null.
   [Null(Empty)]
   attribute DOMString delivery;
+
+  // A read flag that can be a boolean or undefined.
+  [implicit_jscontext]
+  attribute jsval read;
 };
--- a/dom/sms/interfaces/nsIDOMSmsManager.idl
+++ b/dom/sms/interfaces/nsIDOMSmsManager.idl
@@ -1,51 +1,19 @@
-/* ***** 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 mozilla.org.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMEventListener;
 interface nsIDOMMozSmsRequest;
 interface nsIDOMMozSmsFilter;
 
-[scriptable, uuid(c9916dce-2947-41bb-95c2-818f792a020c)]
+[scriptable, uuid(6363c0ff-b58f-4fb3-9707-0ba27f120b2c)]
 interface nsIDOMMozSmsManager : nsIDOMEventTarget
 {
   unsigned short      getNumberOfMessagesForText(in DOMString text);
 
   // The first parameter can be either a DOMString (only one number) or an array
   // of DOMStrings.
   // The method returns a SmsRequest object if one number has been passed.
   // An array of SmsRequest objects otherwise.
@@ -53,12 +21,14 @@ interface nsIDOMMozSmsManager : nsIDOMEv
 
   [binaryname(GetMessageMoz)] nsIDOMMozSmsRequest getMessage(in long id);
 
   // The parameter can be either a message id or a SmsMessage.
   nsIDOMMozSmsRequest delete(in jsval param);
 
   nsIDOMMozSmsRequest getMessages(in nsIDOMMozSmsFilter filter, in boolean reverse);
 
+  nsIDOMMozSmsRequest markMessageRead(in long id, in boolean aValue);
+
   attribute nsIDOMEventListener onreceived;
   attribute nsIDOMEventListener onsent;
   attribute nsIDOMEventListener ondelivered;
 };
--- a/dom/sms/interfaces/nsIDOMSmsMessage.idl
+++ b/dom/sms/interfaces/nsIDOMSmsMessage.idl
@@ -1,52 +1,20 @@
-/* ***** 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 mozilla.org.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *  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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, builtinclass, uuid(20da0c51-2224-49ae-afe9-4b309c6d8f84)]
+[scriptable, builtinclass, uuid(fc58ba6e-70de-4550-aa1e-790ecc19cf98)]
 interface nsIDOMMozSmsMessage : nsISupports
 {
   // TODO: we should add SENT and RECEIVED DOMString constants, see bug 443316.
 
   readonly attribute long      id;
   readonly attribute DOMString delivery;  // Should be "sent" or "received".
   readonly attribute DOMString sender;
   readonly attribute DOMString receiver;
   readonly attribute DOMString body;
   [implicit_jscontext]
   readonly attribute jsval     timestamp; // jsval is for Date.
+  readonly attribute boolean   read;
 };
--- a/dom/sms/interfaces/nsISmsDatabaseService.idl
+++ b/dom/sms/interfaces/nsISmsDatabaseService.idl
@@ -1,62 +1,31 @@
-/* ***** 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 mozilla.org.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 %{C++
 #define SMS_DATABASE_SERVICE_CID \
 { 0x2454c2a1, 0xefdd, 0x4d96,    \
 { 0x83, 0xbd, 0x51, 0xa2, 0x9a, 0x21, 0xf5, 0xab } }
 #define SMS_DATABASE_SERVICE_CONTRACTID "@mozilla.org/sms/smsdatabaseservice;1"
 %}
 
 interface nsIDOMMozSmsFilter;
 
-[scriptable, uuid(74d6ff05-818a-4179-a745-6258e1c3cd08)]
+[scriptable, uuid(30e8cdfb-155d-44c7-8fb3-6bcd9c1c3f99)]
 interface nsISmsDatabaseService : nsISupports
 {
   // Takes some information required to save the message and returns its id.
   long saveReceivedMessage(in DOMString aSender, in DOMString aBody, in unsigned long long aDate);
   // Takes some information required to save the message and returns its id.
   long saveSentMessage(in DOMString aReceiver, in DOMString aBody, in unsigned long long aDate);
 
   [binaryname(GetMessageMoz)] void getMessage(in long messageId, in long requestId, [optional] in unsigned long long processId);
   void deleteMessage(in long messageId, in long requestId, [optional] in unsigned long long processId);
 
   void createMessageList(in nsIDOMMozSmsFilter filter, in boolean reverse, in long requestId, [optional] in unsigned long long processId);
   void getNextMessageInList(in long listId, in long requestId, [optional] in unsigned long long processId);
   void clearMessageList(in long listId);
+  void markMessageRead(in long messageId, in boolean value, in long requestId, [optional] in unsigned long long processId);
 };
--- a/dom/sms/interfaces/nsISmsRequestManager.idl
+++ b/dom/sms/interfaces/nsISmsRequestManager.idl
@@ -10,17 +10,17 @@ interface nsIDOMMozSmsManager;
 
 %{C++
 #define SMS_REQUEST_MANAGER_CID \
 { 0xa97a3129, 0x1e0b, 0x45da,    \
 { 0xa3, 0x85, 0xcf, 0xe5, 0xb0, 0xb1, 0xc4, 0x8f } }
 #define SMS_REQUEST_MANAGER_CONTRACTID "@mozilla.org/sms/smsrequestmanager;1"
 %}
 
-[scriptable, uuid(1638b963-3a45-4937-b6a9-280c1bfb166c)]
+[scriptable, uuid(be747cca-ba07-410a-8b91-2754d5406d66)]
 interface nsISmsRequestManager : nsISupports
 {
 
   /**
    * All SMS related errors that could apply to SmsRequest objects.
    * Make sure to keep this list in sync with the list in:
    * embedding/android/GeckoSmsManager.java
    */
@@ -70,9 +70,14 @@ interface nsISmsRequestManager : nsISupp
                                in nsIDOMMozSmsMessage aMessage);
 
   void notifyGotNextMessage(in long aRequestId,
                             in nsIDOMMozSmsMessage aMessage);
 
   void notifyReadMessageListFailed(in long aRequestId,
                                    in long aError);
 
+  void notifyMarkedMessageRead(in long aRequestId,
+                               in bool aRead);
+
+  void notifyMarkMessageReadFailed(in long aRequestId,
+                                   in long aError);
 };
--- a/dom/sms/interfaces/nsISmsService.idl
+++ b/dom/sms/interfaces/nsISmsService.idl
@@ -1,62 +1,30 @@
-/* ***** 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 mozilla.org.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *  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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMMozSmsMessage;
 
 %{C++
 #define SMS_SERVICE_CID { 0xbada3cb8, 0xa568, 0x4dff, { 0xb5, 0x43, 0x52, 0xbb, 0xb3, 0x14, 0x31, 0x21 } }
 #define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
 %}
 
-[scriptable, builtinclass, uuid(a0fbbe74-5d61-4b7e-b7ab-9b5224f9e5e9)]
+[scriptable, builtinclass, uuid(00d23a50-6ed1-48b4-b1e9-5987b155e54b)]
 interface nsISmsService : nsISupports
 {
   boolean        hasSupport();
   unsigned short getNumberOfMessagesForText(in DOMString text);
             void send(in DOMString number, in DOMString message,
                       in long requestId, [optional] in unsigned long long processId);
 
   [implicit_jscontext]
   nsIDOMMozSmsMessage createSmsMessage(in long      id,
                                        in DOMString delivery,
                                        in DOMString sender,
                                        in DOMString receiver,
                                        in DOMString body,
-                                       in jsval     timestamp);
+                                       in jsval     timestamp,
+                                       in bool      read);
 };
--- a/dom/sms/src/SmsFilter.cpp
+++ b/dom/sms/src/SmsFilter.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsFilter.h"
 #include "nsIDOMClassInfo.h"
 #include "Constants.h"
 #include "nsError.h"
 #include "Constants.h"
 #include "jsapi.h"
 #include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch.
@@ -61,16 +29,17 @@ NS_INTERFACE_MAP_END
 NS_IMPL_ADDREF(SmsFilter)
 NS_IMPL_RELEASE(SmsFilter)
 
 SmsFilter::SmsFilter()
 {
   mData.startDate() = 0;
   mData.endDate() = 0;
   mData.delivery() = eDeliveryState_Unknown;
+  mData.read() = eReadState_Unknown;
 }
 
 SmsFilter::SmsFilter(const SmsFilterData& aData)
   : mData(aData)
 {
 }
 
 /* static */ nsresult
@@ -254,11 +223,40 @@ SmsFilter::SetDelivery(const nsAString& 
   if (aDelivery.Equals(DELIVERY_SENT)) {
     mData.delivery() = eDeliveryState_Sent;
     return NS_OK;
   }
 
   return NS_ERROR_INVALID_ARG;
 }
 
+NS_IMETHODIMP
+SmsFilter::GetRead(JSContext* aCx, jsval* aRead)
+{
+  if (mData.read() == eReadState_Unknown) {
+    *aRead = JSVAL_VOID;
+    return NS_OK;
+  }
+
+  aRead->setBoolean(mData.read());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::SetRead(JSContext* aCx, const jsval& aRead)
+{
+  if (aRead == JSVAL_VOID) {
+    mData.read() = eReadState_Unknown;
+    return NS_OK;
+  }
+
+  if (!aRead.isBoolean()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mData.read() = aRead.toBoolean() ? eReadState_Read : eReadState_Unread;
+  return NS_OK;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/sms/src/SmsManager.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsFilter.h"
 #include "SmsManager.h"
 #include "nsIDOMClassInfo.h"
 #include "nsISmsService.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "Constants.h"
@@ -311,16 +279,39 @@ SmsManager::GetMessages(nsIDOMMozSmsFilt
     do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsDBService, NS_ERROR_FAILURE);
 
   smsDBService->CreateMessageList(filter, aReverse, requestId, 0);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SmsManager::MarkMessageRead(PRInt32 aId, bool aValue,
+                            nsIDOMMozSmsRequest** aRequest)
+{
+  nsCOMPtr<nsISmsRequestManager> requestManager =
+    do_GetService(SMS_REQUEST_MANAGER_CONTRACTID);
+
+  PRInt32 requestId;
+  nsresult rv = requestManager->CreateRequest(this, aRequest, &requestId);
+  if (NS_FAILED(rv)) {
+    NS_ERROR("Failed to create the request!");
+    return rv;
+  }
+
+  nsCOMPtr<nsISmsDatabaseService> smsDBService =
+    do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(smsDBService, NS_ERROR_FAILURE);
+
+  smsDBService->MarkMessageRead(aId, aValue, requestId, 0);
+
+  return NS_OK;
+}
+
 NS_IMPL_EVENT_HANDLER(SmsManager, received)
 NS_IMPL_EVENT_HANDLER(SmsManager, sent)
 NS_IMPL_EVENT_HANDLER(SmsManager, delivered)
 
 nsresult
 SmsManager::DispatchTrustedSmsEventToSelf(const nsAString& aEventName, nsIDOMMozSmsMessage* aMessage)
 {
   nsRefPtr<nsDOMEvent> event = new SmsEvent(nsnull, nsnull);
--- a/dom/sms/src/SmsMessage.cpp
+++ b/dom/sms/src/SmsMessage.cpp
@@ -1,45 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *   Philipp von Weitershausen <philipp@weitershausen.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsMessage.h"
 #include "nsIDOMClassInfo.h"
 #include "jsapi.h" // For OBJECT_TO_JSVAL and JS_NewDateObjectMsec
 #include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch
 #include "Constants.h"
 
 DOMCI_DATA(MozSmsMessage, mozilla::dom::sms::SmsMessage)
@@ -54,45 +21,47 @@ NS_INTERFACE_MAP_BEGIN(SmsMessage)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozSmsMessage)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(SmsMessage)
 NS_IMPL_RELEASE(SmsMessage)
 
 SmsMessage::SmsMessage(PRInt32 aId, DeliveryState aDelivery,
                        const nsString& aSender, const nsString& aReceiver,
-                       const nsString& aBody, PRUint64 aTimestamp)
-  : mData(aId, aDelivery, aSender, aReceiver, aBody, aTimestamp)
+                       const nsString& aBody, PRUint64 aTimestamp, bool aRead)
+  : mData(aId, aDelivery, aSender, aReceiver, aBody, aTimestamp, aRead)
 {
 }
 
 SmsMessage::SmsMessage(const SmsMessageData& aData)
   : mData(aData)
 {
 }
 
 /* static */ nsresult
 SmsMessage::Create(PRInt32 aId,
                    const nsAString& aDelivery,
                    const nsAString& aSender,
                    const nsAString& aReceiver,
                    const nsAString& aBody,
                    const jsval& aTimestamp,
+                   const bool aRead,
                    JSContext* aCx,
                    nsIDOMMozSmsMessage** aMessage)
 {
   *aMessage = nsnull;
 
   // SmsMessageData exposes these as references, so we can simply assign
   // to them.
   SmsMessageData data;
   data.id() = aId;
   data.sender() = nsString(aSender);
   data.receiver() = nsString(aReceiver);
   data.body() = nsString(aBody);
+  data.read() = aRead;
 
   if (aDelivery.Equals(DELIVERY_RECEIVED)) {
     data.delivery() = eDeliveryState_Received;
   } else if (aDelivery.Equals(DELIVERY_SENT)) {
     data.delivery() = eDeliveryState_Sent;
   } else {
     return NS_ERROR_INVALID_ARG;
   }
@@ -176,11 +145,18 @@ SmsMessage::GetBody(nsAString& aBody)
 
 NS_IMETHODIMP
 SmsMessage::GetTimestamp(JSContext* cx, jsval* aDate)
 {
   *aDate = OBJECT_TO_JSVAL(JS_NewDateObjectMsec(cx, mData.timestamp()));
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SmsMessage::GetRead(bool* aRead)
+{
+  *aRead = mData.read();
+  return NS_OK;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/SmsMessage.h
+++ b/dom/sms/src/SmsMessage.h
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* 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/. */
 
 #ifndef mozilla_dom_sms_SmsMessage_h
 #define mozilla_dom_sms_SmsMessage_h
 
 #include "mozilla/dom/sms/PSms.h"
 #include "nsIDOMSmsMessage.h"
 #include "nsString.h"
 #include "jspubtd.h"
@@ -51,25 +19,26 @@ namespace sms {
 class SmsMessage : public nsIDOMMozSmsMessage
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMMOZSMSMESSAGE
 
   SmsMessage(PRInt32 aId, DeliveryState aDelivery, const nsString& aSender,
              const nsString& aReceiver, const nsString& aBody,
-             PRUint64 aTimestamp);
+             PRUint64 aTimestamp, bool aRead);
   SmsMessage(const SmsMessageData& aData);
 
   static nsresult Create(PRInt32 aId,
                          const nsAString& aDelivery,
                          const nsAString& aSender,
                          const nsAString& aReceiver,
                          const nsAString& aBody,
                          const JS::Value& aTimestamp,
+                         const bool aRead,
                          JSContext* aCx,
                          nsIDOMMozSmsMessage** aMessage);
   const SmsMessageData& GetData() const;
 
 private:
   // Don't try to use the default constructor.
   SmsMessage();
 
--- a/dom/sms/src/SmsRequestManager.cpp
+++ b/dom/sms/src/SmsRequestManager.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsRequestManager.h"
 #include "nsIDOMSmsMessage.h"
 #include "nsDOMEvent.h"
 #include "SmsCursor.h"
 #include "SmsManager.h"
 
 /**
@@ -240,11 +208,23 @@ SmsRequestManager::NotifyReadMessageList
   nsCOMPtr<nsIDOMMozSmsCursor> cursor = request->GetCursor();
   if (cursor) {
     static_cast<SmsCursor*>(cursor.get())->Disconnect();
   }
 
   return NotifyError(aRequestId, aError);
 }
 
+NS_IMETHODIMP
+SmsRequestManager::NotifyMarkedMessageRead(PRInt32 aRequestId, bool aRead)
+{
+  return NotifySuccess<bool>(aRequestId, aRead);
+}
+
+NS_IMETHODIMP
+SmsRequestManager::NotifyMarkMessageReadFailed(PRInt32 aRequestId, PRInt32 aError)
+{
+  return NotifyError(aRequestId, aError);
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/Types.h
+++ b/dom/sms/src/Types.h
@@ -1,76 +1,63 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* 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/. */
 
 #ifndef mozilla_dom_sms_Types_h
 #define mozilla_dom_sms_Types_h
 
 #include "IPCMessageUtils.h"
 
 namespace mozilla {
 namespace dom {
 namespace sms {
 
-// For SmsMessageDate.delivery.
+// For SmsMessageData.delivery.
 // Please keep the following files in sync with enum below:
 // embedding/android/GeckoSmsManager.java
 enum DeliveryState {
   eDeliveryState_Sent = 0,
   eDeliveryState_Received,
   eDeliveryState_Unknown,
   // This state should stay at the end.
   eDeliveryState_EndGuard
 };
 
+// For SmsFilterData.read
+enum ReadState {
+  eReadState_Unknown = -1,
+  eReadState_Unread,
+  eReadState_Read,
+  // This state should stay at the end.
+  eReadState_EndGuard
+};
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
 
 namespace IPC {
 
 /**
  * Delivery state serializer.
  */
 template <>
 struct ParamTraits<mozilla::dom::sms::DeliveryState>
   : public EnumSerializer<mozilla::dom::sms::DeliveryState,
                           mozilla::dom::sms::eDeliveryState_Sent,
                           mozilla::dom::sms::eDeliveryState_EndGuard>
 {};
 
+/**
+ * Read state serializer.
+ */
+template <>
+struct ParamTraits<mozilla::dom::sms::ReadState>
+  : public EnumSerializer<mozilla::dom::sms::ReadState,
+                          mozilla::dom::sms::eReadState_Unknown,
+                          mozilla::dom::sms::eReadState_EndGuard>
+{};
+
 } // namespace IPC
 
 #endif // mozilla_dom_sms_Types_h
--- a/dom/sms/src/android/SmsDatabaseService.cpp
+++ b/dom/sms/src/android/SmsDatabaseService.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsFilter.h"
 #include "SmsDatabaseService.h"
 #include "AndroidBridge.h"
 
 namespace mozilla {
 namespace dom {
 namespace sms {
@@ -127,11 +95,19 @@ SmsDatabaseService::ClearMessageList(PRI
   if (!AndroidBridge::Bridge()) {
     return NS_OK;
   }
 
   AndroidBridge::Bridge()->ClearMessageList(aListId);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SmsDatabaseService::MarkMessageRead(PRInt32 aMessageId, bool aValue,
+                                    PRInt32 aRequestId, PRUint64 aProcessId)
+{
+  // TODO: This would need to be implemented as part of Bug 748391
+  return NS_OK;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/android/SmsService.cpp
+++ b/dom/sms/src/android/SmsService.cpp
@@ -1,45 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *   Philipp von Weitershausen <philipp@weitershausen.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/sms/SmsMessage.h"
 #include "SmsService.h"
 #include "AndroidBridge.h"
 #include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
@@ -81,18 +48,19 @@ SmsService::Send(const nsAString& aNumbe
 
 NS_IMETHODIMP
 SmsService::CreateSmsMessage(PRInt32 aId,
                              const nsAString& aDelivery,
                              const nsAString& aSender,
                              const nsAString& aReceiver,
                              const nsAString& aBody,
                              const jsval& aTimestamp,
+                             const bool aRead,
                              JSContext* aCx,
                              nsIDOMMozSmsMessage** aMessage)
 {
-  return SmsMessage::Create(
-    aId, aDelivery, aSender, aReceiver, aBody, aTimestamp, aCx, aMessage);
+  return SmsMessage::Create(aId, aDelivery, aSender, aReceiver, aBody,
+                            aTimestamp, aRead, aCx, aMessage);
 }
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/fallback/SmsDatabaseService.cpp
+++ b/dom/sms/src/fallback/SmsDatabaseService.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsDatabaseService.h"
 
 namespace mozilla {
 namespace dom {
 namespace sms {
 
 NS_IMPL_ISUPPORTS1(SmsDatabaseService, nsISmsDatabaseService)
@@ -98,11 +66,19 @@ SmsDatabaseService::GetNextMessageInList
 
 NS_IMETHODIMP
 SmsDatabaseService::ClearMessageList(PRInt32 aListId)
 {
   NS_ERROR("We should not be here!");
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SmsDatabaseService::MarkMessageRead(PRInt32 aMessageId, bool aValue,
+                                  PRInt32 aRequestId, PRUint64 aProcessId)
+{
+  NS_ERROR("We should not be here!");
+  return NS_OK;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/fallback/SmsService.cpp
+++ b/dom/sms/src/fallback/SmsService.cpp
@@ -1,45 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *   Philipp von Weitershausen <philipp@weitershausen.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/sms/SmsMessage.h"
 #include "SmsService.h"
 #include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 namespace sms {
@@ -71,18 +38,19 @@ SmsService::Send(const nsAString& aNumbe
 
 NS_IMETHODIMP
 SmsService::CreateSmsMessage(PRInt32 aId,
                              const nsAString& aDelivery,
                              const nsAString& aSender,
                              const nsAString& aReceiver,
                              const nsAString& aBody,
                              const jsval& aTimestamp,
+                             const bool aRead,
                              JSContext* aCx,
                              nsIDOMMozSmsMessage** aMessage)
 {
-  return SmsMessage::Create(
-    aId, aDelivery, aSender, aReceiver, aBody, aTimestamp, aCx, aMessage);
+  return SmsMessage::Create(aId, aDelivery, aSender, aReceiver, aBody,
+                            aTimestamp, aRead, aCx, aMessage);
 }
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/ipc/PSms.ipdl
+++ b/dom/sms/src/ipc/PSms.ipdl
@@ -1,70 +1,40 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et ft=cpp : */
-/* ***** 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 Mozilla Code.
- *
- * The Initial Developer of the Original Code is
- *   The Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Chris Jones <jones.chris.g@gmail.com>
- *
- * 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 include "mozilla/dom/sms/Types.h";
 
 using DeliveryState;
+using ReadState;
 
 namespace mozilla {
 namespace dom {
 namespace sms {
 
 struct SmsMessageData {
   PRInt32       id;
   DeliveryState delivery;
   nsString      sender;
   nsString      receiver;
   nsString      body;
   PRUint64      timestamp; // ms since epoch.
+  bool          read;
 };
 
 struct SmsFilterData {
   PRUint64      startDate;
   PRUint64      endDate;
   nsString[]    numbers;
   DeliveryState delivery;
+  ReadState     read;  
 };
 
 sync protocol PSms {
     manager PContent;
 
 child:
     NotifyReceivedMessage(SmsMessageData aMessageData);
 
@@ -93,16 +63,20 @@ child:
     NotifyRequestNoMessageInList(PRInt32 aRequestId, PRUint64 aProcessId);
 
     NotifyRequestCreateMessageList(PRInt32 aListId, SmsMessageData aMessageData, PRInt32 aRequestId, PRUint64 aProcessId);
 
     NotifyRequestGotNextMessage(SmsMessageData aMessageData, PRInt32 aRequestId, PRUint64 aProcessId);
 
     NotifyRequestReadListFailed(PRInt32 aError, PRInt32 aRequestId,
                                 PRUint64 aProcessId);
+    NotifyRequestMarkedMessageRead(bool aRead, PRInt32 aRequestId,
+                                   PRUint64 aProcessId);
+    NotifyRequestMarkMessageReadFailed(PRInt32 aError, PRInt32 aRequestId,
+                                       PRUint64 aProcessId);
 
 parent:
     sync HasSupport()
         returns (bool aHasSupport);
 
     sync GetNumberOfMessagesForText(nsString aText)
         returns (PRUint16 aNumber);
 
@@ -120,14 +94,16 @@ parent:
     DeleteMessage(PRInt32 aMessageId, PRInt32 aRequestId, PRUint64 aProcessId);
 
     CreateMessageList(SmsFilterData aFilter, bool aReverse, PRInt32 aRequestId, PRUint64 aProcessId);
 
     GetNextMessageInList(PRInt32 aListId, PRInt32 aRequestId, PRUint64 aProcessId);
 
     ClearMessageList(PRInt32 aListId);
 
+    MarkMessageRead(PRInt32 aMessageId, bool aValue, PRInt32 aRequestId, PRUint64 aProcessId);
+
     __delete__();
 };
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/ipc/SmsChild.cpp
+++ b/dom/sms/src/ipc/SmsChild.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsChild.h"
 #include "SmsMessage.h"
 #include "Constants.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/ContentChild.h"
 #include "SmsRequestManager.h"
@@ -235,11 +203,42 @@ SmsChild::RecvNotifyRequestReadListFaile
     return true;
   }
 
   nsCOMPtr<nsISmsRequestManager> requestManager = do_GetService(SMS_REQUEST_MANAGER_CONTRACTID);
   requestManager->NotifyReadMessageListFailed(aRequestId, aError);
   return true;
 }
 
+bool
+SmsChild::RecvNotifyRequestMarkedMessageRead(const bool& aRead,
+                                             const PRInt32& aRequestId,
+                                             const PRUint64& aProcessId)
+{
+  if (ContentChild::GetSingleton()->GetID() != aProcessId) {
+    return true;
+  }
+
+  nsCOMPtr<nsISmsRequestManager> requestManager =
+    do_GetService(SMS_REQUEST_MANAGER_CONTRACTID);
+  requestManager->NotifyMarkedMessageRead(aRequestId, aRead);
+  return true;
+}
+
+bool
+SmsChild::RecvNotifyRequestMarkMessageReadFailed(const PRInt32& aError,
+                                                 const PRInt32& aRequestId,
+                                                 const PRUint64& aProcessId)
+{
+  if (ContentChild::GetSingleton()->GetID() != aProcessId) {
+    return true;
+  }
+
+  nsCOMPtr<nsISmsRequestManager> requestManager =
+    do_GetService(SMS_REQUEST_MANAGER_CONTRACTID);
+  requestManager->NotifyMarkMessageReadFailed(aRequestId, aError);
+
+  return true;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/ipc/SmsChild.h
+++ b/dom/sms/src/ipc/SmsChild.h
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* 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/. */
 
 #ifndef mozilla_dom_sms_SmsChild_h
 #define mozilla_dom_sms_SmsChild_h
 
 #include "mozilla/dom/sms/PSmsChild.h"
 
 namespace mozilla {
 namespace dom {
@@ -55,15 +23,17 @@ public:
   NS_OVERRIDE virtual bool RecvNotifyRequestGotSms(const SmsMessageData& aMessage, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestGetSmsFailed(const PRInt32& aError, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestSmsDeleted(const bool& aDeleted, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestSmsDeleteFailed(const PRInt32& aError, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestNoMessageInList(const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestCreateMessageList(const PRInt32& aListId, const SmsMessageData& aMessage, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestGotNextMessage(const SmsMessageData& aMessage, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvNotifyRequestReadListFailed(const PRInt32& aError, const PRInt32& aRequestId, const PRUint64& aProcessId);
+  NS_OVERRIDE virtual bool RecvNotifyRequestMarkedMessageRead(const bool& aRead, const PRInt32& aRequestId, const PRUint64& aProcessId);
+  NS_OVERRIDE virtual bool RecvNotifyRequestMarkMessageReadFailed(const PRInt32& aError, const PRInt32& aRequestId, const PRUint64& aProcessId);
 };
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_sms_SmsChild_h
--- a/dom/sms/src/ipc/SmsIPCService.cpp
+++ b/dom/sms/src/ipc/SmsIPCService.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ContentChild.h"
 #include "SmsIPCService.h"
 #include "nsXULAppAPI.h"
 #include "jsapi.h"
 #include "mozilla/dom/sms/SmsChild.h"
 #include "mozilla/dom/sms/SmsMessage.h"
 #include "SmsFilter.h"
@@ -92,21 +60,22 @@ SmsIPCService::Send(const nsAString& aNu
 
 NS_IMETHODIMP
 SmsIPCService::CreateSmsMessage(PRInt32 aId,
                                 const nsAString& aDelivery,
                                 const nsAString& aSender,
                                 const nsAString& aReceiver,
                                 const nsAString& aBody,
                                 const jsval& aTimestamp,
+                                const bool aRead,
                                 JSContext* aCx,
                                 nsIDOMMozSmsMessage** aMessage)
 {
-  return SmsMessage::Create(
-    aId, aDelivery, aSender, aReceiver, aBody, aTimestamp, aCx, aMessage);
+  return SmsMessage::Create(aId, aDelivery, aSender, aReceiver, aBody,
+                            aTimestamp, aRead, aCx, aMessage);
 }
 
 /*
  * Implementation of nsISmsDatabaseService.
  */
 NS_IMETHODIMP
 SmsIPCService::SaveReceivedMessage(const nsAString& aSender,
                                    const nsAString& aBody,
@@ -169,11 +138,20 @@ SmsIPCService::GetNextMessageInList(PRIn
 
 NS_IMETHODIMP
 SmsIPCService::ClearMessageList(PRInt32 aListId)
 {
   GetSmsChild()->SendClearMessageList(aListId);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SmsIPCService::MarkMessageRead(PRInt32 aMessageId, bool aValue,
+                               PRInt32 aRequestId, PRUint64 aProcessId)
+{
+  GetSmsChild()->SendMarkMessageRead(aMessageId, aValue, aRequestId,
+                                     ContentChild::GetSingleton()->GetID());
+  return NS_OK;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/ipc/SmsParent.cpp
+++ b/dom/sms/src/ipc/SmsParent.cpp
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SmsParent.h"
 #include "nsISmsService.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "Constants.h"
 #include "nsIDOMSmsMessage.h"
 #include "mozilla/unused.h"
@@ -269,11 +237,25 @@ SmsParent::RecvClearMessageList(const PR
     do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsDBService, true);
 
   smsDBService->ClearMessageList(aListId);
 
   return true;
 }
 
+bool
+SmsParent::RecvMarkMessageRead(const PRInt32& aMessageId,
+                               const bool& aValue,
+                               const PRInt32& aRequestId,
+                               const PRUint64& aProcessId)
+{
+  nsCOMPtr<nsISmsDatabaseService> smsDBService =
+    do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(smsDBService, true);
+
+  smsDBService->MarkMessageRead(aMessageId, aValue, aRequestId, aProcessId);
+  return true;
+}
+
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/ipc/SmsParent.h
+++ b/dom/sms/src/ipc/SmsParent.h
@@ -1,44 +1,12 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** 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 mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of 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 ***** */
+/* 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/. */
 
 #ifndef mozilla_dom_sms_SmsParent_h
 #define mozilla_dom_sms_SmsParent_h
 
 #include "mozilla/dom/sms/PSmsParent.h"
 #include "nsIObserver.h"
 
 namespace mozilla {
@@ -61,16 +29,17 @@ public:
   NS_OVERRIDE virtual bool RecvSendMessage(const nsString& aNumber, const nsString& aMessage, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvSaveReceivedMessage(const nsString& aSender, const nsString& aBody, const PRUint64& aDate, PRInt32* aId);
   NS_OVERRIDE virtual bool RecvSaveSentMessage(const nsString& aRecipient, const nsString& aBody, const PRUint64& aDate, PRInt32* aId);
   NS_OVERRIDE virtual bool RecvGetMessage(const PRInt32& aMessageId, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvDeleteMessage(const PRInt32& aMessageId, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvCreateMessageList(const SmsFilterData& aFilter, const bool& aReverse, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvGetNextMessageInList(const PRInt32& aListId, const PRInt32& aRequestId, const PRUint64& aProcessId);
   NS_OVERRIDE virtual bool RecvClearMessageList(const PRInt32& aListId);
+  NS_OVERRIDE virtual bool RecvMarkMessageRead(const PRInt32& aMessageId, const bool& aValue, const PRInt32& aRequestId, const PRUint64& aProcessId);
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason why);
 
 private:
   static nsTArray<SmsParent*>* gSmsParents;
 };
 
--- a/dom/sms/src/ril/SmsDatabaseService.js
+++ b/dom/sms/src/ril/SmsDatabaseService.js
@@ -9,25 +9,31 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const RIL_SMSDATABASESERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsdatabaseservice;1";
 const RIL_SMSDATABASESERVICE_CID = Components.ID("{a1fa610c-eb6c-4ac2-878f-b005d5e89249}");
 
 const DEBUG = false;
 const DB_NAME = "sms";
-const DB_VERSION = 1;
+const DB_VERSION = 2;
 const STORE_NAME = "sms";
 
 const DELIVERY_SENT = "sent";
 const DELIVERY_RECEIVED = "received";
 
 const FILTER_TIMESTAMP = "timestamp";
 const FILTER_NUMBERS = "numbers";
 const FILTER_DELIVERY = "delivery";
+const FILTER_READ = "read";
+
+// We canĀ“t create an IDBKeyCursor with a boolean, so we need to use numbers
+// instead.
+const FILTER_READ_UNREAD = 0;
+const FILTER_READ_READ = 1;
 
 const READ_ONLY = "readonly";
 const READ_WRITE = "readwrite";
 const PREV = "prev";
 const NEXT = "next";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/smsservice;1",
@@ -151,16 +157,22 @@ SmsDatabaseService.prototype = {
       let db = event.target.result;
 
       switch (event.oldVersion) {
         case 0:
           if (DEBUG) debug("New database");
           self.createSchema(db);
           break;
 
+        case 1:
+          if (DEBUG) debug("Upgrade to version 2. Including `read` index");
+          let objectStore = event.target.transaction.objectStore(STORE_NAME); 
+          self.upgradeSchema(objectStore);
+          break;
+
         default:
           event.target.transaction.abort();
           callback("Old database version: " + event.oldVersion, null);
           break;
       }
     };
     request.onerror = function (event) {
       //TODO look at event.target.Code and change error constant accordingly
@@ -205,29 +217,37 @@ SmsDatabaseService.prototype = {
     });
   },
 
   /**
    * Create the initial database schema.
    *
    * TODO need to worry about number normalization somewhere...
    * TODO full text search on body???
-   * TODO We probably want to add a 'read' index
    */
   createSchema: function createSchema(db) {
     let objectStore = db.createObjectStore(STORE_NAME, { keyPath: "id" });
     objectStore.createIndex("id", "id", { unique: true });
     objectStore.createIndex("delivery", "delivery", { unique: false });
     objectStore.createIndex("sender", "sender", { unique: false });
     objectStore.createIndex("receiver", "receiver", { unique: false });
-    objectStore.createIndex("timestamp", "timestamp", { unique:false });
+    objectStore.createIndex("timestamp", "timestamp", { unique: false });
+    objectStore.createIndex("read", "read", { unique: false });
     if (DEBUG) debug("Created object stores and indexes");
   },
 
   /**
+   * Upgrade to the corresponding database schema version.
+   */
+  upgradeSchema: function upgradeSchema(objectStore) {
+    // For now, the only possible upgrade is to version 2.
+    objectStore.createIndex("read", "read", { unique: false });  
+  },
+
+  /**
    * Helper function to make the intersection of the partial result arrays
    * obtained within createMessageList.
    *
    * @param keys
    *        Object containing the partial result arrays.
    * @param fiter
    *        Object containing the filter search criteria used to retrieved the
    *        partial results.
@@ -241,16 +261,21 @@ SmsDatabaseService.prototype = {
         return result.indexOf(i) != -1;
       });
     }
     if (keys[FILTER_DELIVERY].length || filter.delivery) {
       result = keys[FILTER_DELIVERY].filter(function(i) {
         return result.indexOf(i) != -1;
       });
     }
+    if (keys[FILTER_READ].length || filter.read) {
+      result = keys[FILTER_READ].filter(function(i) {
+        return result.indexOf(i) != -1;
+      });
+    }
     return result;
   },
 
   /**
    * Helper function called after createMessageList gets the final result array
    * containing the list of primary keys of records that matches the provided
    * search criteria. This function retrieves from the store the message with
    * the primary key matching the first one in the message list array and keeps
@@ -288,17 +313,18 @@ SmsDatabaseService.prototype = {
         }
         self.lastMessageListId += 1;
         self.messageLists[self.lastMessageListId] = messageList;
         let sms = gSmsService.createSmsMessage(message.id,
                                                message.delivery,
                                                message.sender,
                                                message.receiver,
                                                message.body,
-                                               message.timestamp);
+                                               message.timestamp,
+                                               message.read);
         gSmsRequestManager.notifyCreateMessageList(requestId,
                                                    self.lastMessageListId,
                                                    sms);
       };
     });
   },
 
   saveMessage: function saveMessage(message) {
@@ -320,26 +346,28 @@ SmsDatabaseService.prototype = {
    * nsISmsDatabaseService API
    */
 
   saveReceivedMessage: function saveReceivedMessage(sender, body, date) {
     let message = {delivery:  DELIVERY_RECEIVED,
                    sender:    sender,
                    receiver:  this.mRIL.radioState.msisdn, 
                    body:      body,
-                   timestamp: date};
+                   timestamp: date,
+                   read:      FILTER_READ_UNREAD};
     return this.saveMessage(message);
   },
 
   saveSentMessage: function saveSentMessage(receiver, body, date) {
     let message = {delivery:  DELIVERY_SENT,
                    sender:    this.mRIL.radioState.msisdn,
                    receiver:  receiver,
                    body:      body,
-                   timestamp: date};
+                   timestamp: date,
+                   read:      FILTER_READ_READ};
     return this.saveMessage(message);
   },
 
   getMessage: function getMessage(messageId, requestId) {
     if (DEBUG) debug("Retrieving message with ID " + messageId);
     this.newTxn(READ_ONLY, function (error, txn, store) {
       if (error) {
         if (DEBUG) debug(error);
@@ -373,17 +401,18 @@ SmsDatabaseService.prototype = {
             requestId, Ci.nsISmsRequestManager.UNKNOWN_ERROR);
           return;
         }
         let message = gSmsService.createSmsMessage(data.id,
                                                    data.delivery,
                                                    data.sender,
                                                    data.receiver,
                                                    data.body,
-                                                   data.timestamp);
+                                                   data.timestamp,
+                                                   data.read);
         gSmsRequestManager.notifyGotSms(requestId, message);
       };
 
       txn.onerror = function onerror(event) {
         if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
         //TODO look at event.target.errorCode, pick appropriate error constant
         gSmsRequestManager.notifyGetSmsFailed(
           requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
@@ -427,26 +456,28 @@ SmsDatabaseService.prototype = {
 
   createMessageList: function createMessageList(filter, reverse, requestId) {
     if (DEBUG) {
       debug("Creating a message list. Filters:" +
             " startDate: " + filter.startDate +
             " endDate: " + filter.endDate +
             " delivery: " + filter.delivery +
             " numbers: " + filter.numbers +
+            " read: " + filter.read +
             " reverse: " + reverse);
     }
     // This object keeps the lists of keys retrieved by the search specific to
     // each nsIMozSmsFilter. Once all the keys have been retrieved from the
     // store, the final intersection of this arrays will contain all the
     // keys for the message list that we are creating.
     let filteredKeys = {};
     filteredKeys[FILTER_TIMESTAMP] = [];
     filteredKeys[FILTER_NUMBERS] = [];
     filteredKeys[FILTER_DELIVERY] = [];
+    filteredKeys[FILTER_READ] = [];
 
     // Callback function to iterate through request results via IDBCursor.
     let successCb = function onsuccess(result, filter) {
       // Once the cursor has retrieved all keys that matches its key range,
       // the filter search is done.
       if (!result) {
         if (DEBUG) {
           debug("These messages match the " + filter + " filter: " +
@@ -520,16 +551,30 @@ SmsDatabaseService.prototype = {
           senderRequest.onsuccess = receiverRequest.onsuccess =
             function onsuccess(event){
               successCb(event.target.result, FILTER_NUMBERS);
             };
           senderRequest.onerror = receiverRequest.onerror = errorCb;
         }
       }
 
+      // Retrieve the keys from the 'read' index that matches the value of
+      // filter.read
+      if (filter.read != undefined) {
+        let read = filter.read ? FILTER_READ_READ : FILTER_READ_UNREAD;
+        if (DEBUG) debug("filter.read " + read);
+        let readKeyRange = IDBKeyRange.only(read);
+        let readRequest = store.index("read")
+                               .openKeyCursor(readKeyRange);
+        readRequest.onsuccess = function onsuccess(event) {
+          successCb(event.target.result, FILTER_READ);
+        };
+        readRequest.onerror = errorCb;
+      }
+
       txn.oncomplete = function oncomplete(event) {
         if (DEBUG) debug("Transaction " + txn + " completed.");
         // We need to get the intersection of all the partial searches to
         // get the final result array.
         let result =  self.keyIntersection(filteredKeys, filter);
         if (!result.length) {
           if (DEBUG) debug("No messages matching the filter criteria");
           gSmsRequestManager.notifyNoMessageInList(requestId);
@@ -580,17 +625,18 @@ SmsDatabaseService.prototype = {
           gSmsRequestManager.notifyReadMessageListFailed(
             requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
         }
         let sms = gSmsService.createSmsMessage(message.id,
                                                message.delivery,
                                                message.sender,
                                                message.receiver,
                                                message.body,
-                                               message.timestamp);
+                                               message.timestamp,
+                                               message.read);
         gSmsRequestManager.notifyGotNextMessage(requestId, sms);
       };
 
       txn.onerror = function onerror(event) {
         //TODO check event.target.errorCode
         if (DEBUG) {
           debug("Error retrieving message id: " + messageId +
                 ". Error code: " + event.target.errorCode);
@@ -599,16 +645,75 @@ SmsDatabaseService.prototype = {
           requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
       };
     });
   },
 
   clearMessageList: function clearMessageList(listId) {
     if (DEBUG) debug("Clearing message list: " + listId);
     delete this.messageLists[listId];
+  },
+
+  markMessageRead: function markMessageRead(messageId, value, requestId) {
+    if (DEBUG) debug("Setting message " + messageId + " read to " + value);
+    this.newTxn(READ_WRITE, function (error, txn, store) {
+      if (error) {
+        if (DEBUG) debug(error);
+        gSmsRequestManager.notifyMarkMessageReadFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+      let getRequest = store.get(messageId);
+
+      getRequest.onsuccess = function onsuccess(event) {
+        let message = event.target.result;
+        if (DEBUG) debug("Message ID " + messageId + " not found");
+        if (!message) {
+          gSmsRequestManager.notifyMarkMessageReadFailed(
+            requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+          return;
+        }
+        if (message.id != messageId) {
+          if (DEBUG) {
+            debug("Retrieve message ID (" + messageId + ") is " +
+                  "different from the one we got");
+          }
+          gSmsRequestManager.notifyMarkMessageReadFailed(
+            requestId, Ci.nsISmsRequestManager.UNKNOWN_ERROR);
+          return;
+        }
+        // If the value to be set is the same as the current message `read`
+        // value, we just notify successfully.
+        if (message.read == value) {
+          if (DEBUG) debug("The value of message.read is already " + value);
+          gSmsRequestManager.notifyMarkedMessageRead(requestId, message.read);
+          return;
+        }
+        message.read = value ? FILTER_READ_READ : FILTER_READ_UNREAD;
+        if (DEBUG) debug("Message.read set to: " + value);
+        let putRequest = store.put(message);
+        putRequest.onsuccess = function onsuccess(event) {
+          if (DEBUG) {
+            debug("Update successfully completed. Message: " +
+                  JSON.stringify(event.target.result));
+          }
+          let checkRequest = store.get(message.id);
+          checkRequest.onsuccess = function onsuccess(event) {
+            gSmsRequestManager.notifyMarkedMessageRead(
+              requestId, event.target.result.read);
+          };
+        }
+      };
+
+      txn.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on transaction ", event.target.errorCode);
+        gSmsRequestManager.notifyMarkMessageReadFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   }
 
 };
 
 XPCOMUtils.defineLazyGetter(SmsDatabaseService.prototype, "mRIL", function () {
     return Cc["@mozilla.org/telephony/system-worker-manager;1"]
               .getService(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIRadioInterfaceLayer);
--- a/dom/sms/src/ril/SmsService.cpp
+++ b/dom/sms/src/ril/SmsService.cpp
@@ -1,44 +1,12 @@
-/* ***** 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 SMS DOM API.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * 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 ***** */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/sms/SmsMessage.h"
 #include "SmsService.h"
 #include "SystemWorkerManager.h"
 #include "jsapi.h"
 #include "nsIInterfaceRequestorUtils.h"
 
 using mozilla::dom::gonk::SystemWorkerManager;
@@ -95,18 +63,19 @@ SmsService::Send(const nsAString& aNumbe
 
 NS_IMETHODIMP
 SmsService::CreateSmsMessage(PRInt32 aId,
                              const nsAString& aDelivery,
                              const nsAString& aSender,
                              const nsAString& aReceiver,
                              const nsAString& aBody,
                              const jsval& aTimestamp,
+                             const bool aRead,
                              JSContext* aCx,
                              nsIDOMMozSmsMessage** aMessage)
 {
-  return SmsMessage::Create(
-    aId, aDelivery, aSender, aReceiver, aBody, aTimestamp, aCx, aMessage);
+  return SmsMessage::Create(aId, aDelivery, aSender, aReceiver, aBody,
+                            aTimestamp, aRead, aCx, aMessage);
 }
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/tests/test_smsdatabaseservice.xul
+++ b/dom/sms/tests/test_smsdatabaseservice.xul
@@ -124,18 +124,21 @@ function fakeSmsRequestManager(obj) {
   gRegistrar.unregisterFactory(service.CID, oldFactory);
   gRegistrar.registerFactory(service.CID,
                              "Fake SmsRequestManager",
                              service.contractID,
                              service);
 }
 
 const DB_NAME = "sms";
-const DB_VERSION = 1;
+const DB_VERSION = 2;
 const STORE_NAME = "sms";
+const MAX_SMS = 3;
+const LONG_MAX = 2147483647;
+
 gIDBManager.initWindowless(this);
 
 let _db;
 function ensureDB(callback) {
   if (_db) {
     callback(_db);
     return;
   }
@@ -153,26 +156,29 @@ function ensureDB(callback) {
   request.onupgradeneeded =
   request.onblocked = function onerror(event) {
     ok(false, "Opening DB failed: " + request.errorCode);
   };
 };
 
 function checkDB(callback) {
   ensureDB(function (db) {
-    let txn = db.transaction([STORE_NAME], Ci.nsIIDBTransaction.READ_WRITE);
+    let txn = db.transaction([STORE_NAME], "readwrite");
     let store = txn.objectStore(STORE_NAME);
     txn.oncomplete = run_next_test;
     txn.onerror = function onerror(event) {
       ok(false, "Transaction failed: " + event.target.errorCode);
     };
     callback(store);
   });
 }
 
+function newRandomId() {
+  return Math.floor(Math.random() * LONG_MAX);
+};
 
 let sms = [
   {
     sender: "+34666000000",
     receiver: "+34666111000",
     body: "message 0",
     timestamp: 1329999861762
   },
@@ -183,147 +189,694 @@ let sms = [
     timestamp: 1329999861763
   },
   {
     sender: "+34666000222",
     receiver: "+34666111222",
     body: "message 2",
     timestamp: 1329999861764
   },
-  {
-    sender: "+34666000333",
-    receiver: "+34666111333",
-    body: "message 3",
-    timestamp: 1329999861765
-  }
 ];
 
-add_test(function test_save_received_message() {
+/**
+ * nsISmsDatabaseService.saveReceivedMessage
+ */
+add_test(function test_saveReceivedMessage() {
+  info("test_saveReceivedMessage");
   let messageId = gSmsDatabaseService.saveReceivedMessage(sms[0].sender,
                                                           sms[0].body,
                                                           sms[0].timestamp);
   checkDB(function (store) {
     let request = store.get(messageId);
     request.onsuccess = function onsuccess() {
       let data = request.result;
       isnot(data, null);
       is(data.id,        messageId);
       is(data.sender,    sms[0].sender);
       is(data.receiver,  null);
       is(data.body,      sms[0].body);
       is(data.timestamp, sms[0].timestamp);
+      is(data.read,      false);
     };
   });
 });
 
-add_test(function test_save_sent_message() {
+/**
+ * nsISmsDatabaseService.saveSentMessage
+ */
+add_test(function test_saveSentMessage() {
+  info("test_saveSentMessage");
   let messageId = gSmsDatabaseService.saveSentMessage(sms[1].receiver,
                                                       sms[1].body,
                                                       sms[1].timestamp);
 
   checkDB(function (store) {
     let request = store.get(messageId);
     request.onsuccess = function onsuccess() {
       let data = request.result;
       isnot(data, null);
       is(data.id,        messageId);
       is(data.sender,    null);
       is(data.receiver,  sms[1].receiver);
       is(data.body,      sms[1].body);
       is(data.timestamp, sms[1].timestamp);
+      is(data.read,      true);
     };
   });
 });
 
+/**
+ * nsISmsDatabaseService.getMessage
+ */
 add_test(function test_getMessage_success() {
+  info("test_getMessage_success");
+  let fakeRequestId = newRandomId();
   fakeSmsRequestManager({
     notifyGotSms: function notifyGotSms(requestId, message) {
-      is(requestId, 42);
+      is(requestId, fakeRequestId);
       ok(message instanceof Ci.nsIDOMMozSmsMessage);
       is(message.id,       messageId);
       is(message.delivery, "received");
       is(message.sender,   sms[2].sender);
       is(message.receiver, null);
       is(message.body,     sms[2].body);
+      is(message.read,     false);
       run_next_test();
     }
   });
   let messageId = gSmsDatabaseService.saveReceivedMessage(sms[2].sender,
                                                           sms[2].body,
                                                           sms[2].timestamp);
   SimpleTest.executeSoon(function () {
-    gSmsDatabaseService.getMessage(messageId, 42);
+    gSmsDatabaseService.getMessage(messageId, fakeRequestId);
   });
 });
 
 add_test(function test_getMessage_failed() {
+  info("test_getMessage_failed");
+  let fakeRequestId = newRandomId();
   fakeSmsRequestManager({
     notifyGetSmsFailed: function notifyGetSmsFailed(requestId, error) {
-      is(requestId, 23);
+      is(requestId, fakeRequestId);
       is(error, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
       run_next_test();
     }
   });
-  gSmsDatabaseService.getMessage(-1, 23);
+  gSmsDatabaseService.getMessage(-1, fakeRequestId);
+});
+
+/**
+ * nsISmsDatabaseService.createMessageList
+ */
+function checkReceivedSms(message, i) {
+  ok(message instanceof Ci.nsIDOMMozSmsMessage);
+  is(message.sender,              sms[i].sender);
+  is(message.receiver,            null);
+  is(message.body,                sms[i].body);
+  is(message.timestamp.getTime(), sms[i].timestamp);
+  is(message.read,                false);
+}
+
+function checkSentSms(message, i) {
+  ok(message instanceof Ci.nsIDOMMozSmsMessage);
+  is(message.sender,              null);
+  is(message.receiver,            sms[i].receiver);
+  is(message.body,                sms[i].body);
+  is(message.timestamp.getTime(), sms[i].timestamp);
+  is(message.read,                true);
+}
+
+add_test(function test_createMessageList_empty_filter() {
+  info("test_createMessageList_empty_filter");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let i = 0;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, i);
+      auxListId = listId;
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId,
+                                                        message) {
+      is(requestId, fakeRequestId);
+      ok(message instanceof Ci.nsIDOMMozSmsMessage);
+      i % 2 ? checkSentSms(message, i) : checkReceivedSms(message, i);
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      is(i, MAX_SMS);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
 });
 
-add_test(function test_createMessageList_empty_filter() {
+add_test(function test_createMessageList_empty_filter_reverse() {
+  info("test_createMessageList_empty_filter_reverse");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let i = 2;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, i);
+      auxListId = listId;
+      i -= 1;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId,
+                                                        message) {
+      is(requestId, fakeRequestId);
+      ok(message instanceof Ci.nsIDOMMozSmsMessage);
+      i % 2 ? checkSentSms(message, i) : checkReceivedSms(message, i);
+      i -= 1;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      is(i, -1);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  gSmsDatabaseService.createMessageList(filter, true, fakeRequestId);
+});
+
+add_test(function test_createMessageList_complete_filter_one_result() {
+  info("test_createMessageList_complete_filter_one_result");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId,
+                                                        message) {
+      is(requestId, fakeRequestId);
+      ok(false, "No more messages expected");
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.startDate = new Date(sms[0].timestamp);
+  filter.endDate = new Date(sms[0].timestamp);
+  filter.numbers = [sms[0].sender];
+  filter.delivery = "received";
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_complete_filter_multi_result() {
+  info("test_createMessageList_complete_filter_multi_result");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let secondMessage = false;
   fakeSmsRequestManager({
     notifyCreateMessageList: function notifyCreateMessageList(requestId,
                                                               listId,
                                                               message) {
-      is(requestId, 24);
-      is(message.sender,              sms[0].sender);
-      is(message.receiver,            null);
-      is(message.body,                sms[0].body);
-      is(message.timestamp.getTime(), sms[0].timestamp);
-      // TODO check the rest of the message list
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      auxListId = listId;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId, message) {
+      ok(!secondMessage);
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 2);
+      secondMessage = true;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      ok(secondMessage);
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.startDate = new Date(sms[0].timestamp);
+  filter.endDate = new Date(sms[2].timestamp);
+  filter.numbers = [sms[0].sender, sms[2].sender];
+  filter.delivery = "received";
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_complete_filter_no_result() {
+  info("test_createMessageList_complete_filter_no_result");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    },
+  });
+  let filter = new MozSmsFilter();
+  filter.startDate = new Date(sms[0].timestamp);
+  filter.endDate = new Date(sms[0].timestamp);
+  filter.numbers = [sms[0].sender];
+  filter.delivery = "sent";
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_delivery_sent_filter_success() {
+  info("test_createMessageList_delivery_sent_filter_success");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkSentSms(message, 1);
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.delivery = "sent";
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_delivery_received_filter_success() {
+  info("test_createMessageList_delivery_received_filter_success");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let secondMessage = false;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      auxListId = listId;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId, message) {
+      ok(!secondMessage);
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 2);
+      secondMessage = true;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      ok(secondMessage);
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.delivery = "received";
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_startDate_filter_success() {
+  info("test_createMessageList_startDate_filter_success");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let i = 0;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, i);
+      auxListId = listId;
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId,
+                                                        message) {
+      is(requestId, fakeRequestId);
+      i % 2 ? checkSentSms(message, i) : checkReceivedSms(message, i);
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      is(i, MAX_SMS);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.startDate = new Date(sms[0].timestamp);
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_startDate_filter_failed() {
+  info("test_createMessageList_startDate_filter_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
       run_next_test();
     },
   });
   let filter = new MozSmsFilter();
-  gSmsDatabaseService.createMessageList(filter, false, 24);
+  filter.startDate = new Date(sms[2].timestamp + 1);
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_endDate_filter_success() {
+  info("test_createMessageList_endDate_filter_success");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let i = 0;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, i);
+      auxListId = listId;
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId,
+                                                        message) {
+      is(requestId, fakeRequestId);
+      i % 2 ? checkSentSms(message, i) : checkReceivedSms(message, i);
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      is(i, MAX_SMS);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.endDate = new Date(sms[2].timestamp);
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_endDate_filter_failed() {
+  info("test_createMessageList_endDate_filter_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+     notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.endDate = new Date(sms[0].timestamp - 1);
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
 });
 
-add_test(function test_createMessageList_delivery_sent_filter() {
+add_test(function test_createMessageList_fullDate_filter_success() {
+  info("test_createMessageList_fullDate_filter_success");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let i = 0;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, i);
+      auxListId = listId;
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId,
+                                                        message) {
+      is(requestId, fakeRequestId);
+      i % 2 ? checkSentSms(message, i) : checkReceivedSms(message, i);
+      i += 1;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      is(i, MAX_SMS);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.startDate = new Date(sms[0].timestamp);
+  filter.endDate = new Date(sms[MAX_SMS - 1].timestamp);
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_fullDate_filter_failed() {
+  info("test_createMessageList_fullDate_filter_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+     notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.startDate = new Date(sms[MAX_SMS - 1] + 1);
+  filter.endDate = new Date(sms[MAX_SMS - 1] + 1);
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_single_number_success() {
+  info("test_createMessageList_single_number_success");
+  let fakeRequestId = newRandomId();
+  let firstMessage = false;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(firstMessage, false);
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      firstMessage = true;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      ok(firstMessage);
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.numbers = [sms[0].sender];
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_single_number_failed() {
+  info("test_createMessageList_single_number_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+     notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.numbers = ["00000000000"];
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_multi_number_success() {
+  info("test_createMessageList_multi_number_success");
+  let fakeRequestId = newRandomId();
+  let auxListId;
+  let secondMessage = false;
   fakeSmsRequestManager({
     notifyCreateMessageList: function notifyCreateMessageList(requestId,
                                                               listId,
                                                               message) {
-      is(requestId, 24);
-      is(message.sender,              null);
-      is(message.receiver,            sms[1].receiver);
-      is(message.body,                sms[1].body);
-      is(message.timestamp.getTime(), sms[1].timestamp);
-      // TODO check the rest of the message list
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      auxListId = listId;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId, message) {
+      ok(!secondMessage);
+      is(requestId, fakeRequestId);
+      checkSentSms(message, 1);
+      secondMessage = true;
+      gSmsDatabaseService.getNextMessageInList(auxListId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      ok(secondMessage);
+      is(requestId, fakeRequestId);
       run_next_test();
-    },
+    }
   });
   let filter = new MozSmsFilter();
-  filter.delivery = "sent";
-  gSmsDatabaseService.createMessageList(filter, false, 24);
+  filter.numbers = [sms[0].sender, sms[1].receiver];
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
 });
 
-add_test(function test_createMessageList_delivery_received_filter() {
+add_test(function test_createMessageList_multi_number_wrong_number_success() {
+  info("test_createMessageList_multi_number_wrong_number_success");
+  let fakeRequestId = newRandomId();
+  let firstMessage = false;
+  fakeSmsRequestManager({
+    notifyCreateMessageList: function notifyCreateMessageList(requestId,
+                                                              listId,
+                                                              message) {
+      is(firstMessage, false);
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      firstMessage = true;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      ok(firstMessage);
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.numbers = ["00000000000", sms[0].sender];
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+add_test(function test_createMessageList_multi_number_failed() {
+  info("test_createMessageList_multi_number_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+     notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  let filter = new MozSmsFilter();
+  filter.numbers = ["00000000000", "11111111111"];
+  gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+});
+
+
+add_test(function test_createMessageList_read_filter_success() {
+  info("test_createMessageList_read_filter_success");
+  let fakeRequestId = newRandomId();
+  let lId;
+  let secondMessage = false;
   fakeSmsRequestManager({
     notifyCreateMessageList: function notifyCreateMessageList(requestId,
                                                               listId,
                                                               message) {
-      is(requestId, 24);
-      is(message.sender,              sms[0].sender);
-      is(message.receiver,            null);
-      is(message.body,                sms[0].body);
-      is(message.timestamp.getTime(), sms[0].timestamp);
-      // TODO check the rest of the message list
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 0);
+      lId = listId;
+      gSmsDatabaseService.getNextMessageInList(listId, requestId);
+    },
+    notifyGotNextMessage: function notifyGotNextMessage(requestId, message) {
+      ok(!secondMessage);
+      is(requestId, fakeRequestId);
+      checkReceivedSms(message, 2);
+      secondMessage = true;
+      gSmsDatabaseService.getNextMessageInList(lId, requestId);
+    },
+    notifyNoMessageInList: function notifyNoMessageInList(requestId) {
+      ok(secondMessage);
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  SimpleTest.executeSoon(function () {
+    let filter = new MozSmsFilter();
+    filter.read = false;
+    gSmsDatabaseService.createMessageList(filter, false, fakeRequestId);
+  });
+});
+
+/**
+ * nsISmsDatabaseService.getNextMessageInList
+ */
+add_test(function test_getNextMessageInList_unknown_list() {
+  info("test_getNextMessageInList_unknown_list");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyReadMessageListFailed:
+      function notifyReadMessageListFailed(requestId) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  gSmsDatabaseService.getNextMessageInList(-1, fakeRequestId);
+});
+
+/**
+ * nsISmsDatabaseService.deleteMessage
+ */
+add_test(function test_deleteMessage_success() {
+  info("test_deleteMessage_success");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifySmsDeleted: function notifySmsDeleted(requestId, deleted) {
+      is(requestId, fakeRequestId);
+      ok(deleted);
       run_next_test();
-    },
+    }
+  });
+  let messageId = gSmsDatabaseService.saveReceivedMessage(sms[0].sender,
+                                                          sms[0].body,
+                                                          sms[0].timestamp);
+  SimpleTest.executeSoon(function () {
+    gSmsDatabaseService.deleteMessage(messageId, fakeRequestId);
+  });
+});
+
+add_test(function test_deleteMessage_failed() {
+  info("test_deleteMessage_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifySmsDeleted: function notifySmsDeleted(requestId, deleted) {
+      is(requestId, fakeRequestId);
+      is(deleted, false);
+      run_next_test();
+    }
   });
-  let filter = new MozSmsFilter();
-  filter.delivery = "received";
-  gSmsDatabaseService.createMessageList(filter, false, 24);
+  gSmsDatabaseService.deleteMessage(-1, fakeRequestId);
+});
+
+add_test(function test_markMessageRead_success() {
+  info("test_markMessageRead_success");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyMarkedMessageRead: function notifyMarkedMessageRead(requestId, read) {
+      is(requestId, fakeRequestId);
+      is(read, true);
+      run_next_test();
+    }
+  });
+  let messageId = gSmsDatabaseService.saveReceivedMessage(sms[2].sender,
+                                                          sms[2].body,
+                                                          sms[2].timestamp); 
+  SimpleTest.executeSoon(function () {
+    gSmsDatabaseService.markMessageRead(messageId, true, fakeRequestId);
+  });
+});
+
+add_test(function test_markMessageRead_failed() {
+  info("test_markMessageRead_failed");
+  let fakeRequestId = newRandomId();
+  fakeSmsRequestManager({
+    notifyMarkMessageReadFailed: function notifyMarkMessageReadFailed(requestId, error) {
+      is(requestId, fakeRequestId);
+      run_next_test();
+    }
+  });
+  SimpleTest.executeSoon(function () {
+    gSmsDatabaseService.markMessageRead(-1, true, fakeRequestId);
+  });
 });
 
 ]]></script>
   <body xmlns="http://www.w3.org/1999/xhtml">
     <p id="display"></p>
     <div id="content" style="display: none"></div>
     <pre id="test"></pre>
   </body>
--- a/dom/sms/tests/test_smsservice_createsmsmessage.js
+++ b/dom/sms/tests/test_smsservice_createsmsmessage.js
@@ -27,32 +27,33 @@ function newMessage() {
 function run_test() {
   run_next_test();
 }
 
 /**
  * Ensure an SmsMessage object created has sensible initial values.
  */
 add_test(function test_interface() {
-  let sms = newMessage(null, "sent", null, null, null, new Date());
+  let sms = newMessage(null, "sent", null, null, null, new Date(), true);
   do_check_true(sms instanceof Ci.nsIDOMMozSmsMessage);
   do_check_eq(sms.id, 0);
   do_check_eq(sms.delivery, "sent");
   do_check_eq(sms.receiver, null);
   do_check_eq(sms.sender, null);
   do_check_eq(sms.body, null);
   do_check_true(sms.timestamp instanceof Date);
+  do_check_true(sms.read);
   run_next_test();
 });
 
 /**
  * Verify that attributes are read-only.
  */
 add_test(function test_readonly_attributes() {
-  let sms = newMessage(null, "received", null, null, null, new Date());
+  let sms = newMessage(null, "received", null, null, null, new Date(), true);
 
   sms.id = 1;
   do_check_eq(sms.id, 0);
 
   sms.delivery = "sent";
   do_check_eq(sms.delivery, "received");
 
   sms.receiver = "a receiver";
@@ -63,103 +64,113 @@ add_test(function test_readonly_attribut
 
   sms.body = "a body";
   do_check_eq(sms.body, null);
 
   let oldTimestamp = sms.timestamp.getTime();
   sms.timestamp = new Date();
   do_check_eq(sms.timestamp.getTime(), oldTimestamp);
 
+  sms.read = false;
+  do_check_true(sms.read);
+
   run_next_test();
 });
 
 /**
  * Test supplying the timestamp as a number of milliseconds.
  */
 add_test(function test_timestamp_number() {
   let ts = Date.now();
-  let sms = newMessage(42, "sent", "the sender", "the receiver", "the body", ts);
+  let sms = newMessage(42, "sent", "the sender", "the receiver", "the body", ts,
+                       true);
   do_check_eq(sms.id, 42);
   do_check_eq(sms.delivery, "sent");
   do_check_eq(sms.sender, "the sender");
   do_check_eq(sms.receiver, "the receiver");
   do_check_eq(sms.body, "the body");
   do_check_true(sms.timestamp instanceof Date);
   do_check_eq(sms.timestamp.getTime(), ts);
+  do_check_true(sms.read);
   run_next_test();
 });
 
 /**
  * Test supplying the timestamp as a Date object.
  */
 add_test(function test_timestamp_date() {
   let date = new Date();
-  let sms = newMessage(42, "sent", "the sender", "the receiver", "the body", date);
+  let sms = newMessage(42, "sent", "the sender", "the receiver", "the body",
+                       date, true);
   do_check_eq(sms.id, 42);
   do_check_eq(sms.delivery, "sent");
   do_check_eq(sms.sender, "the sender");
   do_check_eq(sms.receiver, "the receiver");
   do_check_eq(sms.body, "the body");
   do_check_true(sms.timestamp instanceof Date);
   do_check_eq(sms.timestamp.getTime(), date.getTime());
+  do_check_true(sms.read);
   run_next_test();
 });
 
 /**
  * Test that a floating point number for the timestamp is not allowed.
  */
 add_test(function test_invalid_timestamp_float() {
   do_check_throws(function() {
-    newMessage(42, "sent", "the sender", "the receiver", "the body", 3.1415);
+    newMessage(42, "sent", "the sender", "the receiver", "the body", 3.1415,
+               true);
   }, Cr.NS_ERROR_INVALID_ARG);
   run_next_test();
 });
 
 /**
  * Test that a null value for the timestamp is not allowed.
  */
 add_test(function test_invalid_timestamp_null() {
   do_check_throws(function() {
-    newMessage(42, "sent", "the sender", "the receiver", "the body", null);
+    newMessage(42, "sent", "the sender", "the receiver", "the body", null,
+               true);
   }, Cr.NS_ERROR_INVALID_ARG);
   run_next_test();
 });
 
 /**
  * Test that undefined for the timestamp is not allowed.
  */
 add_test(function test_invalid_timestamp_undefined() {
   do_check_throws(function() {
-    newMessage(42, "sent", "the sender", "the receiver", "the body", undefined);
+    newMessage(42, "sent", "the sender", "the receiver", "the body", undefined,
+               true);
   }, Cr.NS_ERROR_INVALID_ARG);
   run_next_test();
 });
 
 /**
  * Test that a random object for the timestamp is not allowed.
  */
 add_test(function test_invalid_timestamp_object() {
   do_check_throws(function() {
-    newMessage(42, "sent", "the sender", "the receiver", "the body", {});
+    newMessage(42, "sent", "the sender", "the receiver", "the body", {}, true);
   }, Cr.NS_ERROR_INVALID_ARG);
   run_next_test();
 });
 
 /**
  * Test that an invalid delivery string is not accepted.
  */
 add_test(function test_invalid_delivery_string() {
   do_check_throws(function() {
     newMessage(42, "this is invalid", "the sender", "the receiver", "the body",
-               new Date());
+               new Date(), true);
   }, Cr.NS_ERROR_INVALID_ARG);
   run_next_test();
 });
 
 /**
  * Test that a number is not accepted for the 'delivery' argument.
  */
 add_test(function test_invalid_delivery_string() {
   do_check_throws(function() {
-    newMessage(42, 1, "the sender", "the receiver", "the body", new Date());
+    newMessage(42, 1, "the sender", "the receiver", "the body", new Date(), true);
   }, Cr.NS_ERROR_INVALID_ARG);
   run_next_test();
 });
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -539,17 +539,18 @@ RadioInterfaceLayer.prototype = {
     let id = gSmsDatabaseService.saveReceivedMessage(message.sender || null,
                                                      message.fullBody || null,
                                                      message.timestamp);
     let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_RECEIVED,
                                            message.sender || null,
                                            message.receiver || null,
                                            message.fullBody || null,
-                                           message.timestamp);
+                                           message.timestamp,
+                                           false);
     Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
   },
 
   /**
    * Local storage for sent SMS messages.
    */
   _sentSmsEnvelopes: null,
   createSmsEnvelope: function createSmsEnvelope(options) {
@@ -575,17 +576,18 @@ RadioInterfaceLayer.prototype = {
     let id = gSmsDatabaseService.saveSentMessage(options.number,
                                                  options.fullBody,
                                                  timestamp);
     let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_SENT,
                                            null,
                                            options.number,
                                            options.fullBody,
-                                           timestamp);
+                                           timestamp,
+                                           true);
 
     if (!options.requestStatusReport) {
       // No more used if STATUS-REPORT not requested.
       delete this._sentSmsEnvelopes[message.envelopeId];
     } else {
       options.sms = sms;
     }
 
--- a/dom/system/nsDeviceSensors.cpp
+++ b/dom/system/nsDeviceSensors.cpp
@@ -298,17 +298,16 @@ nsDeviceSensors::FireDOMUserProximityEve
   nsCOMPtr<nsIDOMUserProximityEvent> pe = do_QueryInterface(event);
 
   pe->InitUserProximityEvent(NS_LITERAL_STRING("userproximity"),
                              true,
                              false,
                              aNear);
 
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
-  privateEvent = do_QueryInterface(event);
   if (privateEvent) {
     privateEvent->SetTrusted(true);
   }
   bool defaultActionEnabled;
   aTarget->DispatchEvent(event, &defaultActionEnabled);
 }
 
 void
--- a/gfx/cairo/libpixman/src/pixman-fast-path.c
+++ b/gfx/cairo/libpixman/src/pixman-fast-path.c
@@ -1250,17 +1250,17 @@ scaled_bilinear_scanline_8888_8888_OVER 
 				      bl, br,
 				      interpolation_coord(vx),
 				      wb >> (8 - INTERPOLATION_PRECISION_BITS));
 	vx += unit_x;
 	*dst++ = over (src, d);
     }
 }
 
-#if 1
+#ifndef LOWER_QUALITY_INTERPOLATION
 
 static force_inline void
 scaled_bilinear_scanline_565_565_SRC (uint16_t *       dst,
 				      const uint32_t * mask,
 				      const uint16_t * src_top,
 				      const uint16_t * src_bottom,
 				      int32_t          w,
 				      int              wt,
@@ -1285,49 +1285,57 @@ scaled_bilinear_scanline_565_565_SRC (ui
 				   wb >> (8 - INTERPOLATION_PRECISION_BITS));
 	vx += unit_x;
 	*dst++ = CONVERT_8888_TO_0565(d);
     }
 }
 
 #else
 
-#define SK_G16_MASK_IN_PLACE 0xfc0
+/* This is a clever low resolution bilinear interpolation inspired by the code
+   in Skia */
+
+/* This takes the green component from the 565 representation and moves it:
+   00000000 00000000 rrrrrggg gggbbbbb
+
+   00000ggg ggg00000 rrrrr000 000bbbbb
 
-static inline uint32_t SkExpand_rgb_16(uint16_t c) {
+   This gives us 5 extra bits of space before each component to let us do
+   SWAR style optimizations
+*/
 
-    return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & ~SK_G16_MASK_IN_PLACE);
+#define GREEN_MASK (((1 << 6) - 1) << 5)
+
+static inline uint32_t
+expand_rgb_565 (uint16_t c) {
+    return ((c & GREEN_MASK) << 16) | (c & ~GREEN_MASK);
 }
 
-/** Compress an expanded value (from SkExpand_rgb_16) back down to a 16bit
-    color value. The computation yields only 16bits of valid data, but we claim
-    to return 32bits, so that the compiler won't generate extra instructions to
-    "clean" the top 16bits. However, the top 16 can contain garbage, so it is
-    up to the caller to safely ignore them.
-*/
-static inline uint16_t SkCompact_rgb_16(uint32_t c) {
-    return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & ~SK_G16_MASK_IN_PLACE);
-}
-// returns expanded * 5bits
-static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y,
-                                           uint32_t a00, uint32_t a01,
-                                           uint32_t a10, uint32_t a11) {
-    a00 = SkExpand_rgb_16(a00);
-    a01 = SkExpand_rgb_16(a01);
-    a10 = SkExpand_rgb_16(a10);
-    a11 = SkExpand_rgb_16(a11);
-    
-    int xy = x * y >> 3;
-    return  a00 * (32 - 2*y - 2*x + xy) +
-            a01 * (2*x - xy) +
-            a10 * (2*y - xy) +
-            a11 * xy;
+static inline uint16_t
+compact_rgb_565 (uint32_t c) {
+    return ((c >> 16) & GREEN_MASK) | (c & ~GREEN_MASK);
 }
 
+static inline uint16_t
+bilinear_interpolation_565(uint16_t tl, uint16_t tr,
+			   uint16_t bl, uint16_t br,
+			   int x, int y)
+{
+    int xy;
+    uint32_t a00 = expand_rgb_565 (tl);
+    uint32_t a01 = expand_rgb_565 (tr);
+    uint32_t a10 = expand_rgb_565 (bl);
+    uint32_t a11 = expand_rgb_565 (br);
 
+    xy = (x * y) >> 3;
+    return compact_rgb_565 ((a00 * (32 - 2*y - 2*x + xy) +
+			     a01 * (2*x - xy) +
+			     a10 * (2*y - xy) +
+			     a11 * xy) >> 5);
+}
 
 static force_inline void
 scaled_bilinear_scanline_565_565_SRC (uint16_t *       dst,
 				      const uint32_t * mask,
 				      const uint16_t * src_top,
 				      const uint16_t * src_bottom,
 				      int32_t          w,
 				      int              wt,
@@ -1339,24 +1347,24 @@ scaled_bilinear_scanline_565_565_SRC (ui
 {
     while ((w -= 1) >= 0)
     {
 	uint16_t tl = src_top [pixman_fixed_to_int (vx)];
 	uint16_t tr = src_top [pixman_fixed_to_int (vx) + 1];
 	uint16_t bl = src_bottom [pixman_fixed_to_int (vx)];
 	uint16_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
 
-        uint32_t tmp = Filter_565_Expanded((vx>>12)&0xf, wb>>4, tl, tr, bl, br);
+        uint16_t d = bilinear_interpolation_565 (tl, tr, bl, br, (vx >> 12) & 0xf, wb >> 4);
         vx += unit_x;
-        *dst++ = SkCompact_rgb_16((tmp) >> 5);
+        *dst++ = d;
     }
 }
 
+#endif
 
-#endif
 FAST_BILINEAR_MAINLOOP_COMMON (565_565_cover_SRC,
 			       scaled_bilinear_scanline_565_565_SRC,
 			       uint16_t, uint32_t, uint16_t,
 			       COVER, FLAG_NONE)
 FAST_BILINEAR_MAINLOOP_COMMON (565_565_pad_SRC,
 			       scaled_bilinear_scanline_565_565_SRC,
 			       uint16_t, uint32_t, uint16_t,
 			       PAD, FLAG_NONE)
--- a/gfx/cairo/libpixman/src/pixman-inlines.h
+++ b/gfx/cairo/libpixman/src/pixman-inlines.h
@@ -78,16 +78,17 @@ repeat (pixman_repeat_t repeat, int *c, 
 	if (*c >= size)
 	    *c = size * 2 - *c - 1;
     }
     return TRUE;
 }
 
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
 #define LOW_QUALITY_INTERPOLATION
+#define LOWER_QUALITY_INTERPOLATION
 #endif
 
 #ifdef LOW_QUALITY_INTERPOLATION
 #define INTERPOLATION_PRECISION_BITS 4
 #else
 #define INTERPOLATION_PRECISION_BITS 8
 #endif
 static force_inline int32_t
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -102,18 +102,24 @@ public:
   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
 
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
   const nsIntRegion& GetLastPaintRegion() const { return mLastPaintRegion; }
   void SetLastPaintRegion(const nsIntRegion& aLastPaintRegion) {
     mLastPaintRegion = aLastPaintRegion;
   }
 
+  // Given a position i, this function returns the position inside the current tile.
+  int GetTileStart(int i) const {
+    return (i >= 0) ? (i % GetTileLength())
+                    : ((GetTileLength() - (-i % GetTileLength())) % GetTileLength());
+  }
+
   // Rounds the given coordinate down to the nearest tile boundary.
-  int RoundDownToTileEdge(int aX) const { return aX - aX % GetTileLength(); }
+  int RoundDownToTileEdge(int aX) const { return aX - GetTileStart(aX); }
 
 protected:
   // The implementor should call Update() to change
   // the new valid region. This implementation will call
   // validateTile on each tile that is dirty, which is left
   // to the implementor.
   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
 
@@ -149,43 +155,61 @@ public:
    * Update the current retained layer with the updated layer data.
    * The BasicTiledLayerBuffer is expected to be in the ReadLock state
    * prior to this being called. aTiledBuffer is copy constructed and
    * is retained until it has been uploaded/copyed and unlocked.
    */
   virtual void PaintedTiledLayerBuffer(const BasicTiledLayerBuffer* aTiledBuffer) = 0;
 };
 
+// Normal integer division truncates towards zero,
+// we instead want to floor to hangle negative numbers.
+static int floor_div(int a, int b)
+{
+  int rem = a % b;
+  int div = a/b;
+  if (rem == 0) {
+    return div;
+  } else {
+    // If the signs are different substract 1.
+    int sub;
+    sub = a ^ b;
+    // The results of this shift is either 0 or -1.
+    sub >>= 8*sizeof(int)-1;
+    return div+sub;
+  }
+}
+
 template<typename Derived, typename Tile> Tile
 TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin) const
 {
   // TODO Cache firstTileOriginX/firstTileOriginY
   // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   // origin, the difference is the tile x/y relative to the start of the tile buffer.
-  int firstTileX = mValidRegion.GetBounds().x / GetTileLength();
-  int firstTileY = mValidRegion.GetBounds().y / GetTileLength();
-  return GetTile(aTileOrigin.x / GetTileLength() - firstTileX,
-                 aTileOrigin.y / GetTileLength() - firstTileY);
+  int firstTileX = floor_div(mValidRegion.GetBounds().x, GetTileLength());
+  int firstTileY = floor_div(mValidRegion.GetBounds().y, GetTileLength());
+  return GetTile(floor_div(aTileOrigin.x, GetTileLength()) - firstTileX,
+                 floor_div(aTileOrigin.y, GetTileLength()) - firstTileY);
 }
 
 template<typename Derived, typename Tile> Tile
 TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y) const
 {
   int index = x * mRetainedHeight + y;
   return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
 }
 
 template<typename Derived, typename Tile> bool
 TiledLayerBuffer<Derived, Tile>::RemoveTile(const nsIntPoint& aTileOrigin,
                                             Tile& aRemovedTile)
 {
-  int firstTileX = mValidRegion.GetBounds().x / GetTileLength();
-  int firstTileY = mValidRegion.GetBounds().y / GetTileLength();
-  return RemoveTile(aTileOrigin.x / GetTileLength() - firstTileX,
-                    aTileOrigin.y / GetTileLength() - firstTileY,
+  int firstTileX = floor_div(mValidRegion.GetBounds().x, GetTileLength());
+  int firstTileY = floor_div(mValidRegion.GetBounds().y, GetTileLength());
+  return RemoveTile(floor_div(aTileOrigin.x, GetTileLength()) - firstTileX,
+                    floor_div(aTileOrigin.y, GetTileLength()) - firstTileY,
                     aRemovedTile);
 }
 
 template<typename Derived, typename Tile> bool
 TiledLayerBuffer<Derived, Tile>::RemoveTile(int x, int y, Tile& aRemovedTile)
 {
   int index = x * mRetainedHeight + y;
   const Tile& tileToRemove = mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
@@ -216,38 +240,38 @@ TiledLayerBuffer<Derived, Tile>::Update(
   // Pass 1: Recycle valid content from the old buffer
   // Recycle tiles from the old buffer that contain valid regions.
   // Insert placeholders tiles if we have no valid area for that tile
   // which we will allocate in pass 2.
   // TODO: Add a tile pool to reduce new allocation
   int tileX = 0;
   int tileY;
   // Iterate over the new drawing bounds in steps of tiles.
-  for (int x = newBound.x; x < newBound.XMost(); tileX++) {
+  for (int32_t x = newBound.x; x < newBound.XMost(); tileX++) {
     // Compute tileRect(x,y,width,height) in layer space coordinate
     // giving us the rect of the tile that hits the newBounds.
-    int width = GetTileLength() - x % GetTileLength();
+    int width = GetTileLength() - GetTileStart(x);
     if (x + width > newBound.XMost()) {
       width = newBound.x + newBound.width - x;
     }
 
     tileY = 0;
-    for (int y = newBound.y; y < newBound.YMost(); tileY++) {
-      int height = GetTileLength() - y % GetTileLength();
+    for (int32_t y = newBound.y; y < newBound.YMost(); tileY++) {
+      int height = GetTileLength() - GetTileStart(y);
       if (y + height > newBound.y + newBound.height) {
         height = newBound.y + newBound.height - y;
       }
 
       const nsIntRect tileRect(x,y,width,height);
       if (oldValidRegion.Intersects(tileRect) && newValidRegion.Intersects(tileRect)) {
         // This old tiles contains some valid area so move it to the new tile
         // buffer. Replace the tile in the old buffer with a placeholder
         // to leave the old buffer index unaffected.
-        int tileX = (x - oldBufferOrigin.x) / GetTileLength();
-        int tileY = (y - oldBufferOrigin.y) / GetTileLength();
+        int tileX = floor_div(x - oldBufferOrigin.x, GetTileLength());
+        int tileY = floor_div(y - oldBufferOrigin.y, GetTileLength());
         int index = tileX * oldRetainedHeight + tileY;
 
         // The tile may have been removed, skip over it in this case.
         if (IsPlaceholder(oldRetainedTiles.
                           SafeElementAt(index, AsDerived().GetPlaceholderTile()))) {
           newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
         } else {
           Tile tileWithPartialValidContent = oldRetainedTiles[index];
@@ -289,71 +313,77 @@ TiledLayerBuffer<Derived, Tile>::Update(
   // Pass 2: Validate
   // We know at this point that any tile in the new buffer that had valid content
   // from the previous buffer is placed correctly in the new buffer.
   // We know that any tile in the old buffer that isn't a place holder is
   // of no use and can be recycled.
   // We also know that any place holder tile in the new buffer must be
   // allocated.
   tileX = 0;
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  printf_stderr("Update %i, %i, %i, %i\n", newBound.x, newBound.y, newBound.width, newBound.height);
+#endif
   for (int x = newBound.x; x < newBound.x + newBound.width; tileX++) {
     // Compute tileRect(x,y,width,height) in layer space coordinate
     // giving us the rect of the tile that hits the newBounds.
     int tileStartX = RoundDownToTileEdge(x);
-    int width = GetTileLength() - x % GetTileLength();
+    int width = GetTileLength() - GetTileStart(x);
     if (x + width > newBound.XMost())
       width = newBound.XMost() - x;
 
     tileY = 0;
     for (int y = newBound.y; y < newBound.y + newBound.height; tileY++) {
       int tileStartY = RoundDownToTileEdge(y);
-      int height = GetTileLength() - y % GetTileLength();
+      int height = GetTileLength() - GetTileStart(y);
       if (y + height > newBound.YMost()) {
         height = newBound.YMost() - y;
       }
 
       const nsIntRect tileRect(x, y, width, height);
 
       nsIntRegion tileDrawRegion;
       tileDrawRegion.And(tileRect, regionToPaint);
 
       if (tileDrawRegion.IsEmpty()) {
         // We have a tile but it doesn't hit the draw region
         // because we can reuse all of the content from the
         // previous buffer.
 #ifdef DEBUG
-        int currTileX = (x - newBufferOrigin.x) / GetTileLength();
-        int currTileY = (y - newBufferOrigin.y) / GetTileLength();
+        int currTileX = floor_div(x - newBufferOrigin.x, GetTileLength());
+        int currTileY = floor_div(y - newBufferOrigin.y, GetTileLength());
         int index = currTileX * mRetainedHeight + currTileY;
         NS_ABORT_IF_FALSE(!newValidRegion.Intersects(tileRect) ||
                           !IsPlaceholder(newRetainedTiles.
                                          SafeElementAt(index, AsDerived().GetPlaceholderTile())),
                           "If we don't draw a tile we shouldn't have a placeholder there.");
 #endif
         y += height;
         continue;
       }
 
-      int tileX = (x - newBufferOrigin.x) / GetTileLength();
-      int tileY = (y - newBufferOrigin.y) / GetTileLength();
+      int tileX = floor_div(x - newBufferOrigin.x, GetTileLength());
+      int tileY = floor_div(y - newBufferOrigin.y, GetTileLength());
       int index = tileX * mRetainedHeight + tileY;
       NS_ABORT_IF_FALSE(index >= 0 && index < newRetainedTiles.Length(), "index out of range");
       Tile newTile = newRetainedTiles[index];
       while (IsPlaceholder(newTile) && oldRetainedTiles.Length() > 0) {
         AsDerived().SwapTiles(newTile, oldRetainedTiles[oldRetainedTiles.Length()-1]);
         oldRetainedTiles.RemoveElementAt(oldRetainedTiles.Length()-1);
       }
 
       // We've done our best effort to recycle a tile but it can be null
       // in which case it's up to the derived class's ValidateTile()
       // implementation to allocate a new tile before drawing
       nsIntPoint tileOrigin(tileStartX, tileStartY);
       newTile = AsDerived().ValidateTile(newTile, nsIntPoint(tileStartX, tileStartY),
                                          tileDrawRegion);
       NS_ABORT_IF_FALSE(!IsPlaceholder(newTile), "index out of range");
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+      printf_stderr("Store Validate tile %i, %i -> %i\n", tileStartX, tileStartY, index);
+#endif
       newRetainedTiles[index] = newTile;
 
       y += height;
     }
 
     x += width;
   }
 
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=2 et tw=80 : */
 /* ***** 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/
@@ -66,16 +66,17 @@ CompositorParent::CompositorParent(nsIWi
   , mXScale(1.0)
   , mYScale(1.0)
   , mIsFirstPaint(false)
   , mLayersUpdated(false)
   , mCompositorLoop(aMsgLoop)
   , mThreadID(aThreadID)
   , mRenderToEGLSurface(aRenderToEGLSurface)
   , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight)
+  , mPauseCompositionMonitor("PauseCompositionMonitor")
 {
   MOZ_COUNT_CTOR(CompositorParent);
 }
 
 MessageLoop*
 CompositorParent::CompositorLoop()
 {
   return mCompositorLoop;
@@ -141,23 +142,29 @@ CompositorParent::ScheduleRenderOnCompos
   CompositorLoop()->PostTask(FROM_HERE, renderTask);
 }
 
 void
 CompositorParent::PauseComposition()
 {
   NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(),
                     "PauseComposition() can only be called on the compositor thread");
+
+  mozilla::MonitorAutoLock lock(mPauseCompositionMonitor);
+
   if (!mPaused) {
     mPaused = true;
 
 #ifdef MOZ_WIDGET_ANDROID
     static_cast<LayerManagerOGL*>(mLayerManager.get())->gl()->ReleaseSurface();
 #endif
   }
+
+  // if anyone's waiting to make sure that composition really got paused, tell them
+  lock.NotifyAll();
 }
 
 void
 CompositorParent::ResumeComposition()
 {
   NS_ABORT_IF_FALSE(CompositorThreadID() == PlatformThread::CurrentId(),
                     "ResumeComposition() can only be called on the compositor thread");
   mPaused = false;
@@ -179,22 +186,31 @@ CompositorParent::SetEGLSurfaceSize(int 
 
 void
 CompositorParent::ResumeCompositionAndResize(int width, int height)
 {
   SetEGLSurfaceSize(width, height);
   ResumeComposition();
 }
 
+/*
+ * This will execute a pause synchronously, waiting to make sure that the compositor
+ * really is paused.
+ */
 void
 CompositorParent::SchedulePauseOnCompositorThread()
 {
+  mozilla::MonitorAutoLock lock(mPauseCompositionMonitor);
+
   CancelableTask *pauseTask = NewRunnableMethod(this,
                                                 &CompositorParent::PauseComposition);
   CompositorLoop()->PostTask(FROM_HERE, pauseTask);
+
+  // Wait until the pause has actually been processed by the compositor thread
+  lock.Wait();
 }
 
 void
 CompositorParent::ScheduleResumeOnCompositorThread(int width, int height)
 {
   CancelableTask *resumeTask =
     NewRunnableMethod(this, &CompositorParent::ResumeCompositionAndResize, width, height);
   CompositorLoop()->PostTask(FROM_HERE, resumeTask);
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=4 ts=8 et tw=80 : */
 /* ***** 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/
@@ -47,16 +47,17 @@
 //    1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
 //    2) Unless a frame was composited within the throttle threshold in
 //       which the deadline will be 15ms + throttle threshold
 #define COMPOSITOR_PERFORMANCE_WARNING
 
 #include "mozilla/layers/PCompositorParent.h"
 #include "mozilla/layers/PLayersParent.h"
 #include "base/thread.h"
+#include "mozilla/Monitor.h"
 #include "ShadowLayersManager.h"
 
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
 class LayerManager;
@@ -165,15 +166,17 @@ private:
   // after a layers update has it set. It is cleared after that first composition.
   bool mLayersUpdated;
 
   MessageLoop* mCompositorLoop;
   PlatformThreadId mThreadID;
   bool mRenderToEGLSurface;
   nsIntSize mEGLSurfaceSize;
 
+  mozilla::Monitor mPauseCompositionMonitor;
+
   DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
 };
 
 } // layers
 } // mozilla
 
 #endif // mozilla_layers_CompositorParent_h
--- a/gfx/layers/opengl/ReusableTileStoreOGL.cpp
+++ b/gfx/layers/opengl/ReusableTileStoreOGL.cpp
@@ -120,22 +120,22 @@ ReusableTileStoreOGL::HarvestTiles(Tiled
   // We harvest any tile that is entirely outside of the new valid region, or
   // any tile that is partially outside of the valid region and whose
   // resolution has changed.
   // XXX Tile iteration needs to be abstracted, or have some utility functions
   //     to make it simpler.
   uint16_t tileSize = aVideoMemoryTiledBuffer->GetTileLength();
   nsIntRect validBounds = aOldValidRegion.GetBounds();
   for (int x = validBounds.x; x < validBounds.XMost();) {
-    int w = tileSize - x % tileSize;
+    int w = tileSize - aVideoMemoryTiledBuffer->GetTileStart(x);
     if (x + w > validBounds.x + validBounds.width)
       w = validBounds.x + validBounds.width - x;
 
     for (int y = validBounds.y; y < validBounds.YMost();) {
-      int h = tileSize - y % tileSize;
+      int h = tileSize - aVideoMemoryTiledBuffer->GetTileStart(y);
       if (y + h > validBounds.y + validBounds.height)
         h = validBounds.y + validBounds.height - y;
 
       // If the new valid region doesn't contain this tile region,
       // harvest the tile.
       nsIntRegion tileRegion;
       tileRegion.And(aOldValidRegion, nsIntRect(x, y, w, h));
 
@@ -268,18 +268,28 @@ ReusableTileStoreOGL::DrawTiles(TiledThe
     }
 
     // If the tile region is empty, skip drawing.
     if (tileRegion.IsEmpty())
       continue;
 
     // XXX If we have multiple tiles covering the same area, we will
     //     end up with rendering artifacts if the aLayer isn't opaque.
-    uint16_t tileStartX = tile->mTileOrigin.x % tile->mTileSize;
-    uint16_t tileStartY = tile->mTileOrigin.y % tile->mTileSize;
+    int32_t tileStartX;
+    int32_t tileStartY;
+    if (tile->mTileOrigin.x >= 0) {
+      tileStartX = tile->mTileOrigin.x % tile->mTileSize;
+    } else {
+      tileStartX = (tile->mTileSize - (-tile->mTileOrigin.x % tile->mTileSize)) % tile->mTileSize;
+    }
+    if (tile->mTileOrigin.y >= 0) {
+      tileStartY = tile->mTileOrigin.y % tile->mTileSize;
+    } else {
+      tileStartY = (tile->mTileSize - (-tile->mTileOrigin.y % tile->mTileSize)) % tile->mTileSize;
+    }
     nsIntPoint tileOffset(tile->mTileOrigin.x - tileStartX, tile->mTileOrigin.y - tileStartY);
     nsIntSize textureSize(tile->mTileSize, tile->mTileSize);
     aLayer->RenderTile(tile->mTexture, transform, aRenderOffset, tileRegion, tileOffset, textureSize, aMaskLayer);
   }
 }
 
 } // mozilla
 } // layers
--- a/gfx/layers/opengl/TiledThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/TiledThebesLayerOGL.cpp
@@ -232,28 +232,28 @@ TiledThebesLayerOGL::RenderLayer(int aPr
                                   mVideoMemoryTiledBuffer.GetResolution(),
                                   GetEffectiveTransform(), aOffset, maskLayer);
   }
 
   // Render valid tiles.
   const nsIntRegion& visibleRegion = GetEffectiveVisibleRegion();
   const nsIntRect visibleRect = visibleRegion.GetBounds();
 
-  unsigned int rowCount = 0;
-  int tileX = 0;
-  for (size_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
+  uint32_t rowCount = 0;
+  uint32_t tileX = 0;
+  for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
     rowCount++;
-    uint16_t tileStartX = x % mVideoMemoryTiledBuffer.GetTileLength();
-    uint16_t w = mVideoMemoryTiledBuffer.GetTileLength() - tileStartX;
+    int32_t tileStartX = mVideoMemoryTiledBuffer.GetTileStart(x);
+    int16_t w = mVideoMemoryTiledBuffer.GetTileLength() - tileStartX;
     if (x + w > visibleRect.x + visibleRect.width)
       w = visibleRect.x + visibleRect.width - x;
     int tileY = 0;
-    for (size_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
-      uint16_t tileStartY = y % mVideoMemoryTiledBuffer.GetTileLength();
-      uint16_t h = mVideoMemoryTiledBuffer.GetTileLength() - tileStartY;
+    for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
+      int32_t tileStartY = mVideoMemoryTiledBuffer.GetTileStart(y);
+      int16_t h = mVideoMemoryTiledBuffer.GetTileLength() - tileStartY;
       if (y + h > visibleRect.y + visibleRect.height)
         h = visibleRect.y + visibleRect.height - y;
 
       TiledTexture tileTexture = mVideoMemoryTiledBuffer.
         GetTile(nsIntPoint(mVideoMemoryTiledBuffer.RoundDownToTileEdge(x),
                            mVideoMemoryTiledBuffer.RoundDownToTileEdge(y)));
       if (tileTexture != mVideoMemoryTiledBuffer.GetPlaceholderTile()) {
         nsIntRegion tileDrawRegion = nsIntRegion(nsIntRect(x, y, w, h));
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -311,16 +311,18 @@ namespace CDataFinalizer {
   static void Finalize(JSFreeOp *fop, JSObject *obj);
 
   /*
    * Return the jsval contained by this finalizer.
    *
    * Note that the jsval is actually not recorded, but converted back from C.
    */
   static bool GetValue(JSContext *cx, JSObject *obj, jsval *result);
+
+  static JSObject* GetCData(JSContext *cx, JSObject *obj);
  }
 
 
 // Int64Base provides functions common to Int64 and UInt64.
 namespace Int64Base {
   JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data,
     bool isUnsigned);
 
@@ -503,16 +505,17 @@ static JSFunctionSpec sCDataFunctions[] 
 
 static JSPropertySpec sCDataFinalizerProps[] = {
   { 0, 0, 0, NULL, NULL }
 };
 
 static JSFunctionSpec sCDataFinalizerFunctions[] = {
   JS_FN("dispose",  CDataFinalizer::Methods::Dispose,  0, CDATAFINALIZERFN_FLAGS),
   JS_FN("forget",   CDataFinalizer::Methods::Forget,   0, CDATAFINALIZERFN_FLAGS),
+  JS_FN("readString",CData::ReadString, 0, CDATAFINALIZERFN_FLAGS),
   JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
   JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
   JS_FS_END
 };
 
 static JSFunctionSpec sPointerFunction =
   JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
 
@@ -6280,17 +6283,17 @@ CData::GetRuntime(JSContext* cx, unsigne
 JSBool
 CData::ReadString(JSContext* cx, unsigned argc, jsval* vp)
 {
   if (argc != 0) {
     JS_ReportError(cx, "readString takes zero arguments");
     return JS_FALSE;
   }
 
-  JSObject* obj = JS_THIS_OBJECT(cx, vp);
+  JSObject* obj = CDataFinalizer::GetCData(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return JS_FALSE;
   if (!IsCData(obj)) {
     JS_ReportError(cx, "not a CData");
     return JS_FALSE;
   }
 
   // Make sure we are a pointer to, or an array of, an 8-bit or 16-bit
@@ -6557,16 +6560,38 @@ CDataFinalizer::GetCType(JSContext *cx, 
                                      SLOT_DATAFINALIZER_VALTYPE);
   if (JSVAL_IS_VOID(valData)) {
     return NULL;
   }
 
   return JSVAL_TO_OBJECT(valData);
 }
 
+JSObject*
+CDataFinalizer::GetCData(JSContext *cx, JSObject *obj)
+{
+  if (!obj) {
+    JS_ReportError(cx, "No C data");
+    return NULL;
+  }
+  if (CData::IsCData(obj)) {
+    return obj;
+  }
+  if (!CDataFinalizer::IsCDataFinalizer(obj)) {
+    JS_ReportError(cx, "Not C data");
+    return NULL;
+  }
+  jsval val;
+  if (!CDataFinalizer::GetValue(cx, obj, &val) || JSVAL_IS_PRIMITIVE(val)) {
+    JS_ReportError(cx, "Empty CDataFinalizer");
+    return NULL;
+  }
+  return JSVAL_TO_OBJECT(val);
+}
+
 bool
 CDataFinalizer::GetValue(JSContext *cx, JSObject *obj, jsval *aResult)
 {
   MOZ_ASSERT(IsCDataFinalizer(obj));
 
   CDataFinalizer::Private *p = (CDataFinalizer::Private *)
     JS_GetPrivate(obj);
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2297,27 +2297,32 @@ nsGfxScrollFrameInner::BuildDisplayList(
     dirtyRect = displayPort;
   }
 
   nsDisplayListCollection set;
   rv = mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, set);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Since making new layers is expensive, only use nsDisplayScrollLayer
-  // if the area is scrollable and there's a displayport (or we're the content
-  // process).
-  nsRect scrollRange = GetScrollRange();
-  ScrollbarStyles styles = GetScrollbarStylesFromFrame();
-  mShouldBuildLayer =
-     (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN ||
-      styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
-     (usingDisplayport ||
-      (XRE_GetProcessType() == GeckoProcessType_Content &&
-       (scrollRange.width > 0 || scrollRange.height > 0) &&
-       (!mIsRoot || !mOuter->PresContext()->IsRootContentDocument())));
+  // if the area is scrollable and we're the content process.
+  // When a displayport is being used, force building of a layer so that
+  // CompositorParent can always find the scrollable layer for the root content
+  // document.
+  if (usingDisplayport) {
+    mShouldBuildLayer = true;
+  } else {
+    nsRect scrollRange = GetScrollRange();
+    ScrollbarStyles styles = GetScrollbarStylesFromFrame();
+    mShouldBuildLayer =
+       (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN ||
+        styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
+       (XRE_GetProcessType() == GeckoProcessType_Content &&
+        (scrollRange.width > 0 || scrollRange.height > 0) &&
+        (!mIsRoot || !mOuter->PresContext()->IsRootContentDocument()));
+  }
 
   nsRect clip;
   nscoord radii[8];
 
   if (usingDisplayport) {
     clip = displayPort + aBuilder->ToReferenceFrame(mOuter);
     memset(radii, 0, sizeof(nscoord) * ArrayLength(radii));
   } else {
new file mode 100644
--- /dev/null
+++ b/layout/mathml/crashtests/716349-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Crashtest bug 716349</title>
+  </head>
+  <body>
+    <math>
+      <mspace width="-10px"/>
+      <mspace height="-10px"/>
+    </math>
+  </body>
+</html>
+
--- a/layout/mathml/crashtests/crashtests.list
+++ b/layout/mathml/crashtests/crashtests.list
@@ -29,16 +29,17 @@ load 372483-1.xhtml
 load 373472-1.xhtml
 load 373472-2.xhtml
 load 375562-1.xhtml
 load 377824-1.xhtml
 load 379418-1.xhtml
 load 385226-1.xhtml
 load 393760-1.xhtml
 load 397518-1.xhtml
+load 398038-1.html
 load 400475-1.xhtml
 load 402400-1.xhtml
 load 403156-1.xhtml
 load 404485-1.xhtml
 load 405187-1.xhtml
 load 405271-1.xml
 load 412237-1.xml
 load 413063-1.xhtml
@@ -47,11 +48,11 @@ load 420420-1.xhtml
 load 431072-1.xhtml
 load 443089-1.xhtml
 load 463763-1.xhtml
 load 463763-2.xhtml
 load 476547-1.xhtml
 load 477740-1.xhtml
 load 541620-1.xhtml
 load 557474-1.html
+load 654928-1.html
 load 655451-1.xhtml
-load 654928-1.html
-load 398038-1.html
+load 716349-1.html
--- a/layout/mathml/nsMathMLmspaceFrame.cpp
+++ b/layout/mathml/nsMathMLmspaceFrame.cpp
@@ -98,64 +98,64 @@ nsMathMLmspaceFrame::ProcessAttributes(n
   // height
   //
   // "Specifies the desired height (above the baseline) of the space."
   //
   // values: length
   // default: 0ex
   //
   // The default value is "0ex", so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bugs 411227, 716349)
+  // We do not allow negative values. See bug 716349.
   //
   mHeight = 0;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::height,
                value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mHeight,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
+    ParseNumericValue(value, &mHeight, 0,
                       aPresContext, mStyleContext);
   }
 
   // depth
   //
   // "Specifies the desired depth (below the baseline) of the space."
   //
   // values: length
   // default: 0ex
   //
   // The default value is "0ex", so unitless values can be ignored.
-  // XXXfredw Should we forbid negative values? (bugs 411227, 716349)
+  // We do not allow negative values. See bug 716349.
   //
   mDepth = 0;
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::depth_,
                value);
   if (!value.IsEmpty()) {
-    ParseNumericValue(value, &mDepth,
-                      nsMathMLElement::PARSE_ALLOW_NEGATIVE,
+    ParseNumericValue(value, &mDepth, 0,
                       aPresContext, mStyleContext);
   }
 }
 
 NS_IMETHODIMP
 nsMathMLmspaceFrame::Reflow(nsPresContext*          aPresContext,
                             nsHTMLReflowMetrics&     aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus&          aStatus)
 {
   ProcessAttributes(aPresContext);
+  // nsLineLayout doesn't expect negative widths.
+  // XXXfredw Negative spaces are not implemented. See bug 717546
 
   mBoundingMetrics = nsBoundingMetrics();
-  mBoundingMetrics.width = mWidth;
+  mBoundingMetrics.width = NS_MAX(0, mWidth);
   mBoundingMetrics.ascent = mHeight;
   mBoundingMetrics.descent = mDepth;
   mBoundingMetrics.leftBearing = 0;
-  mBoundingMetrics.rightBearing = mWidth;
+  mBoundingMetrics.rightBearing = mBoundingMetrics.width;
 
   aDesiredSize.ascent = mHeight;
-  aDesiredSize.width = mWidth;
+  aDesiredSize.width = mBoundingMetrics.width;
   aDesiredSize.height = aDesiredSize.ascent + mDepth;
   // Also return our bounding metrics
   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
 
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return NS_OK;
 }
--- a/mfbt/CheckedInt.h
+++ b/mfbt/CheckedInt.h
@@ -205,41 +205,40 @@ template<typename IntegerType>
 struct PositionOfSignBit
 {
     static const size_t value = CHAR_BIT * sizeof(IntegerType) - 1;
 };
 
 template<typename IntegerType>
 struct MinValue
 {
-    static IntegerType value()
-    {
-      // Bitwise ops may return a larger type, that's why we cast explicitly.
-      // In C++, left bit shifts on signed values is undefined by the standard
-      // unless the shifted value is representable.
-      // Notice that signed-to-unsigned conversions are always well-defined in
-      // the standard as the value congruent to 2**n, as expected. By contrast,
-      // unsigned-to-signed is only well-defined if the value is representable.
-      return IsSigned<IntegerType>::value
-             ? IntegerType(typename UnsignedType<IntegerType>::Type(1)
-                             << PositionOfSignBit<IntegerType>::value)
-             : IntegerType(0);
-    }
+  private:
+    typedef typename UnsignedType<IntegerType>::Type UnsignedIntegerType;
+    static const size_t PosOfSignBit = PositionOfSignBit<IntegerType>::value;
+
+  public:
+    // Bitwise ops may return a larger type, that's why we cast explicitly.
+    // In C++, left bit shifts on signed values is undefined by the standard
+    // unless the shifted value is representable.
+    // Notice that signed-to-unsigned conversions are always well-defined in
+    // the standard as the value congruent to 2**n, as expected. By contrast,
+    // unsigned-to-signed is only well-defined if the value is representable.
+    static const IntegerType value =
+        IsSigned<IntegerType>::value
+        ? IntegerType(UnsignedIntegerType(1) << PosOfSignBit)
+        : IntegerType(0);
 };
 
 template<typename IntegerType>
 struct MaxValue
 {
-    static IntegerType value()
-    {
-      // Tricksy, but covered by the unit test.
-      // Relies heavily on the return type of MinValue<IntegerType>::value()
-      // being IntegerType.
-      return ~MinValue<IntegerType>::value();
-    }
+    // Tricksy, but covered by the unit test.
+    // Relies heavily on the type of MinValue<IntegerType>::value
+    // being IntegerType.
+    static const IntegerType value = ~MinValue<IntegerType>::value;
 };
 
 /*
  * Step 3: Implement the actual validity checks.
  *
  * Ideas taken from IntegerLib, code different.
  */
 
@@ -273,49 +272,46 @@ template<typename T,
          bool IsUSigned = IsSigned<U>::value>
 struct IsInRangeImpl {};
 
 template<typename T, typename U>
 struct IsInRangeImpl<T, U, true, true>
 {
     static bool run(U x)
     {
-      return x <= MaxValue<T>::value() &&
-             x >= MinValue<T>::value();
+      return x <= MaxValue<T>::value && x >= MinValue<T>::value;
     }
 };
 
 template<typename T, typename U>
 struct IsInRangeImpl<T, U, false, false>
 {
     static bool run(U x)
     {
-      return x <= MaxValue<T>::value();
+      return x <= MaxValue<T>::value;
     }
 };
 
 template<typename T, typename U>
 struct IsInRangeImpl<T, U, true, false>
 {
     static bool run(U x)
     {
-      return sizeof(T) > sizeof(U)
-             ? true
-             : x <= U(MaxValue<T>::value());
+      return sizeof(T) > sizeof(U) || x <= U(MaxValue<T>::value);
     }
 };
 
 template<typename T, typename U>
 struct IsInRangeImpl<T, U, false, true>
 {
     static bool run(U x)
     {
       return sizeof(T) >= sizeof(U)
              ? x >= 0
-             : x >= 0 && x <= U(MaxValue<T>::value());
+             : x >= 0 && x <= U(MaxValue<T>::value);
     }
 };
 
 template<typename T, typename U>
 inline bool
 IsInRange(U x)
 {
   return IsInRangeImpl<T, U>::run(x);
@@ -361,18 +357,18 @@ struct IsMulValidImpl<T, IsSigned, true>
     }
 };
 
 template<typename T>
 struct IsMulValidImpl<T, true, false>
 {
     static bool run(T x, T y)
     {
-      const T max = MaxValue<T>::value();
-      const T min = MinValue<T>::value();
+      const T max = MaxValue<T>::value;
+      const T min = MinValue<T>::value;
 
       if (x == 0 || y == 0)
         return true;
 
       if (x > 0) {
         return y > 0
                ? x <= max / y
                : y >= min / x;
@@ -385,36 +381,34 @@ struct IsMulValidImpl<T, true, false>
     }
 };
 
 template<typename T>
 struct IsMulValidImpl<T, false, false>
 {
     static bool run(T x, T y)
     {
-      return y == 0 ||
-             x <= MaxValue<T>::value() / y;
+      return y == 0 ||  x <= MaxValue<T>::value / y;
     }
 };
 
 template<typename T>
 inline bool
 IsMulValid(T x, T y, T /* result not used */)
 {
   return IsMulValidImpl<T>::run(x, y);
 }
 
 template<typename T>
 inline bool
 IsDivValid(T x, T y)
 {
   // Keep in mind that in the signed case, min/-1 is invalid because abs(min)>max.
-  return IsSigned<T>::value
-         ? (y != 0) && (x != MinValue<T>::value() || y != T(-1))
-         : y != 0;
+  return y != 0 &&
+         !(IsSigned<T>::value && x == MinValue<T>::value && y == T(-1));
 }
 
 // This is just to shut up msvc warnings about negating unsigned ints.
 template<typename T, bool IsSigned = IsSigned<T>::value>
 struct OppositeIfSignedImpl
 {
     static T run(T x) { return -x; }
 };
--- a/mfbt/tests/TestCheckedInt.cpp
+++ b/mfbt/tests/TestCheckedInt.cpp
@@ -91,18 +91,18 @@ void test()
 
   testTwiceBiggerType<T>::run();
 
   typedef typename detail::UnsignedType<T>::Type unsignedT;
 
   VERIFY(sizeof(unsignedT) == sizeof(T));
   VERIFY(detail::IsSigned<unsignedT>::value == false);
 
-  const CheckedInt<T> max(detail::MaxValue<T>::value());
-  const CheckedInt<T> min(detail::MinValue<T>::value());
+  const CheckedInt<T> max(detail::MaxValue<T>::value);
+  const CheckedInt<T> min(detail::MinValue<T>::value);
 
   // Check min() and max(), since they are custom implementations and a mistake there
   // could potentially NOT be caught by any other tests... while making everything wrong!
 
   T bit = 1;
   for (size_t i = 0; i < sizeof(T) * CHAR_BIT - 1; i++)
   {
     VERIFY((min.value() & bit) == 0);
@@ -389,20 +389,20 @@ void test()
   { \
     bool isUSigned = detail::IsSigned<U>::value; \
     VERIFY_IS_VALID(CheckedInt<T>(U(0))); \
     VERIFY_IS_VALID(CheckedInt<T>(U(1))); \
     VERIFY_IS_VALID(CheckedInt<T>(U(100))); \
     if (isUSigned) \
       VERIFY_IS_VALID_IF(CheckedInt<T>(U(-1)), isTSigned); \
     if (sizeof(U) > sizeof(T)) \
-      VERIFY_IS_INVALID(CheckedInt<T>(U(detail::MaxValue<T>::value())+1)); \
-    VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MaxValue<U>::value()), \
+      VERIFY_IS_INVALID(CheckedInt<T>(U(detail::MaxValue<T>::value) + 1)); \
+    VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MaxValue<U>::value), \
       (sizeof(T) > sizeof(U) || ((sizeof(T) == sizeof(U)) && (isUSigned || !isTSigned)))); \
-    VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MinValue<U>::value()), \
+    VERIFY_IS_VALID_IF(CheckedInt<T>(detail::MinValue<U>::value), \
       isUSigned == false ? 1 : \
       bool(isTSigned) == false ? 0 : \
       sizeof(T) >= sizeof(U)); \
   }
   VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int8_t)
   VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint8_t)
   VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(int16_t)
   VERIFY_CONSTRUCTION_FROM_INTEGER_TYPE(uint16_t)
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1290,17 +1290,21 @@ abstract public class GeckoApp
         });
     }
 
     void handleContentLoaded(int tabId) {
         final Tab tab = Tabs.getInstance().getTab(tabId);
         if (tab == null)
             return;
 
-        Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOADED);
+        mMainHandler.post(new Runnable() {
+            public void run() {
+                Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOADED);
+            }
+        });
     }
 
     void handleTitleChanged(int tabId, String title) {
         final Tab tab = Tabs.getInstance().getTab(tabId);
         if (tab == null)
             return;
 
         tab.updateTitle(title);
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -57,16 +57,18 @@ public class GeckoThread extends Thread 
     Intent mIntent;
     String mUri;
     int mRestoreMode;
 
     GeckoThread(Intent intent, String uri, int restoreMode) {
         mIntent = intent;
         mUri = uri;
         mRestoreMode = restoreMode;
+
+        setName("Gecko");
     }
 
     public void run() {
         final GeckoApp app = GeckoApp.mAppContext;
         File cacheFile = GeckoAppShell.getCacheDir(app);
         File libxulFile = new File(cacheFile, "libxul.so");
 
         if ((!libxulFile.exists() ||
--- a/mobile/android/base/TabsTray.java
+++ b/mobile/android/base/TabsTray.java
@@ -158,18 +158,17 @@ public class TabsTray extends GeckoActiv
 
         int position = mTabsAdapter.getPositionForTab(tab);
         if (position == -1)
             return;
 
         if (Tabs.getInstance().getIndexOf(tab) == -1) {
             mWaitingForClose = false;
             mTabsAdapter.removeTab(tab);
-            mList.invalidateViews();
-            mListContainer.requestLayout();
+            mTabsAdapter.notifyDataSetChanged();
         } else {
             View view = mList.getChildAt(position - mList.getFirstVisiblePosition());
             if (view == null)
                 return;
 
             TabRow row = (TabRow) view.getTag();
             mTabsAdapter.assignValues(row, tab);
         }
@@ -339,18 +338,10 @@ public class TabsTray extends GeckoActiv
                 row = (TabRow) convertView.getTag();
             }
 
             Tab tab = mTabs.get(position);
             assignValues(row, tab);
 
             return convertView;
         }
-
-        @Override
-        public void notifyDataSetChanged() {
-        }
-
-        @Override
-        public void notifyDataSetInvalidated() {
-        }
     }
 }
--- a/mobile/android/base/android-sync-files.mk
+++ b/mobile/android/base/android-sync-files.mk
@@ -1,10 +1,10 @@
 # These files are managed in the android-sync repo. Do not modify directly, or your changes will be lost.
-SYNC_JAVA_FILES := sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/crypto/PersistedCrypto5Keys.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/EngineSettings.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/DeleteChannel.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/ConnectionMonitorThread.java sync/net/HandleProgressException.java sync/net/HttpResponseObserver.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NodeAuthenticationException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/NullClusterURLException.java sync/PersistedMetaGlobal.java sync/PrefsSource.java sync/receivers/UpgradeReceiver.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BookmarksDeletionManager.java sync/repositories/android/BookmarksInsertionManager.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/FennecControlHelper.java sync/repositories/android/FennecTabsRepository.java sync/repositories/android/FormHistoryRepositorySession.java sync/repositories/android/PasswordsRepositorySession.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/FormHistoryRecord.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoContentProviderException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/ActivityUtils.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/InvalidSyncKeyException.java sync/setup/SyncAccounts.java sync/setup/SyncAuthenticatorService.java sync/stage/AbstractNonRepositorySyncStage.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureCrypto5KeysStage.java sync/stage/FennecTabsServerSyncStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/FormHistoryServerSyncStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/PasswordsServerSyncStage.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java
+SYNC_JAVA_FILES := sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/crypto/PersistedCrypto5Keys.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/EngineSettings.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/DeleteChannel.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/ConnectionMonitorThread.java sync/net/HandleProgressException.java sync/net/HttpResponseObserver.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NodeAuthenticationException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/NullClusterURLException.java sync/PersistedMetaGlobal.java sync/PrefsSource.java sync/receivers/UpgradeReceiver.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BookmarksDeletionManager.java sync/repositories/android/BookmarksInsertionManager.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/FennecControlHelper.java sync/repositories/android/FennecTabsRepository.java sync/repositories/android/FormHistoryRepositorySession.java sync/repositories/android/PasswordsRepositorySession.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/FormHistoryRecord.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/domain/VersionConstants.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoContentProviderException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/ActivityUtils.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/InvalidSyncKeyException.java sync/setup/SyncAccounts.java sync/setup/SyncAuthenticatorService.java sync/stage/AbstractNonRepositorySyncStage.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureCrypto5KeysStage.java sync/stage/FennecTabsServerSyncStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/FormHistoryServerSyncStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/PasswordsServerSyncStage.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/stage/UploadMetaGlobalStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java
 SYNC_PP_JAVA_FILES := sync/GlobalConstants.java
 SYNC_THIRDPARTY_JAVA_FILES := httpclientandroidlib/androidextra/HttpClientAndroidLog.java httpclientandroidlib/annotation/GuardedBy.java httpclientandroidlib/annotation/Immutable.java httpclientandroidlib/annotation/NotThreadSafe.java httpclientandroidlib/annotation/ThreadSafe.java httpclientandroidlib/auth/AUTH.java httpclientandroidlib/auth/AuthenticationException.java httpclientandroidlib/auth/AuthScheme.java httpclientandroidlib/auth/AuthSchemeFactory.java httpclientandroidlib/auth/AuthSchemeRegistry.java httpclientandroidlib/auth/AuthScope.java httpclientandroidlib/auth/AuthState.java httpclientandroidlib/auth/BasicUserPrincipal.java httpclientandroidlib/auth/ContextAwareAuthScheme.java httpclientandroidlib/auth/Credentials.java httpclientandroidlib/auth/InvalidCredentialsException.java httpclientandroidlib/auth/MalformedChallengeException.java httpclientandroidlib/auth/NTCredentials.java httpclientandroidlib/auth/NTUserPrincipal.java httpclientandroidlib/auth/params/AuthParamBean.java httpclientandroidlib/auth/params/AuthParams.java httpclientandroidlib/auth/params/AuthPNames.java httpclientandroidlib/auth/UsernamePasswordCredentials.java httpclientandroidlib/client/AuthCache.java httpclientandroidlib/client/AuthenticationHandler.java httpclientandroidlib/client/CircularRedirectException.java httpclientandroidlib/client/ClientProtocolException.java httpclientandroidlib/client/CookieStore.java httpclientandroidlib/client/CredentialsProvider.java httpclientandroidlib/client/entity/DecompressingEntity.java httpclientandroidlib/client/entity/DeflateDecompressingEntity.java httpclientandroidlib/client/entity/GzipDecompressingEntity.java httpclientandroidlib/client/entity/UrlEncodedFormEntity.java httpclientandroidlib/client/HttpClient.java httpclientandroidlib/client/HttpRequestRetryHandler.java httpclientandroidlib/client/HttpResponseException.java httpclientandroidlib/client/methods/AbortableHttpRequest.java httpclientandroidlib/client/methods/HttpDelete.java httpclientandroidlib/client/methods/HttpEntityEnclosingRequestBase.java httpclientandroidlib/client/methods/HttpGet.java httpclientandroidlib/client/methods/HttpHead.java httpclientandroidlib/client/methods/HttpOptions.java httpclientandroidlib/client/methods/HttpPost.java httpclientandroidlib/client/methods/HttpPut.java httpclientandroidlib/client/methods/HttpRequestBase.java httpclientandroidlib/client/methods/HttpTrace.java httpclientandroidlib/client/methods/HttpUriRequest.java httpclientandroidlib/client/NonRepeatableRequestException.java httpclientandroidlib/client/params/AllClientPNames.java httpclientandroidlib/client/params/AuthPolicy.java httpclientandroidlib/client/params/ClientParamBean.java httpclientandroidlib/client/params/ClientPNames.java httpclientandroidlib/client/params/CookiePolicy.java httpclientandroidlib/client/params/HttpClientParams.java httpclientandroidlib/client/protocol/ClientContext.java httpclientandroidlib/client/protocol/ClientContextConfigurer.java httpclientandroidlib/client/protocol/RequestAcceptEncoding.java httpclientandroidlib/client/protocol/RequestAddCookies.java httpclientandroidlib/client/protocol/RequestAuthCache.java httpclientandroidlib/client/protocol/RequestClientConnControl.java httpclientandroidlib/client/protocol/RequestDefaultHeaders.java httpclientandroidlib/client/protocol/RequestProxyAuthentication.java httpclientandroidlib/client/protocol/RequestTargetAuthentication.java httpclientandroidlib/client/protocol/ResponseAuthCache.java httpclientandroidlib/client/protocol/ResponseContentEncoding.java httpclientandroidlib/client/protocol/ResponseProcessCookies.java httpclientandroidlib/client/RedirectException.java httpclientandroidlib/client/RedirectHandler.java httpclientandroidlib/client/RedirectStrategy.java httpclientandroidlib/client/RequestDirector.java httpclientandroidlib/client/ResponseHandler.java httpclientandroidlib/client/UserTokenHandler.java httpclientandroidlib/client/utils/CloneUtils.java httpclientandroidlib/client/utils/Idn.java httpclientandroidlib/client/utils/JdkIdn.java httpclientandroidlib/client/utils/Punycode.java httpclientandroidlib/client/utils/Rfc3492Idn.java httpclientandroidlib/client/utils/URIUtils.java httpclientandroidlib/client/utils/URLEncodedUtils.java httpclientandroidlib/conn/BasicEofSensorWatcher.java httpclientandroidlib/conn/BasicManagedEntity.java httpclientandroidlib/conn/ClientConnectionManager.java httpclientandroidlib/conn/ClientConnectionManagerFactory.java httpclientandroidlib/conn/ClientConnectionOperator.java httpclientandroidlib/conn/ClientConnectionRequest.java httpclientandroidlib/conn/ConnectionKeepAliveStrategy.java httpclientandroidlib/conn/ConnectionPoolTimeoutException.java httpclientandroidlib/conn/ConnectionReleaseTrigger.java httpclientandroidlib/conn/ConnectTimeoutException.java httpclientandroidlib/conn/EofSensorInputStream.java httpclientandroidlib/conn/EofSensorWatcher.java httpclientandroidlib/conn/HttpHostConnectException.java httpclientandroidlib/conn/HttpRoutedConnection.java httpclientandroidlib/conn/ManagedClientConnection.java httpclientandroidlib/conn/MultihomePlainSocketFactory.java httpclientandroidlib/conn/OperatedClientConnection.java httpclientandroidlib/conn/params/ConnConnectionParamBean.java httpclientandroidlib/conn/params/ConnConnectionPNames.java httpclientandroidlib/conn/params/ConnManagerParamBean.java httpclientandroidlib/conn/params/ConnManagerParams.java httpclientandroidlib/conn/params/ConnManagerPNames.java httpclientandroidlib/conn/params/ConnPerRoute.java httpclientandroidlib/conn/params/ConnPerRouteBean.java httpclientandroidlib/conn/params/ConnRouteParamBean.java httpclientandroidlib/conn/params/ConnRouteParams.java httpclientandroidlib/conn/params/ConnRoutePNames.java httpclientandroidlib/conn/routing/BasicRouteDirector.java httpclientandroidlib/conn/routing/HttpRoute.java httpclientandroidlib/conn/routing/HttpRouteDirector.java httpclientandroidlib/conn/routing/HttpRoutePlanner.java httpclientandroidlib/conn/routing/RouteInfo.java httpclientandroidlib/conn/routing/RouteTracker.java httpclientandroidlib/conn/scheme/HostNameResolver.java httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactory.java httpclientandroidlib/conn/scheme/LayeredSchemeSocketFactoryAdaptor.java httpclientandroidlib/conn/scheme/LayeredSocketFactory.java httpclientandroidlib/conn/scheme/LayeredSocketFactoryAdaptor.java httpclientandroidlib/conn/scheme/PlainSocketFactory.java httpclientandroidlib/conn/scheme/Scheme.java httpclientandroidlib/conn/scheme/SchemeRegistry.java httpclientandroidlib/conn/scheme/SchemeSocketFactory.java httpclientandroidlib/conn/scheme/SchemeSocketFactoryAdaptor.java httpclientandroidlib/conn/scheme/SocketFactory.java httpclientandroidlib/conn/scheme/SocketFactoryAdaptor.java httpclientandroidlib/conn/ssl/AbstractVerifier.java httpclientandroidlib/conn/ssl/AllowAllHostnameVerifier.java httpclientandroidlib/conn/ssl/BrowserCompatHostnameVerifier.java httpclientandroidlib/conn/ssl/SSLSocketFactory.java httpclientandroidlib/conn/ssl/StrictHostnameVerifier.java httpclientandroidlib/conn/ssl/TrustManagerDecorator.java httpclientandroidlib/conn/ssl/TrustSelfSignedStrategy.java httpclientandroidlib/conn/ssl/TrustStrategy.java httpclientandroidlib/conn/ssl/X509HostnameVerifier.java httpclientandroidlib/conn/util/InetAddressUtils.java httpclientandroidlib/ConnectionClosedException.java httpclientandroidlib/ConnectionReuseStrategy.java httpclientandroidlib/cookie/ClientCookie.java httpclientandroidlib/cookie/Cookie.java httpclientandroidlib/cookie/CookieAttributeHandler.java httpclientandroidlib/cookie/CookieIdentityComparator.java httpclientandroidlib/cookie/CookieOrigin.java httpclientandroidlib/cookie/CookiePathComparator.java httpclientandroidlib/cookie/CookieRestrictionViolationException.java httpclientandroidlib/cookie/CookieSpec.java httpclientandroidlib/cookie/CookieSpecFactory.java httpclientandroidlib/cookie/CookieSpecRegistry.java httpclientandroidlib/cookie/MalformedCookieException.java httpclientandroidlib/cookie/params/CookieSpecParamBean.java httpclientandroidlib/cookie/params/CookieSpecPNames.java httpclientandroidlib/cookie/SetCookie.java httpclientandroidlib/cookie/SetCookie2.java httpclientandroidlib/cookie/SM.java httpclientandroidlib/entity/AbstractHttpEntity.java httpclientandroidlib/entity/BasicHttpEntity.java httpclientandroidlib/entity/BufferedHttpEntity.java httpclientandroidlib/entity/ByteArrayEntity.java httpclientandroidlib/entity/ContentLengthStrategy.java httpclientandroidlib/entity/ContentProducer.java httpclientandroidlib/entity/EntityTemplate.java httpclientandroidlib/entity/FileEntity.java httpclientandroidlib/entity/HttpEntityWrapper.java httpclientandroidlib/entity/InputStreamEntity.java httpclientandroidlib/entity/SerializableEntity.java httpclientandroidlib/entity/StringEntity.java httpclientandroidlib/FormattedHeader.java httpclientandroidlib/Header.java httpclientandroidlib/HeaderElement.java httpclientandroidlib/HeaderElementIterator.java httpclientandroidlib/HeaderIterator.java httpclientandroidlib/HttpClientConnection.java httpclientandroidlib/HttpConnection.java httpclientandroidlib/HttpConnectionMetrics.java httpclientandroidlib/HttpEntity.java httpclientandroidlib/HttpEntityEnclosingRequest.java httpclientandroidlib/HttpException.java httpclientandroidlib/HttpHeaders.java httpclientandroidlib/HttpHost.java httpclientandroidlib/HttpInetConnection.java httpclientandroidlib/HttpMessage.java httpclientandroidlib/HttpRequest.java httpclientandroidlib/HttpRequestFactory.java httpclientandroidlib/HttpRequestInterceptor.java httpclientandroidlib/HttpResponse.java httpclientandroidlib/HttpResponseFactory.java httpclientandroidlib/HttpResponseInterceptor.java httpclientandroidlib/HttpServerConnection.java httpclientandroidlib/HttpStatus.java httpclientandroidlib/HttpVersion.java httpclientandroidlib/impl/AbstractHttpClientConnection.java httpclientandroidlib/impl/AbstractHttpServerConnection.java httpclientandroidlib/impl/auth/AuthSchemeBase.java httpclientandroidlib/impl/auth/BasicScheme.java httpclientandroidlib/impl/auth/BasicSchemeFactory.java httpclientandroidlib/impl/auth/DigestScheme.java httpclientandroidlib/impl/auth/DigestSchemeFactory.java httpclientandroidlib/impl/auth/NTLMEngine.java httpclientandroidlib/impl/auth/NTLMEngineException.java httpclientandroidlib/impl/auth/NTLMEngineImpl.java httpclientandroidlib/impl/auth/NTLMScheme.java httpclientandroidlib/impl/auth/NTLMSchemeFactory.java httpclientandroidlib/impl/auth/RFC2617Scheme.java httpclientandroidlib/impl/auth/SpnegoTokenGenerator.java httpclientandroidlib/impl/auth/UnsupportedDigestAlgorithmException.java httpclientandroidlib/impl/client/AbstractAuthenticationHandler.java httpclientandroidlib/impl/client/AbstractHttpClient.java httpclientandroidlib/impl/client/BasicAuthCache.java httpclientandroidlib/impl/client/BasicCookieStore.java httpclientandroidlib/impl/client/BasicCredentialsProvider.java httpclientandroidlib/impl/client/BasicResponseHandler.java httpclientandroidlib/impl/client/ClientParamsStack.java httpclientandroidlib/impl/client/ContentEncodingHttpClient.java httpclientandroidlib/impl/client/DefaultConnectionKeepAliveStrategy.java httpclientandroidlib/impl/client/DefaultHttpClient.java httpclientandroidlib/impl/client/DefaultHttpRequestRetryHandler.java httpclientandroidlib/impl/client/DefaultProxyAuthenticationHandler.java httpclientandroidlib/impl/client/DefaultRedirectHandler.java httpclientandroidlib/impl/client/DefaultRedirectStrategy.java httpclientandroidlib/impl/client/DefaultRedirectStrategyAdaptor.java httpclientandroidlib/impl/client/DefaultRequestDirector.java httpclientandroidlib/impl/client/DefaultTargetAuthenticationHandler.java httpclientandroidlib/impl/client/DefaultUserTokenHandler.java httpclientandroidlib/impl/client/EntityEnclosingRequestWrapper.java httpclientandroidlib/impl/client/RedirectLocations.java httpclientandroidlib/impl/client/RequestWrapper.java httpclientandroidlib/impl/client/RoutedRequest.java httpclientandroidlib/impl/client/TunnelRefusedException.java httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java httpclientandroidlib/impl/conn/AbstractPoolEntry.java httpclientandroidlib/impl/conn/ConnectionShutdownException.java httpclientandroidlib/impl/conn/DefaultClientConnection.java httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java httpclientandroidlib/impl/conn/DefaultResponseParser.java httpclientandroidlib/impl/conn/HttpInetSocketAddress.java httpclientandroidlib/impl/conn/IdleConnectionHandler.java httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java httpclientandroidlib/impl/conn/SchemeRegistryFactory.java httpclientandroidlib/impl/conn/SingleClientConnManager.java httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java httpclientandroidlib/impl/conn/tsccm/RefQueueHandler.java httpclientandroidlib/impl/conn/tsccm/RefQueueWorker.java httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java httpclientandroidlib/impl/conn/tsccm/WaitingThread.java httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java httpclientandroidlib/impl/conn/Wire.java httpclientandroidlib/impl/cookie/AbstractCookieAttributeHandler.java httpclientandroidlib/impl/cookie/AbstractCookieSpec.java httpclientandroidlib/impl/cookie/BasicClientCookie.java httpclientandroidlib/impl/cookie/BasicClientCookie2.java httpclientandroidlib/impl/cookie/BasicCommentHandler.java httpclientandroidlib/impl/cookie/BasicDomainHandler.java httpclientandroidlib/impl/cookie/BasicExpiresHandler.java httpclientandroidlib/impl/cookie/BasicMaxAgeHandler.java httpclientandroidlib/impl/cookie/BasicPathHandler.java httpclientandroidlib/impl/cookie/BasicSecureHandler.java httpclientandroidlib/impl/cookie/BestMatchSpec.java httpclientandroidlib/impl/cookie/BestMatchSpecFactory.java httpclientandroidlib/impl/cookie/BrowserCompatSpec.java httpclientandroidlib/impl/cookie/BrowserCompatSpecFactory.java httpclientandroidlib/impl/cookie/CookieSpecBase.java httpclientandroidlib/impl/cookie/DateParseException.java httpclientandroidlib/impl/cookie/DateUtils.java httpclientandroidlib/impl/cookie/IgnoreSpec.java httpclientandroidlib/impl/cookie/IgnoreSpecFactory.java httpclientandroidlib/impl/cookie/NetscapeDomainHandler.java httpclientandroidlib/impl/cookie/NetscapeDraftHeaderParser.java httpclientandroidlib/impl/cookie/NetscapeDraftSpec.java httpclientandroidlib/impl/cookie/NetscapeDraftSpecFactory.java httpclientandroidlib/impl/cookie/PublicSuffixFilter.java httpclientandroidlib/impl/cookie/PublicSuffixListParser.java httpclientandroidlib/impl/cookie/RFC2109DomainHandler.java httpclientandroidlib/impl/cookie/RFC2109Spec.java httpclientandroidlib/impl/cookie/RFC2109SpecFactory.java httpclientandroidlib/impl/cookie/RFC2109VersionHandler.java httpclientandroidlib/impl/cookie/RFC2965CommentUrlAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965DiscardAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965DomainAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965PortAttributeHandler.java httpclientandroidlib/impl/cookie/RFC2965Spec.java httpclientandroidlib/impl/cookie/RFC2965SpecFactory.java httpclientandroidlib/impl/cookie/RFC2965VersionAttributeHandler.java httpclientandroidlib/impl/DefaultConnectionReuseStrategy.java httpclientandroidlib/impl/DefaultHttpClientConnection.java httpclientandroidlib/impl/DefaultHttpRequestFactory.java httpclientandroidlib/impl/DefaultHttpResponseFactory.java httpclientandroidlib/impl/DefaultHttpServerConnection.java httpclientandroidlib/impl/EnglishReasonPhraseCatalog.java httpclientandroidlib/impl/entity/EntityDeserializer.java httpclientandroidlib/impl/entity/EntitySerializer.java httpclientandroidlib/impl/entity/LaxContentLengthStrategy.java httpclientandroidlib/impl/entity/StrictContentLengthStrategy.java httpclientandroidlib/impl/HttpConnectionMetricsImpl.java httpclientandroidlib/impl/io/AbstractMessageParser.java httpclientandroidlib/impl/io/AbstractMessageWriter.java httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java httpclientandroidlib/impl/io/AbstractSessionOutputBuffer.java httpclientandroidlib/impl/io/ChunkedInputStream.java httpclientandroidlib/impl/io/ChunkedOutputStream.java httpclientandroidlib/impl/io/ContentLengthInputStream.java httpclientandroidlib/impl/io/ContentLengthOutputStream.java httpclientandroidlib/impl/io/HttpRequestParser.java httpclientandroidlib/impl/io/HttpRequestWriter.java httpclientandroidlib/impl/io/HttpResponseParser.java httpclientandroidlib/impl/io/HttpResponseWriter.java httpclientandroidlib/impl/io/HttpTransportMetricsImpl.java httpclientandroidlib/impl/io/IdentityInputStream.java httpclientandroidlib/impl/io/IdentityOutputStream.java httpclientandroidlib/impl/io/SocketInputBuffer.java httpclientandroidlib/impl/io/SocketOutputBuffer.java httpclientandroidlib/impl/NoConnectionReuseStrategy.java httpclientandroidlib/impl/SocketHttpClientConnection.java httpclientandroidlib/impl/SocketHttpServerConnection.java httpclientandroidlib/io/BufferInfo.java httpclientandroidlib/io/EofSensor.java httpclientandroidlib/io/HttpMessageParser.java httpclientandroidlib/io/HttpMessageWriter.java httpclientandroidlib/io/HttpTransportMetrics.java httpclientandroidlib/io/SessionInputBuffer.java httpclientandroidlib/io/SessionOutputBuffer.java httpclientandroidlib/MalformedChunkCodingException.java httpclientandroidlib/message/AbstractHttpMessage.java httpclientandroidlib/message/BasicHeader.java httpclientandroidlib/message/BasicHeaderElement.java httpclientandroidlib/message/BasicHeaderElementIterator.java httpclientandroidlib/message/BasicHeaderIterator.java httpclientandroidlib/message/BasicHeaderValueFormatter.java httpclientandroidlib/message/BasicHeaderValueParser.java httpclientandroidlib/message/BasicHttpEntityEnclosingRequest.java httpclientandroidlib/message/BasicHttpRequest.java httpclientandroidlib/message/BasicHttpResponse.java httpclientandroidlib/message/BasicLineFormatter.java httpclientandroidlib/message/BasicLineParser.java httpclientandroidlib/message/BasicListHeaderIterator.java httpclientandroidlib/message/BasicNameValuePair.java httpclientandroidlib/message/BasicRequestLine.java httpclientandroidlib/message/BasicStatusLine.java httpclientandroidlib/message/BasicTokenIterator.java httpclientandroidlib/message/BufferedHeader.java httpclientandroidlib/message/HeaderGroup.java httpclientandroidlib/message/HeaderValueFormatter.java httpclientandroidlib/message/HeaderValueParser.java httpclientandroidlib/message/LineFormatter.java httpclientandroidlib/message/LineParser.java httpclientandroidlib/message/ParserCursor.java httpclientandroidlib/MethodNotSupportedException.java httpclientandroidlib/NameValuePair.java httpclientandroidlib/NoHttpResponseException.java httpclientandroidlib/params/AbstractHttpParams.java httpclientandroidlib/params/BasicHttpParams.java httpclientandroidlib/params/CoreConnectionPNames.java httpclientandroidlib/params/CoreProtocolPNames.java httpclientandroidlib/params/DefaultedHttpParams.java httpclientandroidlib/params/HttpAbstractParamBean.java httpclientandroidlib/params/HttpConnectionParamBean.java httpclientandroidlib/params/HttpConnectionParams.java httpclientandroidlib/params/HttpParams.java httpclientandroidlib/params/HttpProtocolParamBean.java httpclientandroidlib/params/HttpProtocolParams.java httpclientandroidlib/params/SyncBasicHttpParams.java httpclientandroidlib/ParseException.java httpclientandroidlib/protocol/BasicHttpContext.java httpclientandroidlib/protocol/BasicHttpProcessor.java httpclientandroidlib/protocol/DefaultedHttpContext.java httpclientandroidlib/protocol/ExecutionContext.java httpclientandroidlib/protocol/HTTP.java httpclientandroidlib/protocol/HttpContext.java httpclientandroidlib/protocol/HttpDateGenerator.java httpclientandroidlib/protocol/HttpExpectationVerifier.java httpclientandroidlib/protocol/HttpProcessor.java httpclientandroidlib/protocol/HttpRequestExecutor.java httpclientandroidlib/protocol/HttpRequestHandler.java httpclientandroidlib/protocol/HttpRequestHandlerRegistry.java httpclientandroidlib/protocol/HttpRequestHandlerResolver.java httpclientandroidlib/protocol/HttpRequestInterceptorList.java httpclientandroidlib/protocol/HttpResponseInterceptorList.java httpclientandroidlib/protocol/HttpService.java httpclientandroidlib/protocol/ImmutableHttpProcessor.java httpclientandroidlib/protocol/RequestConnControl.java httpclientandroidlib/protocol/RequestContent.java httpclientandroidlib/protocol/RequestDate.java httpclientandroidlib/protocol/RequestExpectContinue.java httpclientandroidlib/protocol/RequestTargetHost.java httpclientandroidlib/protocol/RequestUserAgent.java httpclientandroidlib/protocol/ResponseConnControl.java httpclientandroidlib/protocol/ResponseContent.java httpclientandroidlib/protocol/ResponseDate.java httpclientandroidlib/protocol/ResponseServer.java httpclientandroidlib/protocol/SyncBasicHttpContext.java httpclientandroidlib/protocol/UriPatternMatcher.java httpclientandroidlib/ProtocolException.java httpclientandroidlib/ProtocolVersion.java httpclientandroidlib/ReasonPhraseCatalog.java httpclientandroidlib/RequestLine.java httpclientandroidlib/StatusLine.java httpclientandroidlib/TokenIterator.java httpclientandroidlib/TruncatedChunkException.java httpclientandroidlib/UnsupportedHttpVersionException.java httpclientandroidlib/util/ByteArrayBuffer.java httpclientandroidlib/util/CharArrayBuffer.java httpclientandroidlib/util/EncodingUtils.java httpclientandroidlib/util/EntityUtils.java httpclientandroidlib/util/ExceptionUtils.java httpclientandroidlib/util/LangUtils.java httpclientandroidlib/util/VersionInfo.java json-simple/ItemList.java json-simple/JSONArray.java json-simple/JSONAware.java json-simple/JSONObject.java json-simple/JSONStreamAware.java json-simple/JSONValue.java json-simple/parser/ContainerFactory.java json-simple/parser/ContentHandler.java json-simple/parser/JSONParser.java json-simple/parser/ParseException.java json-simple/parser/Yylex.java json-simple/parser/Yytoken.java apache/commons/codec/binary/Base32.java apache/commons/codec/binary/Base32InputStream.java apache/commons/codec/binary/Base32OutputStream.java apache/commons/codec/binary/Base64.java apache/commons/codec/binary/Base64InputStream.java apache/commons/codec/binary/Base64OutputStream.java apache/commons/codec/binary/BaseNCodec.java apache/commons/codec/binary/BaseNCodecInputStream.java apache/commons/codec/binary/BaseNCodecOutputStream.java apache/commons/codec/binary/BinaryCodec.java apache/commons/codec/binary/Hex.java apache/commons/codec/binary/StringUtils.java apache/commons/codec/BinaryDecoder.java apache/commons/codec/BinaryEncoder.java apache/commons/codec/CharEncoding.java apache/commons/codec/Decoder.java apache/commons/codec/DecoderException.java apache/commons/codec/digest/DigestUtils.java apache/commons/codec/Encoder.java apache/commons/codec/EncoderException.java apache/commons/codec/language/AbstractCaverphone.java apache/commons/codec/language/Caverphone.java apache/commons/codec/language/Caverphone1.java apache/commons/codec/language/Caverphone2.java apache/commons/codec/language/ColognePhonetic.java apache/commons/codec/language/DoubleMetaphone.java apache/commons/codec/language/Metaphone.java apache/commons/codec/language/RefinedSoundex.java apache/commons/codec/language/Soundex.java apache/commons/codec/language/SoundexUtils.java apache/commons/codec/net/BCodec.java apache/commons/codec/net/QCodec.java apache/commons/codec/net/QuotedPrintableCodec.java apache/commons/codec/net/RFC1522Codec.java apache/commons/codec/net/URLCodec.java apache/commons/codec/net/Utils.java apache/commons/codec/StringDecoder.java apache/commons/codec/StringEncoder.java apache/commons/codec/StringEncoderComparator.java
 SYNC_RES_DRAWABLE := mobile/android/base/resources/drawable/pin_background.xml mobile/android/base/resources/drawable/sync_ic_launcher.png
 SYNC_RES_DRAWABLE_LDPI := mobile/android/base/resources/drawable-ldpi/sync_ic_launcher.png
 SYNC_RES_DRAWABLE_MDPI := mobile/android/base/resources/drawable-mdpi/sync_ic_launcher.png
 SYNC_RES_DRAWABLE_HDPI := mobile/android/base/resources/drawable-hdpi/sync_ic_launcher.png
 SYNC_RES_LAYOUT := res/layout/sync_account.xml res/layout/sync_setup.xml res/layout/sync_setup_failure.xml res/layout/sync_setup_jpake_waiting.xml res/layout/sync_setup_nointernet.xml res/layout/sync_setup_pair.xml res/layout/sync_setup_success.xml res/layout/sync_stub.xml
 SYNC_RES_VALUES := res/values/sync_styles.xml
--- a/mobile/android/base/db/BrowserProvider.java.in
+++ b/mobile/android/base/db/BrowserProvider.java.in
@@ -462,17 +462,17 @@ public class BrowserProvider extends Con
             Pattern p = Pattern.compile(pattern);
 
             ContentValues bookmarksValues = new ContentValues();
             bookmarksValues.put(Bookmarks.PARENT, guidToID(db, Bookmarks.MOBILE_FOLDER_GUID));
             long now = System.currentTimeMillis();
             bookmarksValues.put(Bookmarks.DATE_CREATED, now);
             bookmarksValues.put(Bookmarks.DATE_MODIFIED, now);
 
-            int pos = 1;
+            int pos = 0;
             for (int i = 0; i < fields.length; i++) {
                 String name = fields[i].getName();
                 Matcher m = p.matcher(name);
                 if (!m.find())
                     continue;
 
                 try {
                     int titleid = fields[i].getInt(null);
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -461,17 +461,23 @@ public class GeckoLayerClient implements
     public void renderRequested() {
         GeckoAppShell.scheduleComposite();
     }
 
     /** Implementation of LayerView.Listener */
     public void compositionPauseRequested() {
         // We need to coordinate with Gecko when pausing composition, to ensure
         // that Gecko never executes a draw event while the compositor is paused.
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createCompositorPauseEvent());
+        // This is sent synchronously to make sure that we don't attempt to use
+        // any outstanding Surfaces after we call this (such as from a
+        // surfaceDestroyed notification), and to make sure that any in-flight
+        // Gecko draw events have been processed.  When this returns, composition is
+        // definitely paused -- it'll synchronize with the Gecko event loop, which
+        // in turn will synchronize with the compositor thread.
+        GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorPauseEvent());
     }
 
     /** Implementation of LayerView.Listener */
     public void compositionResumeRequested(int width, int height) {
         // Asking Gecko to resume the compositor takes too long (see
         // https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we
         // resume the compositor directly. We still need to inform Gecko about
         // the compositor resuming, so that Gecko knows that it can now draw.
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -76,18 +76,16 @@ public class LayerView extends SurfaceVi
     private static String LOGTAG = "GeckoLayerView";
 
     private Context mContext;
     private LayerController mController;
     private TouchEventHandler mTouchEventHandler;
     private GLController mGLController;
     private InputConnectionHandler mInputConnectionHandler;
     private LayerRenderer mRenderer;
-    private long mRenderTime;
-    private boolean mRenderTimeReset;
     /* Must be a PAINT_xxx constant */
     private int mPaintState = PAINT_NONE;
 
     private Listener mListener;
 
     /* Flags used to determine when to show the painted surface. The integer
      * order must correspond to the order in which these states occur. */
     public static final int PAINT_NONE = 0;
@@ -173,41 +171,30 @@ public class LayerView extends SurfaceVi
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (mInputConnectionHandler != null)
             return mInputConnectionHandler.onKeyUp(keyCode, event);
         return false;
     }
 
-    public synchronized void requestRender() {
+    public void requestRender() {
         if (mListener != null) {
             mListener.renderRequested();
         }
     }
 
     public void addLayer(Layer layer) {
         mRenderer.addLayer(layer);
     }
 
     public void removeLayer(Layer layer) {
         mRenderer.removeLayer(layer);
     }
 
-    /**
-     * Returns the time elapsed between the first call of requestRender() after
-     * the last call of getRenderTime(), in nanoseconds.
-     */
-    public long getRenderTime() {
-        synchronized(this) {
-            mRenderTimeReset = false;
-            return System.nanoTime() - mRenderTime;
-        }
-    }
-
     public int getMaxTextureSize() {
         return mRenderer.getMaxTextureSize();
     }
 
     /** Used by robocop for testing purposes. Not for production use! This is called via reflection by robocop. */
     public IntBuffer getPixels() {
         return mRenderer.getPixels();
     }
@@ -237,17 +224,17 @@ public class LayerView extends SurfaceVi
     public LayerRenderer getRenderer() {
         return mRenderer;
     }
 
     public void setListener(Listener listener) {
         mListener = listener;
     }
 
-    public synchronized GLController getGLController() {
+    public GLController getGLController() {
         return mGLController;
     }
 
     /** Implementation of SurfaceHolder.Callback */
     public synchronized void surfaceChanged(SurfaceHolder holder, int format, int width,
                                             int height) {
         mGLController.sizeChanged(width, height);
 
--- a/mobile/android/base/sync/CollectionKeys.java
+++ b/mobile/android/base/sync/CollectionKeys.java
@@ -14,17 +14,17 @@ import java.util.Set;
 import org.json.simple.JSONArray;
 import org.json.simple.parser.ParseException;
 import org.mozilla.apache.commons.codec.binary.Base64;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 
 public class CollectionKeys {
   private KeyBundle                  defaultKeyBundle     = null;
-  private HashMap<String, KeyBundle> collectionKeyBundles = new HashMap<String, KeyBundle>();
+  private final HashMap<String, KeyBundle> collectionKeyBundles = new HashMap<String, KeyBundle>();
 
   /**
    * Randomly generate a basic CollectionKeys object.
    * @throws CryptoException
    */
   public static CollectionKeys generateCollectionKeys() throws CryptoException {
     CollectionKeys ck = new CollectionKeys();
     ck.clear();
@@ -118,56 +118,75 @@ public class CollectionKeys {
 
     ExtendedJSONObject collections = cleartext.getObject("collections");
     HashMap<String, KeyBundle> collectionKeys = new HashMap<String, KeyBundle>();
     for (Entry<String, Object> pair : collections.entryIterable()) {
       KeyBundle bundle = arrayToKeyBundle((JSONArray) pair.getValue());
       collectionKeys.put(pair.getKey(), bundle);
     }
 
-    this.collectionKeyBundles = collectionKeys;
+    this.collectionKeyBundles.clear();
+    this.collectionKeyBundles.putAll(collectionKeys);
     this.defaultKeyBundle     = defaultKey;
   }
 
   public void setKeyBundleForCollection(String collection, KeyBundle keys) {
     this.collectionKeyBundles.put(collection, keys);
   }
 
   public void setDefaultKeyBundle(KeyBundle keys) {
     this.defaultKeyBundle = keys;
   }
 
   public void clear() {
     this.defaultKeyBundle = null;
-    this.collectionKeyBundles = new HashMap<String, KeyBundle>();
+    this.collectionKeyBundles.clear();
   }
 
   /**
    * Return set of collections where key is either missing from one collection
    * or not the same in both collections.
    * <p>
    * Does not check for different default keys.
    */
   public static Set<String> differences(CollectionKeys a, CollectionKeys b) {
     Set<String> differences = new HashSet<String>();
+    Set<String> collections = new HashSet<String>(a.collectionKeyBundles.keySet());
+    collections.addAll(b.collectionKeyBundles.keySet());
 
     // Iterate through one collection, collecting missing and differences.
-    for (String collection : a.collectionKeyBundles.keySet()) {
-      KeyBundle key = b.collectionKeyBundles.get(collection);
-      if (key == null) {
+    for (String collection : collections) {
+      KeyBundle keyA;
+      KeyBundle keyB;
+      try {
+        keyA = a.keyBundleForCollection(collection); // Will return default key as appropriate.
+        keyB = b.keyBundleForCollection(collection); // Will return default key as appropriate.
+      } catch (NoCollectionKeysSetException e) {
         differences.add(collection);
         continue;
       }
-      if (!key.equals(a.collectionKeyBundles.get(collection))) {
-        differences.add(collection);
-      }
-    }
-
-    // Now iterate through the other collection, collecting just the missing.
-    for (String collection : b.collectionKeyBundles.keySet()) {
-      if (!a.collectionKeyBundles.containsKey(collection)) {
+      // keyA and keyB are not null at this point.
+      if (!keyA.equals(keyB)) {
         differences.add(collection);
       }
     }
 
     return differences;
   }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof CollectionKeys)) {
+      return false;
+    }
+    CollectionKeys other = (CollectionKeys) o;
+    try {
+      // It would be nice to use map equality here, but there can be map entries
+      // where the key is the default key that should compare equal to a missing
+      // map entry. Therefore, we always compute the set of differences.
+      return defaultKeyBundle().equals(other.defaultKeyBundle()) &&
+             CollectionKeys.differences(this, other).isEmpty();
+    } catch (NoCollectionKeysSetException e) {
+      // If either default key bundle is not set, we'll say the bundles are not equal.
+      return false;
+    }
+  }
 }
--- a/mobile/android/base/sync/EngineSettings.java
+++ b/mobile/android/base/sync/EngineSettings.java
@@ -7,9 +7,25 @@ package org.mozilla.gecko.sync;
 public class EngineSettings {
   public final String syncID;
   public final int version;
 
   public EngineSettings(final String syncID, final int version) {
     this.syncID = syncID;
     this.version = version;
   }
+
+  public EngineSettings(ExtendedJSONObject object) {
+    try {
+      this.syncID = object.getString("syncID");
+      this.version = object.getIntegerSafely("version").intValue();
+    } catch (Exception e ) {
+      throw new IllegalArgumentException(e);
+    }
+  }
+
+  public ExtendedJSONObject toJSONObject() {
+    ExtendedJSONObject json = new ExtendedJSONObject();
+    json.put("syncID", syncID);
+    json.put("version", version);
+    return json;
+  }
 }
--- a/mobile/android/base/sync/ExtendedJSONObject.java
+++ b/mobile/android/base/sync/ExtendedJSONObject.java
@@ -1,44 +1,11 @@
-/* ***** 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 Android Sync Client.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Richard Newman <rnewman@mozilla.com>
- *
- * 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 ***** */
+/* 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/. */
 
 package org.mozilla.gecko.sync;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
--- a/mobile/android/base/sync/GlobalSession.java
+++ b/mobile/android/base/sync/GlobalSession.java
@@ -1,25 +1,26 @@
 /* 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/. */
 
 package org.mozilla.gecko.sync;
 
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
 import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
 import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
@@ -44,21 +45,21 @@ import org.mozilla.gecko.sync.stage.Fenn
 import org.mozilla.gecko.sync.stage.FetchInfoCollectionsStage;
 import org.mozilla.gecko.sync.stage.FetchMetaGlobalStage;
 import org.mozilla.gecko.sync.stage.FormHistoryServerSyncStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage;
 import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage;
 import org.mozilla.gecko.sync.stage.NoSuchStageException;
 import org.mozilla.gecko.sync.stage.PasswordsServerSyncStage;
 import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
+import org.mozilla.gecko.sync.stage.UploadMetaGlobalStage;
 
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.Bundle;
-import android.util.Log;
 import ch.boye.httpclientandroidlib.HttpResponse;
 
 public class GlobalSession implements CredentialsSource, PrefsSource, HttpResponseObserver {
   private static final String LOG_TAG = "GlobalSession";
 
   public static final String API_VERSION   = "1.1";
   public static final long STORAGE_VERSION = 5;
 
@@ -66,16 +67,21 @@ public class GlobalSession implements Cr
 
   protected Map<Stage, GlobalSyncStage> stages;
   public Stage currentState = Stage.idle;
 
   public final GlobalSessionCallback callback;
   private Context context;
   private ClientsDataDelegate clientsDelegate;
 
+  /**
+   * Map from engine name to new settings for an updated meta/global record.
+   */
+  public final Map<String, EngineSettings> enginesToUpdate = new HashMap<String, EngineSettings>();
+
   /*
    * Key accessors.
    */
   public KeyBundle keyBundleForCollection(String collection) throws NoCollectionKeysSetException {
     return config.getCollectionKeys().keyBundleForCollection(collection);
   }
 
   /*
@@ -214,16 +220,17 @@ public class GlobalSession implements Cr
     stages.put(Stage.syncClientsEngine,       new SyncClientsEngineStage(this));
 
     stages.put(Stage.syncTabs,                new FennecTabsServerSyncStage(this));
     stages.put(Stage.syncPasswords,           new PasswordsServerSyncStage(this));
     stages.put(Stage.syncBookmarks,           new AndroidBrowserBookmarksServerSyncStage(this));
     stages.put(Stage.syncHistory,             new AndroidBrowserHistoryServerSyncStage(this));
     stages.put(Stage.syncFormHistory,         new FormHistoryServerSyncStage(this));
 
+    stages.put(Stage.uploadMetaGlobal,        new UploadMetaGlobalStage(this));
     stages.put(Stage.completed,               new CompletedStage(this));
 
     this.stages = Collections.unmodifiableMap(stages);
   }
 
   public GlobalSyncStage getSyncStageByName(String name) throws NoSuchStageException {
     return getSyncStageByName(Stage.byName(name));
   }
@@ -300,25 +307,16 @@ public class GlobalSession implements Cr
       nextStage.execute();
     } catch (Exception ex) {
       Logger.warn(LOG_TAG, "Caught exception " + ex + " running stage " + next);
       this.abort(ex, "Uncaught exception in stage.");
       return;
     }
   }
 
-  private String getSyncID() {
-    return config.syncID;
-  }
-
-  private String generateSyncID() {
-    config.syncID = Utils.generateGuid();
-    return config.syncID;
-  }
-
   /*
    * PrefsSource methods.
    */
   @Override
   public SharedPreferences getPrefs(String name, int mode) {
     return this.getContext().getSharedPreferences(name, mode);
   }
 
@@ -360,23 +358,125 @@ public class GlobalSession implements Cr
   }
 
   public void completeSync() {
     uninstallAsHttpResponseObserver();
     this.currentState = GlobalSyncStage.Stage.idle;
     this.callback.handleSuccess(this);
   }
 
+  /**
+   * Record that an updated meta/global record should be uploaded with the given
+   * settings for the given engine.
+   *
+   * @param engineName engine to update.
+   * @param engineSettings new syncID and version.
+   */
+  public void updateMetaGlobalWith(String engineName, EngineSettings engineSettings) {
+    enginesToUpdate.put(engineName, engineSettings);
+  }
+
+  public boolean hasUpdatedMetaGlobal() {
+    if (enginesToUpdate.isEmpty()) {
+      Logger.info(LOG_TAG, "Not uploading updated meta/global record since there are no engines requesting upload.");
+      return false;
+    }
+
+    if (Logger.logVerbose(LOG_TAG)) {
+      Logger.trace(LOG_TAG, "Uploading updated meta/global record since there are engines requesting upload: " +
+          Utils.toCommaSeparatedString(enginesToUpdate.keySet()));
+    }
+
+    return true;
+  }
+
+  public void updateMetaGlobalInPlace() {
+    ExtendedJSONObject engines = config.metaGlobal.getEngines();
+    for (Entry<String, EngineSettings> pair : enginesToUpdate.entrySet()) {
+      engines.put(pair.getKey(), pair.getValue().toJSONObject());
+    }
+    enginesToUpdate.clear();
+  }
+
+  /**
+   * Synchronously upload an updated meta/global.
+   * <p>
+   * All problems are logged and ignored.
+   */
+  public void uploadUpdatedMetaGlobal() {
+    updateMetaGlobalInPlace();
+
+    Logger.debug(LOG_TAG, "Uploading updated meta/global record.");
+    final Object monitor = new Object();
+
+    Runnable doUpload = new Runnable() {
+      @Override
+      public void run() {
+        config.metaGlobal.upload(new MetaGlobalDelegate() {
+          @Override
+          public void handleSuccess(MetaGlobal global, SyncStorageResponse response) {
+            Logger.info(LOG_TAG, "Successfully uploaded updated meta/global record.");
+            synchronized (monitor) {
+              monitor.notify();
+            }
+          }
+
+          @Override
+          public void handleMissing(MetaGlobal global, SyncStorageResponse response) {
+            Logger.warn(LOG_TAG, "Got 404 missing uploading updated meta/global record; shouldn't happen.  Ignoring.");
+            synchronized (monitor) {
+              monitor.notify();
+            }
+          }
+
+          @Override
+          public void handleFailure(SyncStorageResponse response) {
+            Logger.warn(LOG_TAG, "Failed to upload updated meta/global record; ignoring.");
+            synchronized (monitor) {
+              monitor.notify();
+            }
+          }
+
+          @Override
+          public void handleError(Exception e) {
+            Logger.warn(LOG_TAG, "Got exception trying to upload updated meta/global record; ignoring.", e);
+            synchronized (monitor) {
+              monitor.notify();
+            }
+          }
+        });
+      }
+    };
+
+    final Thread upload = new Thread(doUpload);
+    synchronized (monitor) {
+      try {
+        upload.start();
+        monitor.wait();
+        Logger.debug(LOG_TAG, "Uploaded updated meta/global record.");
+      } catch (InterruptedException e) {
+        Logger.error(LOG_TAG, "Uploading updated meta/global interrupted; continuing.");
+      }
+    }
+  }
+
+
   public void abort(Exception e, String reason) {
     Logger.warn(LOG_TAG, "Aborting sync: " + reason, e);
     uninstallAsHttpResponseObserver();
     long existingBackoff = largestBackoffObserved.get();
     if (existingBackoff > 0) {
       callback.requestBackoff(existingBackoff);
     }
+    if (!(e instanceof HTTPFailureException)) {
+      //  e is null, or we aborted for a non-HTTP reason; okay to upload new meta/global record.
+      if (this.hasUpdatedMetaGlobal()) {
+        this.uploadUpdatedMetaGlobal(); // Only logs errors; does not call abort.
+      }
+    }
     this.callback.handleError(this, e);
   }
 
   public void handleHTTPError(SyncStorageResponse response, String reason) {
     // TODO: handling of 50x (backoff), 401 (node reassignment or auth error).
     // Fall back to aborting.
     Logger.warn(LOG_TAG, "Aborting sync due to HTTP " + response.getStatusCode());
     this.interpretHTTPFailure(response.httpResponse());
@@ -459,185 +559,218 @@ public class GlobalSession implements Cr
 
       @Override
       public String ifUnmodifiedSince() {
         return null;
       }
 
       @Override
       public void handleRequestSuccess(SyncStorageResponse response) {
+        Logger.debug(LOG_TAG, "Keys uploaded.");
         BaseResource.consumeEntity(response); // We don't need the response at all.
         keyUploadDelegate.onKeysUploaded();
       }
 
       @Override
       public void handleRequestFailure(SyncStorageResponse response) {
+        Logger.debug(LOG_TAG, "Failed to upload keys.");
         self.interpretHTTPFailure(response.httpResponse());
         BaseResource.consumeEntity(response); // The exception thrown should not need the body of the response.
         keyUploadDelegate.onKeyUploadFailed(new HTTPFailureException(response));
       }
 
       @Override
       public void handleRequestError(Exception ex) {
+        Logger.warn(LOG_TAG, "Got exception trying to upload keys", ex);
         keyUploadDelegate.onKeyUploadFailed(ex);
       }
 
       @Override
       public String credentials() {
         return self.credentials();
       }
     };
 
+    // Convert keys to an encrypted crypto record.
     CryptoRecord keysRecord;
     try {
       keysRecord = keys.asCryptoRecord();
       keysRecord.setKeyBundle(config.syncKeyBundle);
       keysRecord.encrypt();
-    } catch (UnsupportedEncodingException e) {
-      keyUploadDelegate.onKeyUploadFailed(e);
-      return;
-    } catch (CryptoException e) {
-      keyUploadDelegate.onKeyUploadFailed(e);
-      return;
-    } catch (NoCollectionKeysSetException e) {
-      // Should not occur.
+    } catch (Exception e) {
+      Logger.warn(LOG_TAG, "Got exception trying creating crypto record from keys", e);
       keyUploadDelegate.onKeyUploadFailed(e);
       return;
     }
 
     request.put(keysRecord);
   }
 
   /*
    * meta/global callbacks.
    */
   public void processMetaGlobal(MetaGlobal global) {
     config.metaGlobal = global;
 
     Long storageVersion = global.getStorageVersion();
+    if (storageVersion == null) {
+      Logger.warn(LOG_TAG, "Malformed remote meta/global: could not retrieve remote storage version.");
+      freshStart();
+      return;
+    }
     if (storageVersion < STORAGE_VERSION) {
-      // Outdated server.
+      Logger.warn(LOG_TAG, "Outdated server: reported " +
+          "remote storage version " + storageVersion + " < " +
+          "local storage version " + STORAGE_VERSION);
       freshStart();
       return;
     }
     if (storageVersion > STORAGE_VERSION) {
-      // Outdated client!
+      Logger.warn(LOG_TAG, "Outdated client: reported " +
+          "remote storage version " + storageVersion + " > " +
+          "local storage version " + STORAGE_VERSION);
       requiresUpgrade();
       return;
     }
     String remoteSyncID = global.getSyncID();
     if (remoteSyncID == null) {
-      // Corrupt meta/global.
+      Logger.warn(LOG_TAG, "Malformed remote meta/global: could not retrieve remote syncID.");
       freshStart();
       return;
     }
-    String localSyncID = this.getSyncID();
+    String localSyncID = config.syncID;
     if (!remoteSyncID.equals(localSyncID)) {
-      // Sync ID has changed. Reset timestamps and fetch new keys.
+      Logger.warn(LOG_TAG, "Remote syncID different from local syncID: resetting client and assuming remote syncID.");
       resetAllStages();
       config.purgeCryptoKeys();
       config.syncID = remoteSyncID;
     }
+    // Persist enabled engine names.
     config.enabledEngineNames = global.getEnabledEngineNames();
+    if (config.enabledEngineNames == null) {
+      Logger.warn(LOG_TAG, "meta/global reported no enabled engine names!");
+    } else {
+      if (Logger.logVerbose(LOG_TAG)) {
+        Logger.trace(LOG_TAG, "Persisting enabled engine names '" +
+            Utils.toCommaSeparatedString(config.enabledEngineNames) + "' from meta/global.");
+      }
+    }
     config.persistToPrefs();
     advance();
   }
 
   public void processMissingMetaGlobal(MetaGlobal global) {
     freshStart();
   }
 
   /**
    * Do a fresh start then quietly finish the sync, starting another.
    */
-  protected void freshStart() {
+  public void freshStart() {
     final GlobalSession globalSession = this;
     freshStart(this, new FreshStartDelegate() {
 
       @Override
       public void onFreshStartFailed(Exception e) {
         globalSession.abort(e, "Fresh start failed.");
       }
 
       @Override
       public void onFreshStart() {
         try {
+          Logger.warn(LOG_TAG, "Fresh start succeeded; restarting global session.");
           globalSession.config.persistToPrefs();
           globalSession.restart();
         } catch (Exception e) {
           Logger.warn(LOG_TAG, "Got exception when restarting sync after freshStart.", e);
           globalSession.abort(e, "Got exception after freshStart.");
         }
       }
     });
   }
 
   /**
    * Clean the server, aborting the current sync.
+   * <p>
+   * <ol>
+   * <li>Wipe the server storage.</li>
+   * <li>Reset all stages and purge cached state: (meta/global and crypto/keys records).</li>
+   * <li>Upload fresh meta/global record.</li>
+   * <li>Upload fresh crypto/keys record.</li>
+   * <li>Restart the sync entirely in order to re-download meta/global and crypto/keys record.</li>
+   * </ol>
+   * @param session the current session.
+   * @param freshStartDelegate delegate to notify on fresh start or failure.
    */
-  protected void freshStart(final GlobalSession session, final FreshStartDelegate freshStartDelegate) {
+  protected static void freshStart(final GlobalSession session, final FreshStartDelegate freshStartDelegate) {
+    Logger.debug(LOG_TAG, "Fresh starting.");
 
-    final String newSyncID   = session.generateSyncID();
-    final String metaURL     = session.config.metaURL();
-    final String credentials = session.credentials();
+    final MetaGlobal mg = session.generateNewMetaGlobal();
 
-    wipeServer(session, new WipeServerDelegate() {
+    session.wipeServer(session, new WipeServerDelegate() {
 
       @Override
       public void onWiped(long timestamp) {
+        Logger.debug(LOG_TAG, "Successfully wiped server.  Resetting all stages and purging cached meta/global and crypto/keys records.");
+
         session.resetAllStages();
+        session.config.purgeMetaGlobal();
         session.config.purgeCryptoKeys();
         session.config.persistToPrefs();
 
-        MetaGlobal mg = new MetaGlobal(metaURL, credentials);
-        mg.setSyncID(newSyncID);
-        mg.setStorageVersion(STORAGE_VERSION);
+        Logger.info(LOG_TAG, "Uploading new meta/global with sync ID " + mg.syncID + ".");
 
         // It would be good to set the X-If-Unmodified-Since header to `timestamp`
         // for this PUT to ensure at least some level of transactionality.
         // Unfortunately, the servers don't support it after a wipe right now
         // (bug 693893), so we're going to defer this until bug 692700.
         mg.upload(new MetaGlobalDelegate() {
+          @Override
+          public void handleSuccess(MetaGlobal uploadedGlobal, SyncStorageResponse uploadResponse) {
+            Logger.info(LOG_TAG, "Uploaded new meta/global with sync ID " + uploadedGlobal.syncID + ".");
 
-          @Override
-          public void handleSuccess(MetaGlobal global, SyncStorageResponse response) {
-            session.config.metaGlobal = global;
-            Logger.info(LOG_TAG, "New meta/global uploaded with sync ID " + newSyncID);
-
-            // Generate and upload new keys.
+            // Generate new keys.
+            CollectionKeys keys = null;
             try {
-              session.uploadKeys(CollectionKeys.generateCollectionKeys(), new KeyUploadDelegate() {
-                @Override
-                public void onKeysUploaded() {
-                  // Now we can download them.
-                  freshStartDelegate.onFreshStart();
-                }
-
-                @Override
-                public void onKeyUploadFailed(Exception e) {
-                  Log.e(LOG_TAG, "Got exception uploading new keys.", e);
-                  freshStartDelegate.onFreshStartFailed(e);
-                }
-              });
+              keys = session.generateNewCryptoKeys();
             } catch (CryptoException e) {
-              Log.e(LOG_TAG, "Got exception generating new keys.", e);
+              Logger.warn(LOG_TAG, "Got exception generating new keys; failing fresh start.", e);
               freshStartDelegate.onFreshStartFailed(e);
             }
+            if (keys == null) {
+              Logger.warn(LOG_TAG, "Got null keys from generateNewKeys; failing fresh start.");
+              freshStartDelegate.onFreshStartFailed(null);
+            }
+
+            // Upload new keys.
+            Logger.info(LOG_TAG, "Uploading new crypto/keys.");
+            session.uploadKeys(keys, new KeyUploadDelegate() {
+              @Override
+              public void onKeysUploaded() {
+                Logger.info(LOG_TAG, "Uploaded new crypto/keys.");
+                freshStartDelegate.onFreshStart();
+              }
+
+              @Override
+              public void onKeyUploadFailed(Exception e) {
+                Logger.warn(LOG_TAG, "Got exception uploading new keys.", e);
+                freshStartDelegate.onFreshStartFailed(e);
+              }
+            });
           }
 
           @Override
           public void handleMissing(MetaGlobal global, SyncStorageResponse response) {
-            // Shouldn't happen.
+            // Shouldn't happen on upload.
             Logger.warn(LOG_TAG, "Got 'missing' response uploading new meta/global.");
-            freshStartDelegate.onFreshStartFailed(new Exception("meta/global missing"));
+            freshStartDelegate.onFreshStartFailed(new Exception("meta/global missing while uploading."));
           }
 
           @Override
           public void handleFailure(SyncStorageResponse response) {
-            // TODO: respect backoffs etc.
             Logger.warn(LOG_TAG, "Got failure " + response.getStatusCode() + " uploading new meta/global.");
             session.interpretHTTPFailure(response.httpResponse());
             freshStartDelegate.onFreshStartFailed(new HTTPFailureException(response));
           }
 
           @Override
           public void handleError(Exception e) {
             Logger.warn(LOG_TAG, "Got error uploading new meta/global.", e);
@@ -667,17 +800,17 @@ public class GlobalSession implements Cr
   //
   // When an engine is disabled: wipe its collections on the server, reupload
   // meta/global.
   //
   // On syncing each stage: if server has engine version 0 or old, wipe server,
   // reset client to prompt reupload.
   // If sync ID mismatch: take that syncID and reset client.
 
-  private void wipeServer(final CredentialsSource credentials, final WipeServerDelegate wipeDelegate) {
+  protected void wipeServer(final CredentialsSource credentials, final WipeServerDelegate wipeDelegate) {
     SyncStorageRequest request;
     final GlobalSession self = this;
 
     try {
       request = new SyncStorageRequest(config.storageURL(false));
     } catch (URISyntaxException ex) {
       Logger.warn(LOG_TAG, "Invalid URI in wipeServer.");
       wipeDelegate.onWipeFailed(ex);
@@ -775,22 +908,89 @@ public class GlobalSession implements Cr
       } catch (NoSuchStageException e) {
         Logger.warn(LOG_TAG, "Cannot reset stage " + name + ": no such stage.");
       }
     }
     GlobalSession.resetStages(stages);
   }
 
   /**
+   * Engines to include in a fresh meta/global record.
+   * <p>
+   * Returns either the persisted engine names (perhaps we have been node
+   * re-assigned and are initializing a clean server: we want to upload the
+   * persisted engine names so that we don't accidentally disable engines that
+   * Android Sync doesn't recognize), or the set of engines names that Android
+   * Sync implements.
+   *
+   * @return set of engine names.
+   */
+  protected Set<String> enabledEngineNames() {
+    if (config.enabledEngineNames != null) {
+      return config.enabledEngineNames;
+    }
+    Set<String> engineNames = new HashSet<String>();
+    for (Stage stage : Stage.getNamedStages()) {
+      engineNames.add(stage.getRepositoryName());
+    }
+    return engineNames;
+  }
+
+  /**
+   * Generate fresh crypto/keys collection.
+   * @return crypto/keys collection.
+   * @throws CryptoException
+   */
+  public CollectionKeys generateNewCryptoKeys() throws CryptoException {
+    return CollectionKeys.generateCollectionKeys();
+  }
+
+  /**
+   * Generate a fresh meta/global record.
+   * @return meta/global record.
+   */
+  public MetaGlobal generateNewMetaGlobal() {
+    final String newSyncID   = Utils.generateGuid();
+    final String metaURL     = this.config.metaURL();
+    final String credentials = this.credentials();
+
+    ExtendedJSONObject engines = new ExtendedJSONObject();
+    for (String engineName : enabledEngineNames()) {
+      EngineSettings engineSettings = null;
+      try {
+        GlobalSyncStage globalStage = this.getSyncStageByName(engineName);
+        Integer version = globalStage.getStorageVersion();
+        if (version == null) {
+          continue; // Don't want this stage to be included in meta/global.
+        }
+        engineSettings = new EngineSettings(Utils.generateGuid(), version.intValue());
+      } catch (NoSuchStageException e) {
+        // No trouble; Android Sync might not recognize this engine yet.
+        // By default, version 0.  Other clients will see the 0 version and reset/wipe accordingly.
+        engineSettings = new EngineSettings(Utils.generateGuid(), 0);
+      }
+      engines.put(engineName, engineSettings.toJSONObject());
+    }
+
+    MetaGlobal metaGlobal = new MetaGlobal(metaURL, credentials);
+    metaGlobal.setSyncID(newSyncID);
+    metaGlobal.setStorageVersion(STORAGE_VERSION);
+    metaGlobal.setEngines(engines);
+
+    return metaGlobal;
+  }
+
+  /**
    * Suggest that your Sync client needs to be upgraded to work
    * with this server.
    */
   public void requiresUpgrade() {
     Logger.info(LOG_TAG, "Client outdated storage version; requires update.");
     // TODO: notify UI.
+    this.abort(null, "Requires upgrade");
   }
 
   /**
    * If meta/global is missing or malformed, throws a MetaGlobalException.
    * Otherwise, returns true if there is an entry for this engine in the
    * meta/global "engines" object.
    *
    * @param engineName the name to check (e.g., "bookmarks").
@@ -799,35 +999,31 @@ public class GlobalSession implements Cr
    *        with this, throwing the appropriate MetaGlobalException if not.
    * @return
    *        true if the engine with the provided name is present in the
    *        meta/global "engines" object, and verification passed.
    *
    * @throws MetaGlobalException
    */
   public boolean engineIsEnabled(String engineName, EngineSettings engineSettings) throws MetaGlobalException {
+    if (this.config.metaGlobal == null) {
+      throw new MetaGlobalNotSetException();
+    }
+
     // This should not occur.
     if (this.config.enabledEngineNames == null) {
       Logger.error(LOG_TAG, "No enabled engines in config. Giving up.");
-      if (this.config.metaGlobal == null) {
-        throw new MetaGlobalNotSetException();
-      }
       throw new MetaGlobalMissingEnginesException();
     }
 
     if (!(this.config.enabledEngineNames.contains(engineName))) {
       Logger.debug(LOG_TAG, "Engine " + engineName + " not enabled: no meta/global entry.");
       return false;
     }
 
-    if (this.config.metaGlobal == null) {
-      Logger.warn(LOG_TAG, "No meta/global; using historical enabled engine names.");
-      return true;
-    }
-
     // If we have a meta/global, check that it's safe for us to sync.
     // (If we don't, we'll create one later, which is why we return `true` above.)
     if (engineSettings != null) {
       // Throws if there's a problem.
       this.config.metaGlobal.verifyEngineSettings(engineName, engineSettings);
     }
 
     return true;
--- a/mobile/android/base/sync/MetaGlobal.java
+++ b/mobile/android/base/sync/MetaGlobal.java
@@ -40,108 +40,128 @@ public class MetaGlobal implements SyncS
   // A little hack so we can use the same delegate implementation for upload and download.
   private boolean isUploading;
 
   public MetaGlobal(String metaURL, String credentials) {
     this.metaURL     = metaURL;
     this.credentials = credentials;
   }
 
-  public void fetch(MetaGlobalDelegate callback) {
-    this.callback = callback;
-    this.doFetch();
-  }
-
-  private void doFetch() {
+  public void fetch(MetaGlobalDelegate delegate) {
+    this.callback = delegate;
     try {
       this.isUploading = false;
       SyncStorageRecordRequest r = new SyncStorageRecordRequest(this.metaURL);
       r.delegate = this;
       r.deferGet();
     } catch (URISyntaxException e) {
-      callback.handleError(e);
+      this.callback.handleError(e);
     }
   }
 
   public void upload(MetaGlobalDelegate callback) {
     try {
       this.isUploading = true;
       SyncStorageRecordRequest r = new SyncStorageRecordRequest(this.metaURL);
 
-      // TODO: PUT! Body!
       r.delegate = this;
-      r.deferPut(null);
-    } catch (URISyntaxException e) {
+      this.callback = callback;
+      r.put(this.asCryptoRecord());
+    } catch (Exception e) {
       callback.handleError(e);
     }
   }
 
   protected ExtendedJSONObject asRecordContents() {
     ExtendedJSONObject json = new ExtendedJSONObject();
     json.put("storageVersion", storageVersion);
     json.put("engines", engines);
     json.put("syncID", syncID);
     return json;
   }
 
+  /**
+   * Return a copy ready for upload.
+   * @return an unencrypted <code>CryptoRecord</code>.
+   */
   public CryptoRecord asCryptoRecord() {
     ExtendedJSONObject payload = this.asRecordContents();
     CryptoRecord record = new CryptoRecord(payload);
     record.collection = "meta";
     record.guid       = "global";
     record.deleted    = false;
     return record;
   }
 
   public void setFromRecord(CryptoRecord record) throws IllegalStateException, IOException, ParseException, NonObjectJSONException {
     Logger.info(LOG_TAG, "meta/global is " + record.payload.toJSONString());
     this.storageVersion = (Long) record.payload.get("storageVersion");
-    this.engines = record.payload.getObject("engines");
     this.syncID = (String) record.payload.get("syncID");
+    setEngines(record.payload.getObject("engines"));
   }
 
   public Long getStorageVersion() {
     return this.storageVersion;
   }
 
   public void setStorageVersion(Long version) {
     this.storageVersion = version;
   }
 
   public ExtendedJSONObject getEngines() {
     return engines;
   }
 
   public void setEngines(ExtendedJSONObject engines) {
+    if (engines == null) {
+      engines = new ExtendedJSONObject();
+    }
     this.engines = engines;
     final int count = engines.size();
     versions   = new HashMap<String, Integer>(count);
     syncIDs    = new HashMap<String, String>(count);
     exceptions = new HashMap<String, MetaGlobalException>(count);
     for (String engineName : engines.keySet()) {
       try {
         ExtendedJSONObject engineEntry = engines.getObject(engineName);
         recordEngineState(engineName, engineEntry);
       } catch (NonObjectJSONException e) {
         Logger.error(LOG_TAG, "Engine field for " + engineName + " in meta/global is not an object.");
+        recordEngineState(engineName, new ExtendedJSONObject()); // Doesn't have a version or syncID, for example, so will be server wiped.
       }
     }
   }
 
   /**
    * Take a JSON object corresponding to the 'engines' field for the provided engine name,
    * updating {@link #syncIDs} and {@link #versions} accordingly.
    *
    * If the record is malformed, an entry is added to {@link #exceptions}, to be rethrown
    * during validation.
    */
   protected void recordEngineState(String engineName, ExtendedJSONObject engineEntry) {
     if (engineEntry == null) {
       throw new IllegalArgumentException("engineEntry cannot be null.");
     }
+
+    // Record syncID first, so that engines with bad versions are recorded.
+    try {
+      String syncID = engineEntry.getString("syncID");
+      if (syncID == null) {
+        Logger.warn(LOG_TAG, "No syncID for " + engineName + ". Recording exception.");
+        exceptions.put(engineName, new MetaGlobalMalformedSyncIDException());
+      }
+      syncIDs.put(engineName, syncID);
+    } catch (ClassCastException e) {
+      // Malformed syncID on the server. Wipe the server.
+      Logger.warn(LOG_TAG, "Malformed syncID " + engineEntry.get("syncID") +
+                           " for " + engineName + ". Recording exception.");
+      exceptions.put(engineName, new MetaGlobalMalformedSyncIDException());
+    }
+
     try {
       Integer version = engineEntry.getIntegerSafely("version");
       Logger.trace(LOG_TAG, "Engine " + engineName + " has server version " + version);
       if (version == null ||
           version.intValue() == 0) {
         // Invalid version. Wipe the server.
         Logger.warn(LOG_TAG, "Malformed version " + version +
                              " for " + engineName + ". Recording exception.");
@@ -151,30 +171,16 @@ public class MetaGlobal implements SyncS
       versions.put(engineName, version);
     } catch (NumberFormatException e) {
       // Invalid version. Wipe the server.
       Logger.warn(LOG_TAG, "Malformed version " + engineEntry.get("version") +
                            " for " + engineName + ". Recording exception.");
       exceptions.put(engineName, new MetaGlobalMalformedVersionException());
       return;
     }
-
-    try {
-      String syncID = engineEntry.getString("syncID");
-      if (syncID == null) {
-        Logger.warn(LOG_TAG, "No syncID for " + engineName + ". Recording exception.");
-        exceptions.put(engineName, new MetaGlobalMalformedSyncIDException());
-      }
-      syncIDs.put(engineName, syncID);
-    } catch (ClassCastException e) {
-      // Malformed syncID on the server. Wipe the server.
-      Logger.warn(LOG_TAG, "Malformed syncID " + engineEntry.get("syncID") +
-                           " for " + engineName + ". Recording exception.");
-      exceptions.put(engineName, new MetaGlobalException.MetaGlobalMalformedSyncIDException());
-    }
   }
 
   /**
    * Get enabled engine names.
    *
    * @return a collection of engine names or <code>null</code> if meta/global
    *         was malformed.
    */
@@ -196,28 +202,31 @@ public class MetaGlobal implements SyncS
     if (syncIDs == null) {
       throw new IllegalStateException("No meta/global record yet processed.");
     }
 
     if (engineSettings == null) {
       throw new IllegalArgumentException("engineSettings cannot be null.");
     }
 
-    final String syncID = syncIDs.get(engineName);
-    if (syncID == null) {
-      throw new IllegalArgumentException("Unknown engine " + engineName);
-    }
-
+    // First, see if we had a parsing problem.
     final MetaGlobalException exception = exceptions.get(engineName);
     if (exception != null) {
       throw exception;
     }
 
+    final String syncID = syncIDs.get(engineName);
+    if (syncID == null) {
+      // We have checked engineName against enabled engine names before this, so
+      // we should either have a syncID or an exception for this engine already.
+      throw new IllegalArgumentException("Unknown engine " + engineName);
+    }
+
+    // Since we don't have an exception, and we do have a syncID, we should have a version.
     final Integer version = versions.get(engineName);
-
     if (version > engineSettings.version) {
       // We're out of date.
       throw new MetaGlobalException.MetaGlobalStaleClientVersionException(version);
     }
 
     if (!syncID.equals(engineSettings.syncID)) {
       // Our syncID is wrong. Reset client and take the server syncID.
       throw new MetaGlobalException.MetaGlobalStaleClientSyncIDException(syncID);
@@ -246,43 +255,36 @@ public class MetaGlobal implements SyncS
       this.handleUploadSuccess(response);
     } else {
       this.handleDownloadSuccess(response);
     }
   }
 
   private void handleUploadSuccess(SyncStorageResponse response) {
     this.callback.handleSuccess(this, response);
-    this.callback = null;
   }
 
   private void handleDownloadSuccess(SyncStorageResponse response) {
     if (response.wasSuccessful()) {
       try {
         CryptoRecord record = CryptoRecord.fromJSONRecord(response.jsonObjectBody());
         this.setFromRecord(record);
         this.callback.handleSuccess(this, response);
-        this.callback = null;
       } catch (Exception e) {
         this.callback.handleError(e);
-        this.callback = null;
       }
       return;
     }
     this.callback.handleFailure(response);
-    this.callback = null;
   }
 
   public void handleRequestFailure(SyncStorageResponse response) {
     if (response.getStatusCode() == 404) {
       this.callback.handleMissing(this, response);
-      this.callback = null;
       return;
     }
     this.callback.handleFailure(response);
-    this.callback = null;
   }
 
   public void handleRequestError(Exception e) {
     this.callback.handleError(e);
-    this.callback = null;
   }
 }
--- a/mobile/android/base/sync/SyncConfiguration.java
+++ b/mobile/android/base/sync/SyncConfiguration.java
@@ -171,17 +171,17 @@ public class SyncConfiguration implement
 
   public static final String DEFAULT_USER_API = "https://auth.services.mozilla.com/user/1.0/";
 
   private static final String LOG_TAG = "SyncConfiguration";
 
   // These must be set in GlobalSession's constructor.
   public String          userAPI;
   public URI             serverURL;
-  protected URI          clusterURL;
+  public URI             clusterURL;
   public String          username;
   public KeyBundle       syncKeyBundle;
 
   public CollectionKeys  collectionKeys;
   public InfoCollections infoCollections;
   public MetaGlobal      metaGlobal;
   public String          password;
   public String          syncID;
@@ -430,16 +430,21 @@ public class SyncConfiguration implement
 
   public void purgeCryptoKeys() {
     if (collectionKeys != null) {
       collectionKeys.clear();
     }
     persistedCryptoKeys().purge();
   }
 
+  public void purgeMetaGlobal() {
+    metaGlobal = null;
+    persistedMetaGlobal().purge();
+  }
+
   public PersistedCrypto5Keys persistedCryptoKeys() {
     return new PersistedCrypto5Keys(getPrefs(), syncKeyBundle);
   }
 
   public PersistedMetaGlobal persistedMetaGlobal() {
     return new PersistedMetaGlobal(getPrefs());
   }
 }
--- a/mobile/android/base/sync/SynchronizerConfiguration.java
+++ b/mobile/android/base/sync/SynchronizerConfiguration.java
@@ -39,17 +39,16 @@ package org.mozilla.gecko.sync;
 
 import java.io.IOException;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.SyncConfiguration.ConfigurationBranch;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 
 import android.content.SharedPreferences.Editor;
-import android.util.Log;
 
 public class SynchronizerConfiguration {
   private static final String LOG_TAG = "SynczrConfiguration";
 
   public String syncID;
   public RepositorySessionBundle remoteBundle;
   public RepositorySessionBundle localBundle;
 
@@ -58,24 +57,16 @@ public class SynchronizerConfiguration {
   }
 
   public SynchronizerConfiguration(String syncID, RepositorySessionBundle remoteBundle, RepositorySessionBundle localBundle) {
     this.syncID       = syncID;
     this.remoteBundle = remoteBundle;
     this.localBundle  = localBundle;
   }
 
-  public String[] toStringValues() {
-    String[] out = new String[3];
-    out[0] = syncID;
-    out[1] = remoteBundle.toJSONString();
-    out[2] = localBundle.toJSONString();
-    return out;
-  }
-
   // This should get partly shuffled back into SyncConfiguration, I think.
   public void load(ConfigurationBranch config) throws NonObjectJSONException, IOException, ParseException {
     if (config == null) {
       throw new IllegalArgumentException("config cannot be null.");
     }
     String remoteJSON = config.getString("remote", null);
     String localJSON  = config.getString("local",  null);
     RepositorySessionBundle rB = new RepositorySessionBundle(remoteJSON);
@@ -84,26 +75,27 @@ public class SynchronizerConfiguration {
       rB.setTimestamp(0);
     }
     if (localJSON == null) {
       lB.setTimestamp(0);
     }
     syncID = config.getString("syncID", null);
     remoteBundle = rB;
     localBundle  = lB;
-    Log.i(LOG_TAG, "Initialized SynchronizerConfiguration. syncID: " + syncID + ", remoteBundle: " + remoteBundle + ", localBundle: " + localBundle);
+    Logger.debug(LOG_TAG, "Loaded SynchronizerConfiguration. syncID: " + syncID + ", remoteBundle: " + remoteBundle + ", localBundle: " + localBundle);
   }
 
   public void persist(ConfigurationBranch config) {
     if (config == null) {
       throw new IllegalArgumentException("config cannot be null.");
     }
     String jsonRemote = remoteBundle.toJSONString();
     String jsonLocal  = localBundle.toJSONString();
     Editor editor = config.edit();
     editor.putString("remote", jsonRemote);
     editor.putString("local",  jsonLocal);
     editor.putString("syncID", syncID);
 
     // Synchronous.
     editor.commit();
+    Logger.debug(LOG_TAG, "Persisted SynchronizerConfiguration. syncID: " + syncID + ", remoteBundle: " + remoteBundle + ", localBundle: " + localBundle);
   }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/repositories/domain/VersionConstants.java
@@ -0,0 +1,14 @@
+/* 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/. */
+
+package org.mozilla.gecko.sync.repositories.domain;
+
+public class VersionConstants {
+  public static final int BOOKMARKS_ENGINE_VERSION = 2;
+  public static final int CLIENTS_ENGINE_VERSION = 1;
+  public static final int FORMS_ENGINE_VERSION = 1;
+  public static final int HISTORY_ENGINE_VERSION = 1;
+  public static final int PASSWORDS_ENGINE_VERSION = 1;
+  public static final int TABS_ENGINE_VERSION = 1;
+}
--- a/mobile/android/base/sync/stage/AbstractNonRepositorySyncStage.java
+++ b/mobile/android/base/sync/stage/AbstractNonRepositorySyncStage.java
@@ -20,9 +20,13 @@ public abstract class AbstractNonReposit
   public void resetLocal() {
     // Do nothing.
   }
 
   @Override
   public void wipeLocal() {
     // Do nothing.
   }
+
+  public Integer getStorageVersion() {
+    return null; // Never include these engines in any meta/global records.
+  }
 }
--- a/mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java
+++ b/mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java
@@ -10,16 +10,17 @@ import org.mozilla.gecko.sync.GlobalSess
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.MetaGlobalException;
 import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
 import org.mozilla.gecko.sync.repositories.RecordFactory;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.android.AndroidBrowserBookmarksRepository;
 import org.mozilla.gecko.sync.repositories.android.FennecControlHelper;
 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecordFactory;
+import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 
 public class AndroidBrowserBookmarksServerSyncStage extends ServerSyncStage {
   protected static final String LOG_TAG = "BookmarksStage";
 
   // Eventually this kind of sync stage will be data-driven,
   // and all this hard-coding can go away.
   private static final String BOOKMARKS_SORT          = "index";
   private static final long   BOOKMARKS_REQUEST_LIMIT = 5000;         // Sanity limit.
@@ -27,22 +28,28 @@ public class AndroidBrowserBookmarksServ
   public AndroidBrowserBookmarksServerSyncStage(GlobalSession session) {
     super(session);
   }
 
   @Override
   protected String getCollection() {
     return "bookmarks";
   }
+
   @Override
   protected String getEngineName() {
     return "bookmarks";
   }
 
   @Override
+  public Integer getStorageVersion() {
+    return VersionConstants.BOOKMARKS_ENGINE_VERSION;
+  }
+
+  @Override
   protected Repository getRemoteRepository() throws URISyntaxException {
     return new ConstrainedServer11Repository(session.config.getClusterURLString(),
                                              session.config.username,
                                              getCollection(),
                                              session,
                                              BOOKMARKS_REQUEST_LIMIT,
                                              BOOKMARKS_SORT);
   }
--- a/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java
+++ b/mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java
@@ -1,58 +1,26 @@
-/* ***** 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 Android Sync Client.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Richard Newman <rnewman@mozilla.com>
- *
- * 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 ***** */
+/* 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/. */
 
 package org.mozilla.gecko.sync.stage;
 
 import java.net.URISyntaxException;
 
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.MetaGlobalException;
 import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
 import org.mozilla.gecko.sync.repositories.RecordFactory;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.android.AndroidBrowserHistoryRepository;
 import org.mozilla.gecko.sync.repositories.android.FennecControlHelper;
 import org.mozilla.gecko.sync.repositories.domain.HistoryRecordFactory;
+import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 
 public class AndroidBrowserHistoryServerSyncStage extends ServerSyncStage {
   protected static final String LOG_TAG = "HistoryStage";
 
   // Eventually this kind of sync stage will be data-driven,
   // and all this hard-coding can go away.
   private static final String HISTORY_SORT          = "index";
   private static final long   HISTORY_REQUEST_LIMIT = 250;
@@ -60,22 +28,28 @@ public class AndroidBrowserHistoryServer
   public AndroidBrowserHistoryServerSyncStage(GlobalSession session) {
     super(session);
   }
 
   @Override
   protected String getCollection() {
     return "history";
   }
+
   @Override
   protected String getEngineName() {
     return "history";
   }
 
   @Override
+  public Integer getStorageVersion() {
+    return VersionConstants.HISTORY_ENGINE_VERSION;
+  }
+
+  @Override
   protected Repository getLocalRepository() {
     return new AndroidBrowserHistoryRepository();
   }
 
   @Override
   protected Repository getRemoteRepository() throws URISyntaxException {
     return new ConstrainedServer11Repository(session.config.getClusterURLString(),
                                              session.config.username,
--- a/mobile/android/base/sync/stage/EnsureCrypto5KeysStage.java
+++ b/mobile/android/base/sync/stage/EnsureCrypto5KeysStage.java
@@ -16,24 +16,23 @@ import org.mozilla.gecko.sync.ExtendedJS
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NoCollectionKeysSetException;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.crypto.PersistedCrypto5Keys;
-import org.mozilla.gecko.sync.delegates.KeyUploadDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
 import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 
 public class EnsureCrypto5KeysStage
 extends AbstractNonRepositorySyncStage
-implements SyncStorageRequestDelegate, KeyUploadDelegate {
+implements SyncStorageRequestDelegate {
 
   public EnsureCrypto5KeysStage(GlobalSession session) {
     super(session);
   }
 
   private static final String LOG_TAG = "EnsureC5KeysStage";
   private static final String CRYPTO_COLLECTION = "crypto";
   protected boolean retrying = false;
@@ -56,17 +55,17 @@ implements SyncStorageRequestDelegate, K
         Logger.info(LOG_TAG, "Using persisted collection keys for this session.");
         session.config.setCollectionKeys(keys);
         session.advance();
         return;
       }
       Logger.info(LOG_TAG, "Failed to use persisted collection keys for this session.");
     }
 
-    // We need an update: fetch or upload keys as necessary.
+    // We need an update: fetch fresh keys.
     Logger.info(LOG_TAG, "Fetching fresh collection keys for this session.");
     try {
       SyncStorageRecordRequest request = new SyncStorageRecordRequest(session.wboURI(CRYPTO_COLLECTION, "keys"));
       request.delegate = this;
       request.get();
     } catch (URISyntaxException e) {
       session.abort(e, "Invalid URI.");
     }
@@ -194,46 +193,21 @@ implements SyncStorageRequestDelegate, K
     if (retrying) {
       // Should happen very rarely -- this means we uploaded our crypto/keys
       // successfully, but failed to re-download.
       session.handleHTTPError(response, "Failure while re-downloading already uploaded keys.");
       return;
     }
 
     int statusCode = response.getStatusCode();
-    Logger.debug(LOG_TAG, "Got " + statusCode + " fetching keys.");
     if (statusCode == 404) {
-      // No keys. Generate and upload, then refetch.
-      CollectionKeys keys;
-      try {
-        keys = CollectionKeys.generateCollectionKeys();
-      } catch (CryptoException e) {
-        session.abort(e, "Couldn't generate new key bundle.");
-        return;
-      }
-      session.uploadKeys(keys, this);
+      Logger.info(LOG_TAG, "Got 404 fetching keys.  Fresh starting since keys are missing on server.");
+      session.freshStart();
       return;
     }
-    session.handleHTTPError(response, "Failure fetching keys.");
+    session.handleHTTPError(response, "Failure fetching keys: got response status code " + statusCode);
   }
 
   @Override
   public void handleRequestError(Exception ex) {
     session.abort(ex, "Failure fetching keys.");
   }
-
-  @Override
-  public void onKeysUploaded() {
-    Logger.debug(LOG_TAG, "New keys uploaded. Persisting before starting stage again.");
-    try {
-      retrying = true;
-      this.execute();
-    } catch (NoSuchStageException e) {
-      session.abort(e, "No such stage.");
-    }
-  }
-
-  @Override
-  public void onKeyUploadFailed(Exception e) {
-    Logger.warn(LOG_TAG, "Key upload failed. Aborting sync.");
-    session.abort(e, "Key upload failed.");
-  }
 }
--- a/mobile/android/base/sync/stage/FennecTabsServerSyncStage.java
+++ b/mobile/android/base/sync/stage/FennecTabsServerSyncStage.java
@@ -1,21 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.stage;
 
-import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.repositories.RecordFactory;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
 import org.mozilla.gecko.sync.repositories.domain.Record;
+import org.mozilla.gecko.sync.repositories.domain.TabsRecord;
+import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 
 public class FennecTabsServerSyncStage extends ServerSyncStage {
   private static final String COLLECTION = "tabs";
 
   public FennecTabsServerSyncStage(GlobalSession session) {
     super(session);
   }
 
@@ -34,16 +35,21 @@ public class FennecTabsServerSyncStage e
   }
 
   @Override
   protected String getEngineName() {
     return COLLECTION;
   }
 
   @Override
+  public Integer getStorageVersion() {
+    return VersionConstants.TABS_ENGINE_VERSION;
+  }
+
+  @Override
   protected Repository getLocalRepository() {
     return new FennecTabsRepository();
   }
 
   @Override
   protected RecordFactory getRecordFactory() {
     return new FennecTabsRecordFactory();
   }
--- a/mobile/android/base/sync/stage/FormHistoryServerSyncStage.java
+++ b/mobile/android/base/sync/stage/FormHistoryServerSyncStage.java
@@ -9,16 +9,17 @@ import java.net.URISyntaxException;
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.repositories.ConstrainedServer11Repository;
 import org.mozilla.gecko.sync.repositories.RecordFactory;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.android.FormHistoryRepositorySession;
 import org.mozilla.gecko.sync.repositories.domain.FormHistoryRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
+import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 
 public class FormHistoryServerSyncStage extends ServerSyncStage {
 
   // Eventually this kind of sync stage will be data-driven,
   // and all this hard-coding can go away.
   private static final String FORM_HISTORY_SORT          = "index";
   private static final long   FORM_HISTORY_REQUEST_LIMIT = 5000;         // Sanity limit.
 
@@ -30,22 +31,28 @@ public class FormHistoryServerSyncStage 
   public void execute() throws NoSuchStageException {
     super.execute();
   }
 
   @Override
   protected String getCollection() {
     return "forms";
   }
+
   @Override
   protected String getEngineName() {
     return "forms";
   }
 
   @Override
+  public Integer getStorageVersion() {
+    return VersionConstants.FORMS_ENGINE_VERSION;
+  }
+
+  @Override
   protected Repository getRemoteRepository() throws URISyntaxException {
     return new ConstrainedServer11Repository(session.config.getClusterURLString(),
                                              session.config.username,
                                              getCollection(),
                                              session,
                                              FORM_HISTORY_REQUEST_LIMIT,
                                              FORM_HISTORY_SORT);
   }
--- a/mobile/android/base/sync/stage/GlobalSyncStage.java
+++ b/mobile/android/base/sync/stage/GlobalSyncStage.java
@@ -29,16 +29,18 @@ public interface GlobalSyncStage {
     processClientCommands,
     updateEnabledEngines,
     */
     syncTabs("tabs"),
     syncPasswords("passwords"),
     syncBookmarks("bookmarks"),
     syncHistory("history"),
     syncFormHistory("forms"),
+
+    uploadMetaGlobal,
     completed;
 
     // Maintain a mapping from names ("bookmarks") to Stage enumerations (syncBookmarks).
     private static final Map<String, Stage> named = new HashMap<String, Stage>();
     static {
       for (Stage s : EnumSet.allOf(Stage.class)) {
         if (s.getRepositoryName() != null) {
           named.put(s.getRepositoryName(), s);
@@ -73,9 +75,16 @@ public interface GlobalSyncStage {
     private Stage(final String name) {
       this.repositoryName = name;
     }
   }
 
   public void execute() throws NoSuchStageException;
   public void resetLocal();
   public void wipeLocal() throws Exception;
+  /**
+   * What storage version number this engine supports.
+   * <p>
+   * Used to generate a fresh meta/global record for upload.
+   * @return a version number or <code>null</code> to never include this engine in a fresh meta/global record.
+   */
+  public Integer getStorageVersion();
 }
--- a/mobile/android/base/sync/stage/PasswordsServerSyncStage.java
+++ b/mobile/android/base/sync/stage/PasswordsServerSyncStage.java
@@ -6,16 +6,17 @@ package org.mozilla.gecko.sync.stage;
 
 import org.mozilla.gecko.sync.CryptoRecord;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.repositories.RecordFactory;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.android.PasswordsRepositorySession;
 import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
+import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 
 public class PasswordsServerSyncStage extends ServerSyncStage {
   public PasswordsServerSyncStage(GlobalSession session) {
     super(session);
   }
 
   @Override
   protected String getCollection() {
@@ -23,16 +24,21 @@ public class PasswordsServerSyncStage ex
   }
 
   @Override
   protected String getEngineName() {
     return "passwords";
   }
 
   @Override
+  public Integer getStorageVersion() {
+    return VersionConstants.PASSWORDS_ENGINE_VERSION;
+  }
+
+  @Override
   protected Repository getLocalRepository() {
     return new PasswordsRepositorySession.PasswordsRepository();
   }
 
   @Override
   protected RecordFactory getRecordFactory() {
     return new PasswordRecordFactory();
   }
--- a/mobile/android/base/sync/stage/ServerSyncStage.java
+++ b/mobile/android/base/sync/stage/ServerSyncStage.java
@@ -4,25 +4,33 @@
 
 package org.mozilla.gecko.sync.stage;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.util.concurrent.ExecutorService;
 
 import org.json.simple.parser.ParseException;
+import org.mozilla.gecko.sync.CredentialsSource;
+import org.mozilla.gecko.sync.EngineSettings;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.HTTPFailureException;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.MetaGlobalException;
 import org.mozilla.gecko.sync.NoCollectionKeysSetException;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.SynchronizerConfiguration;
+import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
+import org.mozilla.gecko.sync.delegates.WipeServerDelegate;
 import org.mozilla.gecko.sync.middleware.Crypto5MiddlewareRepository;
+import org.mozilla.gecko.sync.net.BaseResource;
+import org.mozilla.gecko.sync.net.SyncStorageRequest;
+import org.mozilla.gecko.sync.net.SyncStorageRequestDelegate;
+import org.mozilla.gecko.sync.net.SyncStorageResponse;
 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
 import org.mozilla.gecko.sync.repositories.RecordFactory;
 import org.mozilla.gecko.sync.repositories.Repository;
 import org.mozilla.gecko.sync.repositories.RepositorySession;
 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
 import org.mozilla.gecko.sync.repositories.Server11Repository;
 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
@@ -58,19 +66,40 @@ public abstract class ServerSyncStage im
 
   /**
    * Override these in your subclasses.
    *
    * @return true if this stage should be executed.
    * @throws MetaGlobalException
    */
   protected boolean isEnabled() throws MetaGlobalException {
-    // TODO: pass EngineSettings here to check syncID and storage version.
-    // Catch the subclasses of MetaGlobalException to trigger various resets and wipes.
-    return session.engineIsEnabled(this.getEngineName(), null);
+    EngineSettings engineSettings = null;
+    try {
+       engineSettings = getEngineSettings();
+    } catch (Exception e) {
+      Logger.warn(LOG_TAG, "Unable to get engine settings for " + this + ": fetching config failed.", e);
+      // Fall through; null engineSettings will pass below.
+    }
+
+    // We catch the subclasses of MetaGlobalException to trigger various resets and wipes in execute().
+    return session.engineIsEnabled(this.getEngineName(), engineSettings);
+  }
+
+  protected EngineSettings getEngineSettings() throws NonObjectJSONException, IOException, ParseException {
+    Integer version = getStorageVersion();
+    if (version == null) {
+      Logger.warn(LOG_TAG, "null storage version for " + this + "; using version 0.");
+      version = new Integer(0);
+    }
+
+    SynchronizerConfiguration config = this.getConfig();
+    if (config == null) {
+      return new EngineSettings(null, version.intValue());
+    }
+    return new EngineSettings(config.syncID, version.intValue());
   }
 
   protected abstract String getCollection();
   protected abstract String getEngineName();
   protected abstract Repository getLocalRepository();
   protected abstract RecordFactory getRecordFactory();
 
   // Override this in subclasses.
@@ -108,40 +137,51 @@ public abstract class ServerSyncStage im
   }
 
   public Synchronizer getConfiguredSynchronizer(GlobalSession session) throws NoCollectionKeysSetException, URISyntaxException, NonObjectJSONException, IOException, ParseException {
     Repository remote = wrappedServerRepo();
 
     Synchronizer synchronizer = new Synchronizer();
     synchronizer.repositoryA = remote;
     synchronizer.repositoryB = this.getLocalRepository();
+    synchronizer.load(getConfig());
 
-    SynchronizerConfiguration config = this.getConfig();
-    synchronizer.load(config);
-
-    // TODO: should wipe in either direction?
-    // TODO: syncID?!
     return synchronizer;
   }
 
+  /**
+   * Reset timestamps.
+   */
   @Override
   public void resetLocal() {
+    resetLocal(null);
+  }
+
+  /**
+   * Reset timestamps and possibly set syncID.
+   * @param syncID if non-null, new syncID to persist.
+   */
+  public void resetLocal(String syncID) {
     // Clear both timestamps.
     SynchronizerConfiguration config;
     try {
       config = this.getConfig();
     } catch (Exception e) {
       Logger.warn(LOG_TAG, "Unable to reset " + this + ": fetching config failed.", e);
       return;
     }
 
+    if (syncID != null) {
+      config.syncID = syncID;
+      Logger.info(LOG_TAG, "Setting syncID for " + this + " to '" + syncID + "'.");
+    }
     config.localBundle.setTimestamp(0L);
     config.remoteBundle.setTimestamp(0L);
+    persistConfig(config);
     Logger.info(LOG_TAG, "Reset timestamps for " + this);
-    persistConfig(config);
   }
 
   // Not thread-safe. Use with caution.
   private class WipeWaiter {
     public boolean sessionSucceeded = true;
     public boolean wipeSucceeded = true;
     public Exception error;
 
@@ -151,18 +191,18 @@ public abstract class ServerSyncStage im
       this.error = e;
       this.notify();
     }
   }
 
   /**
    * Synchronously wipe this stage by instantiating a local repository session
    * and wiping that.
-   *
-   * Logs and rethrows an exception on failure.
+   * <p>
+   * Logs and re-throws an exception on failure.
    */
   @Override
   public void wipeLocal() throws Exception {
     // Reset, then clear data.
     this.resetLocal();
 
     final WipeWaiter monitor = new WipeWaiter();
     final Context context = session.getContext();
@@ -285,33 +325,156 @@ public abstract class ServerSyncStage im
     if (!monitor.wipeSucceeded) {
       Logger.error(LOG_TAG, "Failed to wipe session.");
       throw monitor.error;
     }
 
     Logger.info(LOG_TAG, "Wiping stage complete.");
   }
 
+  /**
+   * Asynchronously wipe collection on server.
+   */
+  protected void wipeServer(final CredentialsSource credentials, final WipeServerDelegate wipeDelegate) {
+    SyncStorageRequest request;
+
+    try {
+      request = new SyncStorageRequest(session.config.collectionURI(getCollection()));
+    } catch (URISyntaxException ex) {
+      Logger.warn(LOG_TAG, "Invalid URI in wipeServer.");
+      wipeDelegate.onWipeFailed(ex);
+      return;
+    }
+
+    request.delegate = new SyncStorageRequestDelegate() {
+
+      @Override
+      public String ifUnmodifiedSince() {
+        return null;
+      }
+
+      @Override
+      public void handleRequestSuccess(SyncStorageResponse response) {
+        BaseResource.consumeEntity(response);
+        resetLocal();
+        wipeDelegate.onWiped(response.normalizedWeaveTimestamp());
+      }
+
+      @Override
+      public void handleRequestFailure(SyncStorageResponse response) {
+        Logger.warn(LOG_TAG, "Got request failure " + response.getStatusCode() + " in wipeServer.");
+        // Process HTTP failures here to pick up backoffs, etc.
+        session.interpretHTTPFailure(response.httpResponse());
+        BaseResource.consumeEntity(response); // The exception thrown should not need the body of the response.
+        wipeDelegate.onWipeFailed(new HTTPFailureException(response));
+      }
+
+      @Override
+      public void handleRequestError(Exception ex) {
+        Logger.warn(LOG_TAG, "Got exception in wipeServer.", ex);
+        wipeDelegate.onWipeFailed(ex);
+      }
+
+      @Override
+      public String credentials() {
+        return credentials.credentials();
+      }
+    };
+
+    request.delete();
+  }
+
+  /**
+   * Synchronously wipe the server.
+   * <p>
+   * Logs and re-throws an exception on failure.
+   */
+  public void wipeServer() throws Exception {
+    final WipeWaiter monitor = new WipeWaiter();
+
+    final Runnable doWipe = new Runnable() {
+      @Override
+      public void run() {
+        wipeServer(session, new WipeServerDelegate() {
+          @Override
+          public void onWiped(long timestamp) {
+            synchronized (monitor) {
+              monitor.notify();
+            }
+          }
+
+          @Override
+          public void onWipeFailed(Exception e) {
+            synchronized (monitor) {
+              monitor.notify(e, false);
+            }
+          }
+        });
+      }
+    };
+
+    final Thread wiping = new Thread(doWipe);
+    synchronized (monitor) {
+      wiping.start();
+      try {
+        monitor.wait();
+      } catch (InterruptedException e) {
+        Logger.error(LOG_TAG, "Server wipe interrupted.");
+      }
+    }
+
+    if (!monitor.wipeSucceeded) {
+      Logger.error(LOG_TAG, "Failed to wipe server.");
+      throw monitor.error;
+    }
+
+    Logger.info(LOG_TAG, "Wiping server complete.");
+  }
+
   @Override
   public void execute() throws NoSuchStageException {
     final String name = getEngineName();
     Logger.debug(LOG_TAG, "Starting execute for " + name);
 
     try {
       if (!this.isEnabled()) {
         Logger.info(LOG_TAG, "Stage " + name + " disabled; skipping.");
         session.advance();
         return;
       }
+    } catch (MetaGlobalException.MetaGlobalMalformedSyncIDException e) {
+      // Bad engine syncID. This should never happen. Wipe the server.
+      try {
+        session.updateMetaGlobalWith(name, new EngineSettings(Utils.generateGuid(), this.getStorageVersion()));
+        Logger.info(LOG_TAG, "Wiping server because malformed engine sync ID was found in meta/global.");
+        wipeServer();
+        Logger.info(LOG_TAG, "Wiped server after malformed engine sync ID found in meta/global.");
+      } catch (Exception ex) {
+        session.abort(ex, "Failed to wipe server after malformed engine sync ID found in meta/global.");
+      }
+    } catch (MetaGlobalException.MetaGlobalMalformedVersionException e) {
+      // Bad engine version. This should never happen. Wipe the server.
+      try {
+        session.updateMetaGlobalWith(name, new EngineSettings(Utils.generateGuid(), this.getStorageVersion()));
+        Logger.info(LOG_TAG, "Wiping server because malformed engine version was found in meta/global.");
+        wipeServer();
+        Logger.info(LOG_TAG, "Wiped server after malformed engine version found in meta/global.");
+      } catch (Exception ex) {
+        session.abort(ex, "Failed to wipe server after malformed engine version found in meta/global.");
+      }
+    } catch (MetaGlobalException.MetaGlobalStaleClientSyncIDException e) {
+      // Our syncID is wrong. Reset client and take the server syncID.
+      Logger.warn(LOG_TAG, "Remote engine syncID different from local engine syncID:" +
+                           " resetting local engine and assuming remote engine syncID.");
+      this.resetLocal(e.serverSyncID);
     } catch (MetaGlobalException e) {
       session.abort(e, "Inappropriate meta/global; refusing to execute " + name + " stage.");
       return;
     }
 
-
     Synchronizer synchronizer;
     try {
       synchronizer = this.getConfiguredSynchronizer(session);
     } catch (NoCollectionKeysSetException e) {
       session.abort(e, "No CollectionKeys.");
       return;
     } catch (URISyntaxException e) {
       session.abort(e, "Invalid URI syntax for server repository.");
@@ -331,21 +494,21 @@ public abstract class ServerSyncStage im
     synchronizer.synchronize(session.getContext(), this);
     Logger.debug(LOG_TAG, "Reached end of execute.");
   }
 
   @Override
   public void onSynchronized(Synchronizer synchronizer) {
     Logger.debug(LOG_TAG, "onSynchronized.");
 
-    SynchronizerConfiguration synchronizerConfiguration = synchronizer.save();
-    if (synchronizerConfiguration != null) {
-      persistConfig(synchronizerConfiguration);
+    SynchronizerConfiguration newConfig = synchronizer.save();
+    if (newConfig != null) {
+      persistConfig(newConfig);
     } else {
-      Logger.warn(LOG_TAG, "Didn't get configuration from synchronizer after success");
+      Logger.warn(LOG_TAG, "Didn't get configuration from synchronizer after success.");
     }
 
     Logger.info(LOG_TAG, "Advancing session.");
     session.advance();
   }
 
   @Override
   public void onSynchronizeFailed(Synchronizer synchronizer,
--- a/mobile/android/base/sync/stage/SyncClientsEngineStage.java
+++ b/mobile/android/base/sync/stage/SyncClientsEngineStage.java
@@ -27,16 +27,17 @@ import org.mozilla.gecko.sync.net.SyncSt
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
 import org.mozilla.gecko.sync.net.WBOCollectionRequestDelegate;
 import org.mozilla.gecko.sync.net.WBORequestDelegate;
 import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
 import org.mozilla.gecko.sync.repositories.android.RepoUtils;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecordFactory;
+import org.mozilla.gecko.sync.repositories.domain.VersionConstants;
 
 import ch.boye.httpclientandroidlib.HttpStatus;
 
 public class SyncClientsEngineStage implements GlobalSyncStage {
   public static final String LOG_TAG = "SyncClientsEngineStage";
   public static final String COLLECTION_NAME = "clients";
   public static final int CLIENTS_TTL_REFRESH = 604800000; // 7 days
   public static final int MAX_UPLOAD_FAILURE_COUNT = 5;
@@ -304,23 +305,27 @@ public class SyncClientsEngineStage impl
   }
 
   @Override
   public void wipeLocal() throws Exception {
     // Nothing more to do.
     this.resetLocal();
   }
 
+  public Integer getStorageVersion() {
+    return VersionConstants.CLIENTS_ENGINE_VERSION;
+  }
+
   protected ClientRecord newLocalClientRecord(ClientsDataDelegate delegate) {
     final String ourGUID = delegate.getAccountGUID();
     final String ourName = delegate.getClientName();
 
     ClientRecord r = new ClientRecord(ourGUID);
     r.name = ourName;
-    return r;    
+    return r;
   }
 
   // TODO: Bug 726055 - More considered handling of when to sync.
   protected boolean shouldDownload() {
     // Ask info/collections whether a download is needed.
     return true;
   }
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/stage/UploadMetaGlobalStage.java
@@ -0,0 +1,23 @@
+/* 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/. */
+
+package org.mozilla.gecko.sync.stage;
+
+import org.mozilla.gecko.sync.GlobalSession;
+
+public class UploadMetaGlobalStage extends AbstractNonRepositorySyncStage {
+  public static final String LOG_TAG = "UploadMGStage";
+
+  public UploadMetaGlobalStage(GlobalSession session) {
+    super(session);
+  }
+
+  @Override
+  public void execute() throws NoSuchStageException {
+    if (session.hasUpdatedMetaGlobal()) {
+      session.uploadUpdatedMetaGlobal();
+    }
+    session.advance();
+  }
+}
--- a/mobile/android/base/sync/syncadapter/SyncAdapter.java
+++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java
@@ -172,32 +172,42 @@ public class SyncAdapter extends Abstrac
     // thread and so you must consider the multi-threaded implications of the
     // work that you do in this method."
   }
 
   public Object syncMonitor = new Object();
   private SyncResult syncResult;
 
   public Account localAccount;
+  protected boolean thisSyncIsForced = false;
 
   /**
    * Return the number of milliseconds until we're allowed to sync again,
    * or 0 if now is fine.
    */
   public long delayMilliseconds() {
     long earliestNextSync = getEarliestNextSync();
     if (earliestNextSync <= 0) {
       return 0;
     }
     long now = System.currentTimeMillis();
     return Math.max(0, earliestNextSync - now);
   }
 
   @Override
   public boolean shouldBackOff() {
+    if (thisSyncIsForced) {
+      /*
+       * If the user asks us to sync, we should sync regardless. This path is
+       * hit if the user force syncs and we restart a session after a
+       * freshStart.
+       */
+      return false;
+    }
+
     if (wantNodeAssignment()) {
       /*
        * We recently had a 401 and we aborted the last sync. We should kick off
        * another sync to fetch a new node/weave cluster URL, since ours is
        * stale. If we have a user authentication error, the next sync will
        * determine that and will stop requesting node assignment, so this will
        * only force one abnormally scheduled sync.
        */
@@ -215,20 +225,20 @@ public class SyncAdapter extends Abstrac
                             final SyncResult syncResult) {
 
     Utils.reseedSharedRandom(); // Make sure we don't work with the same random seed for too long.
 
     // Set these so that we don't need to thread them through assorted calls and callbacks.
     this.syncResult   = syncResult;
     this.localAccount = account;
 
-    boolean force = (extras != null) && (extras.getBoolean("force", false));
+    thisSyncIsForced = (extras != null) && (extras.getBoolean("force", false));
     long delay = delayMilliseconds();
     if (delay > 0) {
-      if (force) {
+      if (thisSyncIsForced) {
         Log.i(LOG_TAG, "Forced sync: overruling remaining backoff of " + delay + "ms.");
       } else {
         Log.i(LOG_TAG, "Not syncing: must wait another " + delay + "ms.");
         long remainingSeconds = delay / 1000;
         syncResult.delayUntil = remainingSeconds + BACKOFF_PAD_SECONDS;
         return;
       }
     }
--- a/mobile/android/base/sync/synchronizer/Synchronizer.java
+++ b/mobile/android/base/sync/synchronizer/Synchronizer.java
@@ -23,16 +23,17 @@ import android.util.Log;
  * `onSynchronizeFailed` callback methods. In addition, I call
  * `onSynchronizeAborted` before `onSynchronizeFailed` when I encounter a fetch,
  * store, or session error while synchronizing.
  *
  * After synchronizing, call `save` to get back a SynchronizerConfiguration with
  * updated bundle information.
  */
 public class Synchronizer {
+  protected String configSyncID; // Used to pass syncID from load() back into save().
 
   /**
    * I translate the fine-grained feedback of a SynchronizerSessionDelegate into
    * the coarse-grained feedback of a SynchronizerDelegate.
    */
   public class SynchronizerDelegateSessionDelegate implements
       SynchronizerSessionDelegate {
 
@@ -104,25 +105,24 @@ public class Synchronizer {
    */
   public void synchronize(Context context, SynchronizerDelegate delegate) {
     SynchronizerDelegateSessionDelegate sessionDelegate = new SynchronizerDelegateSessionDelegate(delegate);
     SynchronizerSession session = new SynchronizerSession(this, sessionDelegate);
     session.init(context, bundleA, bundleB);
   }
 
   public SynchronizerConfiguration save() {
-    String syncID = null;      // TODO: syncID.
-    return new SynchronizerConfiguration(syncID, bundleA, bundleB);
+    return new SynchronizerConfiguration(configSyncID, bundleA, bundleB);
   }
 
   /**
    * Set my repository session bundles from a SynchronizerConfiguration.
    *
    * This method is not thread-safe.
    *
    * @param config
    */
   public void load(SynchronizerConfiguration config) {
     bundleA = config.remoteBundle;
     bundleB = config.localBundle;
-    // TODO: syncID.
+    configSyncID  = config.syncID;
   }
 }
--- a/mobile/android/build.mk
+++ b/mobile/android/build.mk
@@ -74,28 +74,29 @@ package:
 
 fast-package:
 	@$(MAKE) package MOZ_FAST_PACKAGE=1
 
 ifeq ($(OS_TARGET),Android)
 ifneq ($(MOZ_ANDROID_INSTALL_TARGET),)
 ANDROID_SERIAL = $(MOZ_ANDROID_INSTALL_TARGET)
 endif
-ifeq ($(ANDROID_SERIAL),)
+ifneq ($(ANDROID_SERIAL),)
+export ANDROID_SERIAL
+else
 # Determine if there's more than one device connected
 android_devices=$(filter device,$(shell $(ANDROID_PLATFORM_TOOLS)/adb devices))
 ifneq ($(android_devices),device)
 install::
 	@echo "Multiple devices are connected. Define ANDROID_SERIAL to specify the install target."
 	$(ANDROID_PLATFORM_TOOLS)/adb devices
 	@exit 1
 endif
 endif
 
-export ANDROID_SERIAL
 install::
 	$(ANDROID_PLATFORM_TOOLS)/adb install -r $(DIST)/$(PKG_PATH)$(PKG_BASENAME).apk
 else
 	@echo "Mobile can't be installed directly."
 	@exit 1
 endif
 
 deb: package
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2567,17 +2567,17 @@ var BrowserEventHandler = {
       if (this._scrollableElement != null) {
         // Discard if it's the top-level scrollable, we let Java handle this
         let doc = BrowserApp.selectedBrowser.contentDocument;
         if (this._scrollableElement != doc.body && this._scrollableElement != doc.documentElement)
           sendMessageToJava({ gecko: { type: "Panning:Override" } });
       }
     }
 
-    if (!ElementTouchHelper.isElementClickable(closest))
+    if (!ElementTouchHelper.isElementClickable(closest, null, false))
       closest = ElementTouchHelper.elementFromPoint(BrowserApp.selectedBrowser.contentWindow,
                                                     aEvent.changedTouches[0].screenX,
                                                     aEvent.changedTouches[0].screenY);
     if (!closest)
       closest = aEvent.target;
 
     if (closest)
       this._doTapHighlight(closest);
@@ -2649,17 +2649,17 @@ var BrowserEventHandler = {
       if (element) {
         try {
           let data = JSON.parse(aData);
 
           this._sendMouseEvent("mousemove", element, data.x, data.y);
           this._sendMouseEvent("mousedown", element, data.x, data.y);
           this._sendMouseEvent("mouseup",   element, data.x, data.y);
   
-          if (ElementTouchHelper.isElementClickable(element))
+          if (ElementTouchHelper.isElementClickable(element, null, true))
             Haptic.performSimpleAction(Haptic.LongPress);
         } catch(e) {
           Cu.reportError(e);
         }
       }
       this._cancelTapHighlight();
     } else if (aTopic == "Gesture:DoubleTap") {
       this._cancelTapHighlight();
@@ -2910,17 +2910,17 @@ const ElementTouchHelper = {
 
     // step through layers of IFRAMEs and FRAMES to find innermost element
     while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) {
       // adjust client coordinates' origin to be top left of iframe viewport
       let rect = elem.getBoundingClientRect();
       aX -= rect.left;
       aY -= rect.top;
       cwu = elem.contentDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-      elem = ElementTouchHelper.getClosest(cwu, aX, aY);
+      elem = this.getClosest(cwu, aX, aY);
     }
 
     return elem;
   },
 
   get radius() {
     let prefs = Services.prefs;
     delete this.radius;
@@ -2946,29 +2946,29 @@ const ElementTouchHelper = {
     let target = aWindowUtils.elementFromPoint(aX, aY,
                                                true,   /* ignore root scroll frame*/
                                                false); /* don't flush layout */
 
     // if this element is clickable we return quickly. also, if it isn't,
     // use a cache to speed up future calls to isElementClickable in the
     // loop below.
     let unclickableCache = new Array();
-    if (this.isElementClickable(target, unclickableCache))
+    if (this.isElementClickable(target, unclickableCache, false))
       return target;
 
-    let target = null;
+    target = null;
     let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio,
                                                    this.radius.right * dpiRatio,
                                                    this.radius.bottom * dpiRatio,
                                                    this.radius.left * dpiRatio, true, false);
 
     let threshold = Number.POSITIVE_INFINITY;
     for (let i = 0; i < nodes.length; i++) {
       let current = nodes[i];
-      if (!current.mozMatchesSelector || !this.isElementClickable(current, unclickableCache))
+      if (!current.mozMatchesSelector || !this.isElementClickable(current, unclickableCache, true))
         continue;
 
       let rect = current.getBoundingClientRect();
       let distance = this._computeDistanceFromRect(aX, aY, rect);
 
       // increase a little bit the weight for already visited items
       if (current && current.mozMatchesSelector("*:visited"))
         distance *= (this.weight.visited / 100);
@@ -2977,19 +2977,24 @@ const ElementTouchHelper = {
         target = current;
         threshold = distance;
       }
     }
 
     return target;
   },
 
-  isElementClickable: function isElementClickable(aElement, aUnclickableCache) {
+  isElementClickable: function isElementClickable(aElement, aUnclickableCache, aAllowBodyListeners) {
     const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label";
-    for (let elem = aElement; elem; elem = elem.parentNode) {
+
+    let stopNode = null;
+    if (!aAllowBodyListeners && aElement && aElement.ownerDocument)
+      stopNode = aElement.ownerDocument.body;
+
+    for (let elem = aElement; elem != stopNode; elem = elem.parentNode) {
       if (aUnclickableCache && aUnclickableCache.indexOf(elem) != -1)
         continue;
       if (this._hasMouseListener(elem))
         return true;
       if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector))
         return true;
       if (aUnclickableCache)
         aUnclickableCache.push(elem);
--- a/mobile/android/sync/java-sources.mn
+++ b/mobile/android/sync/java-sources.mn
@@ -130,16 +130,17 @@ sync/repositories/domain/BookmarkRecordF
 sync/repositories/domain/ClientRecord.java
 sync/repositories/domain/ClientRecordFactory.java
 sync/repositories/domain/FormHistoryRecord.java
 sync/repositories/domain/HistoryRecord.java
 sync/repositories/domain/HistoryRecordFactory.java
 sync/repositories/domain/PasswordRecord.java
 sync/repositories/domain/Record.java
 sync/repositories/domain/TabsRecord.java
+sync/repositories/domain/VersionConstants.java
 sync/repositories/HashSetStoreTracker.java
 sync/repositories/HistoryRepository.java
 sync/repositories/IdentityRecordFactory.java
 sync/repositories/InactiveSessionException.java
 sync/repositories/InvalidBookmarkTypeException.java
 sync/repositories/InvalidRequestException.java
 sync/repositories/InvalidSessionTransitionException.java
 sync/repositories/MultipleRecordsForGuidException.java
@@ -179,16 +180,17 @@ sync/stage/FetchInfoCollectionsStage.jav
 sync/stage/FetchMetaGlobalStage.java
 sync/stage/FormHistoryServerSyncStage.java
 sync/stage/GlobalSyncStage.java
 sync/stage/NoSuchStageException.java
 sync/stage/NoSyncIDException.java
 sync/stage/PasswordsServerSyncStage.java
 sync/stage/ServerSyncStage.java
 sync/stage/SyncClientsEngineStage.java
+sync/stage/UploadMetaGlobalStage.java
 sync/StubActivity.java
 sync/syncadapter/SyncAdapter.java
 sync/syncadapter/SyncService.java
 sync/SyncConfiguration.java
 sync/SyncConfigurationException.java
 sync/SyncException.java
 sync/synchronizer/ConcurrentRecordConsumer.java
 sync/synchronizer/RecordConsumer.java
@@ -199,13 +201,12 @@ sync/synchronizer/SerialRecordConsumer.j
 sync/synchronizer/SessionNotBegunException.java
 sync/synchronizer/Synchronizer.java
 sync/synchronizer/SynchronizerDelegate.java
 sync/synchronizer/SynchronizerSession.java
 sync/synchronizer/SynchronizerSessionDelegate.java
 sync/synchronizer/UnbundleError.java
 sync/synchronizer/UnexpectedSessionException.java
 sync/SynchronizerConfiguration.java
-sync/SynchronizerConfigurations.java
 sync/ThreadPool.java
 sync/UnexpectedJSONException.java
 sync/UnknownSynchronizerConfigurationVersionException.java
 sync/Utils.java
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1369,41 +1369,48 @@ WebSocketChannel::PrimeNewOutgoingMessag
     if (mClientClosed) {
       DeleteCurrentOutGoingMessage();
       PrimeNewOutgoingMessage();
       return;
     }
 
     mClientClosed = 1;
     mOutHeader[0] = kFinalFragBit | kClose;
-    mOutHeader[1] = 0x02; // payload len = 2, maybe more for reason
-    mOutHeader[1] |= kMaskBit;
+    mOutHeader[1] = kMaskBit;
 
     // payload is offset 6 including 4 for the mask
     payload = mOutHeader + 6;
 
-    // length is 8 plus any reason information
-    mHdrOutToSend = 8;
-
     // The close reason code sits in the first 2 bytes of payload
     // If the channel user provided a code and reason during Close()
     // and there isn't an internal error, use that.
-    if (NS_SUCCEEDED(mStopOnClose) && mScriptCloseCode) {
-      *((PRUint16 *)payload) = PR_htons(mScriptCloseCode);
-      if (!mScriptCloseReason.IsEmpty()) {
-        NS_ABORT_IF_FALSE(mScriptCloseReason.Length() <= 123,
-                          "Close Reason Too Long");
-        mOutHeader[1] += mScriptCloseReason.Length();
-        mHdrOutToSend += mScriptCloseReason.Length();
-        memcpy (payload + 2,
-                mScriptCloseReason.BeginReading(),
-                mScriptCloseReason.Length());
+    if (NS_SUCCEEDED(mStopOnClose)) {
+      if (mScriptCloseCode) {
+        *((PRUint16 *)payload) = PR_htons(mScriptCloseCode);
+        mOutHeader[1] += 2;
+        mHdrOutToSend = 8;
+        if (!mScriptCloseReason.IsEmpty()) {
+          NS_ABORT_IF_FALSE(mScriptCloseReason.Length() <= 123,
+                            "Close Reason Too Long");
+          mOutHeader[1] += mScriptCloseReason.Length();
+          mHdrOutToSend += mScriptCloseReason.Length();
+          memcpy (payload + 2,
+                  mScriptCloseReason.BeginReading(),
+                  mScriptCloseReason.Length());
+        }
+      } else {
+        // No close code/reason, so payload length = 0.  We must still send mask
+        // even though it's not used.  Keep payload offset so we write mask
+        // below.
+        mHdrOutToSend = 6;
       }
     } else {
       *((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
+      mOutHeader[1] += 2;
+      mHdrOutToSend = 8;
     }
 
     if (mServerClosed) {
       /* bidi close complete */
       mReleaseOnTransmit = 1;
     } else if (NS_FAILED(mStopOnClose)) {
       /* result of abort session - give up */
       StopSession(mStopOnClose);
@@ -1499,40 +1506,39 @@ WebSocketChannel::PrimeNewOutgoingMessag
     mask = PR_ROTATE_LEFT32(mask, 8);
     payload++;
   }
 
   // Mask the real message payloads
 
   ApplyMask(mask, mCurrentOut->BeginWriting(), mCurrentOut->Length());
 
+  PRInt32 len = mCurrentOut->Length();
+
   // for small frames, copy it all together for a contiguous write
-  if (mCurrentOut->Length() <= kCopyBreak) {
-    memcpy(mOutHeader + mHdrOutToSend, mCurrentOut->BeginWriting(),
-           mCurrentOut->Length());
-    mHdrOutToSend += mCurrentOut->Length();
-    mCurrentOutSent = mCurrentOut->Length();
+  if (len && len <= kCopyBreak) {
+    memcpy(mOutHeader + mHdrOutToSend, mCurrentOut->BeginWriting(), len);
+    mHdrOutToSend += len;
+    mCurrentOutSent = len;
   }
 
-  if (mCompressor) {
+  if (len && mCompressor) {
     // assume a 1/3 reduction in size for sizing the buffer
     // the buffer is used multiple times if necessary
     PRUint32 currentHeaderSize = mHdrOutToSend;
     mHdrOutToSend = 0;
 
-    EnsureHdrOut(32 +
-                 (currentHeaderSize + mCurrentOut->Length() - mCurrentOutSent)
-                 / 2 * 3);
+    EnsureHdrOut(32 + (currentHeaderSize + len - mCurrentOutSent) / 2 * 3);
     mCompressor->Deflate(mOutHeader, currentHeaderSize,
                          mCurrentOut->BeginReading() + mCurrentOutSent,
-                         mCurrentOut->Length() - mCurrentOutSent);
+                         len - mCurrentOutSent);
 
     // All of the compressed data now resides in {mHdrOut, mHdrOutToSend}
     // so do not send the body again
-    mCurrentOutSent = mCurrentOut->Length();
+    mCurrentOutSent = len;
   }
 
   // Transmitting begins - mHdrOutToSend bytes from mOutHeader and
   // mCurrentOut->Length() bytes from mCurrentOut. The latter may be
   // coaleseced into the former for small messages or as the result of the
   // compression process,
 }
 
--- a/netwerk/protocol/websocket/nsIWebSocketChannel.idl
+++ b/netwerk/protocol/websocket/nsIWebSocketChannel.idl
@@ -43,17 +43,17 @@ interface nsILoadGroup;
 interface nsIWebSocketListener;
 interface nsIInputStream;
 
 #include "nsISupports.idl"
 
 /** 
  *  You probably want nsI{Moz}WebSocket.idl
  */
-[uuid(ace34548-6dde-4570-b0b4-451aa6a877e0)]
+[uuid(0683E9A4-994D-11E1-9478-1E356188709B)]
 interface nsIWebSocketChannel : nsISupports
 {
     /**
      * The original URI used to construct the protocol connection. This is used
      * in the case of a redirect or URI "resolution" (e.g. resolving a
      * resource: URI to a file: URI) so that the original pre-redirect
      * URI can still be obtained.  This is never null.
      */
@@ -129,20 +129,23 @@ interface nsIWebSocketChannel : nsISuppo
     const unsigned short CLOSE_UNSUPPORTED_DATATYPE = 1003;
     //  code 1004 is reserved
     const unsigned short CLOSE_NO_STATUS            = 1005;
     const unsigned short CLOSE_ABNORMAL             = 1006;
     const unsigned short CLOSE_INVALID_PAYLOAD      = 1007;
     const unsigned short CLOSE_POLICY_VIOLATION     = 1008;
     const unsigned short CLOSE_TOO_LARGE            = 1009;
     const unsigned short CLOSE_EXTENSION_MISSING    = 1010;
-
-    // Websocket spec doesn't have equivalent of HTTP 500 code for internal
-    // errors: just use CLOSE_GOING_AWAY for now
-    const unsigned short CLOSE_INTERNAL_ERROR      = CLOSE_GOING_AWAY;
+    // Initially used just for server-side internal errors: adopted later for
+    // client-side errors too (not clear if will make into spec: see 
+    // http://www.ietf.org/mail-archive/web/hybi/current/msg09372.html
+    const unsigned short CLOSE_INTERNAL_ERROR       = 1011;
+    // MUST NOT be set as a status code in Close control frame by an endpoint:
+    // To be used if TLS handshake failed (ex: server certificate unverifiable)
+    const unsigned short CLOSE_TLS_FAILED           = 1015;
 
     /**
      * Use to send text message down the connection to WebSocket peer.
      *
      * @param aMsg the utf8 string to send
      */
     void sendMsg(in AUTF8String aMsg);
 
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -244,16 +244,26 @@ Tester.prototype = {
       this.currentTest.scope = null;
     }
 
     // Check the window state for the current test before moving to the next one.
     // This also causes us to check before starting any tests, since nextTest()
     // is invoked to start the tests.
     this.waitForWindowsState((function () {
       if (this.done) {
+        // Many tests randomly add and remove tabs, resulting in the original
+        // tab being replaced by a new one. The last test in the suite doing this
+        // will erroneously be blamed for leaking this new tab's DOM window and
+        // docshell until shutdown. We can prevent this by removing this tab now
+        // that all tests are done.
+        if (window.gBrowser) {
+          gBrowser.addTab();
+          gBrowser.removeCurrentTab();
+        }
+
         // Schedule GC and CC runs before finishing in order to detect
         // DOM windows leaked by our tests or the tested code.
         Cu.schedulePreciseGC((function () {
           let winutils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIDOMWindowUtils);
           winutils.garbageCollect();
           winutils.garbageCollect();
           winutils.garbageCollect();
--- a/toolkit/components/ctypes/tests/unit/test_finalizer_shouldaccept.js
+++ b/toolkit/components/ctypes/tests/unit/test_finalizer_shouldaccept.js
@@ -1,16 +1,19 @@
 try {
   // We might be running without privileges, in which case it's up to the
   // harness to give us the 'ctypes' object.
   Components.utils.import("resource://gre/modules/ctypes.jsm");
 } catch(e) {
 }
 
-let acquire, dispose, reset_errno, dispose_errno, acquire_ptr, dispose_ptr;
+let acquire, dispose, reset_errno, dispose_errno,
+  acquire_ptr, dispose_ptr,
+  acquire_void_ptr, dispose_void_ptr,
+  acquire_string, dispose_string;
 
 function run_test()
 {
   let library = open_ctypes_test_lib();
 
   let start = library.declare("test_finalizer_start", ctypes.default_abi,
                           ctypes.void_t,
                           ctypes.size_t);
@@ -35,22 +38,31 @@ function run_test()
   acquire_ptr = library.declare("test_finalizer_acq_int32_ptr_t",
                                 ctypes.default_abi,
                                 ctypes.int32_t.ptr,
                                 ctypes.size_t);
   dispose_ptr = library.declare("test_finalizer_rel_int32_ptr_t",
                                 ctypes.default_abi,
                                 ctypes.void_t,
                                 ctypes.int32_t.ptr);
+  acquire_string = library.declare("test_finalizer_acq_string_t",
+                                ctypes.default_abi,
+                                ctypes.char.ptr,
+                                ctypes.size_t);
+  dispose_string = library.declare("test_finalizer_rel_string_t",
+                                ctypes.default_abi,
+                                ctypes.void_t,
+                                ctypes.char.ptr);
 
   tester.launch(10, test_to_string);
   tester.launch(10, test_to_source);
   tester.launch(10, test_to_int);
   tester.launch(10, test_errno);
   tester.launch(10, test_to_pointer);
+  tester.launch(10, test_readstring);
 }
 
 /**
  * Check that toString succeeds before/after forget/dispose.
  */
 function test_to_string()
 {
   do_print("Starting test_to_string");
@@ -141,8 +153,22 @@ function test_to_pointer()
   let ptr = ctypes.int32_t(2).address();
   let finalizable = ctypes.CDataFinalizer(ptr, dispose_ptr);
   let unwrapped = ctypes.int32_t.ptr(finalizable);
 
   do_check_eq(""+ptr, ""+unwrapped);
 
   finalizable.forget(); // Do not dispose: This is not a real pointer.
 }
+
+/**
+ * Test that readstring can be applied to a finalizer
+ */
+function test_readstring(size)
+{
+  for (let i = 0; i < size; ++i) {
+    let acquired = acquire_string(i);
+    let finalizable = ctypes.CDataFinalizer(acquired,
+      dispose_string);
+    do_check_eq(finalizable.readString(), acquired.readString());
+    finalizable.dispose();
+  }
+}
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -135,17 +135,17 @@ static NS_DEFINE_CID(kApplicationTracing
 static NS_DEFINE_CID(kPlacesInitCompleteCID,
   NS_PLACES_INIT_COMPLETE_EVENT_CID);
 static NS_DEFINE_CID(kSessionStoreWindowRestoredCID,
   NS_SESSION_STORE_WINDOW_RESTORED_EVENT_CID);
 #endif //defined(XP_WIN)
 
 using namespace mozilla;
 
-PRUint32 gRestartMode = 0;
+uint32_t gRestartMode = 0;
 
 class nsAppExitEvent : public nsRunnable {
 private:
   nsRefPtr<nsAppStartup> mService;
 
 public:
   nsAppExitEvent(nsAppStartup *service) : mService(service) {}
 
@@ -297,19 +297,19 @@ nsAppStartup::Run(void)
       return rv;
   }
 
   return mRestart ? NS_SUCCESS_RESTART_APP : NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsAppStartup::Quit(PRUint32 aMode)
+nsAppStartup::Quit(uint32_t aMode)
 {
-  PRUint32 ferocity = (aMode & 0xF);
+  uint32_t ferocity = (aMode & 0xF);
 
   // Quit the application. We will asynchronously call the appshell's
   // Exit() method via nsAppExitEvent to allow one last pass
   // through any events in the queue. This guarantees a tidy cleanup.
   nsresult rv = NS_OK;
   bool postedExitEvent = false;
 
   if (mShuttingDown)
@@ -368,17 +368,17 @@ nsAppStartup::Quit(PRUint32 aMode)
     mShuttingDown = true;
     if (!mRestart) {
       mRestart = (aMode & eRestart) != 0;
       gRestartMode = (aMode & 0xF0);
     }
 
     if (mRestart) {
       // Firefox-restarts reuse the process. Process start-time isn't a useful indicator of startup time
-      PR_SetEnv(PR_smprintf("MOZ_APP_RESTART=%lld", (PRInt64) PR_Now() / PR_USEC_PER_MSEC));
+      PR_SetEnv(PR_smprintf("MOZ_APP_RESTART=%lld", (int64_t) PR_Now() / PR_USEC_PER_MSEC));
     }
 
     obsService = mozilla::services::GetObserverService();
 
     if (!mAttemptingQuit) {
       mAttemptingQuit = true;
 #ifdef XP_MACOSX
       // now even the Mac wants to quit when the last window is closed
@@ -538,32 +538,32 @@ nsAppStartup::GetInterrupted(bool *aInte
 }
 
 //
 // nsAppStartup->nsIWindowCreator
 //
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
-                                 PRUint32 aChromeFlags,
+                                 uint32_t aChromeFlags,
                                  nsIWebBrowserChrome **_retval)
 {
   bool cancel;
   return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval);
 }
 
 
 //
 // nsAppStartup->nsIWindowCreator2
 //
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
-                                  PRUint32 aChromeFlags,
-                                  PRUint32 aContextFlags,
+                                  uint32_t aChromeFlags,
+                                  uint32_t aContextFlags,
                                   nsIURI *aURI,
                                   bool *aCancel,
                                   nsIWebBrowserChrome **_retval)
 {
   NS_ENSURE_ARG_POINTER(aCancel);
   NS_ENSURE_ARG_POINTER(_retval);
   *aCancel = false;
   *_retval = 0;
@@ -647,30 +647,30 @@ nsAppStartup::Observe(nsISupports *aSubj
   } else {
     NS_ERROR("Unexpected observer topic.");
   }
 
   return NS_OK;
 }
 
 #if defined(LINUX) || defined(ANDROID)
-static PRUint64 
+static uint64_t 
 JiffiesSinceBoot(const char *file)
 {
   char stat[512];
   FILE *f = fopen(file, "r");
   if (!f)
     return 0;
   int n = fread(&stat, 1, sizeof(stat) - 1, f);
   fclose(f);
   if (n <= 0)
     return 0;
   stat[n] = 0;
   
-  long long unsigned starttime = 0; // instead of PRUint64 to keep GCC quiet
+  long long unsigned starttime = 0; // instead of uint64_t to keep GCC quiet
   
   char *s = strrchr(stat, ')');
   if (!s)
     return 0;
   int ret = sscanf(s + 2,
                    "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u "
                    "%*u %*u %*u %*u %*u %*d %*d %*d %*d %llu",
                    &starttime);
@@ -685,18 +685,18 @@ ThreadedCalculateProcessCreationTimestam
   PRTime now = PR_Now();
   long hz = sysconf(_SC_CLK_TCK);
   if (!hz)
     return;
 
   char thread_stat[40];
   sprintf(thread_stat, "/proc/self/task/%d/stat", (pid_t) syscall(__NR_gettid));
   
-  PRUint64 thread_jiffies = JiffiesSinceBoot(thread_stat);
-  PRUint64 self_jiffies = JiffiesSinceBoot("/proc/self/stat");
+  uint64_t thread_jiffies = JiffiesSinceBoot(thread_stat);
+  uint64_t self_jiffies = JiffiesSinceBoot("/proc/self/stat");
   
   if (!thread_jiffies || !self_jiffies)
     return;
 
   PRTime interval = (thread_jiffies - self_jiffies) * PR_USEC_PER_SEC / hz;
   StartupTimeline::Record(StartupTimeline::PROCESS_CREATION, now - interval);
 }
 
@@ -718,17 +718,17 @@ CalculateProcessCreationTimestamp()
 static PRTime
 CalculateProcessCreationTimestamp()
 {
   FILETIME start, foo, bar, baz;
   bool success = GetProcessTimes(GetCurrentProcess(), &start, &foo, &bar, &baz);
   if (!success)
     return 0;
   // copied from NSPR _PR_FileTimeToPRTime
-  PRUint64 timestamp = 0;
+  uint64_t timestamp = 0;
   CopyMemory(&timestamp, &start, sizeof(PRTime));
 #ifdef __GNUC__
   timestamp = (timestamp - 116444736000000000LL) / 10LL;
 #else
   timestamp = (timestamp - 116444736000000000i64) / 10i64;
 #endif
   return timestamp;
 }
@@ -826,18 +826,18 @@ nsAppStartup::GetAutomaticSafeModeNecess
 
   *_retval = mIsSafeModeNecessary;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAppStartup::TrackStartupCrashBegin(bool *aIsSafeModeNecessary)
 {
-  const PRInt32 MAX_TIME_SINCE_STARTUP = 6 * 60 * 60 * 1000;
-  const PRInt32 MAX_STARTUP_BUFFER = 10;
+  const int32_t MAX_TIME_SINCE_STARTUP = 6 * 60 * 60 * 1000;
+  const int32_t MAX_STARTUP_BUFFER = 10;
   nsresult rv;
 
   mStartupCrashTrackingEnded = false;
 
   StartupTimeline::Record(StartupTimeline::STARTUP_CRASH_DETECTION_BEGIN);
 
   bool hasLastSuccess = Preferences::HasUserValue(kPrefLastSuccess);
   if (!hasLastSuccess) {
@@ -848,50 +848,50 @@ nsAppStartup::TrackStartupCrashBegin(boo
   }
 
   bool inSafeMode = false;
   nsCOMPtr<nsIXULRuntime> xr = do_GetService(XULRUNTIME_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(xr, NS_ERROR_FAILURE);
 
   xr->GetInSafeMode(&inSafeMode);
 
-  PRInt64 replacedLockTime;
+  int64_t replacedLockTime;
   rv = xr->GetReplacedLockTime(&replacedLockTime);
 
   if (NS_FAILED(rv) || !replacedLockTime) {
     if (!inSafeMode)
       Preferences::ClearUser(kPrefRecentCrashes);
     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
     return NS_OK;
   }
 
   // check whether safe mode is necessary
-  PRInt32 maxResumedCrashes = -1;
+  int32_t maxResumedCrashes = -1;
   rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes);
   NS_ENSURE_SUCCESS(rv, NS_OK);
 
-  PRInt32 recentCrashes = 0;
+  int32_t recentCrashes = 0;
   Preferences::GetInt(kPrefRecentCrashes, &recentCrashes);
   mIsSafeModeNecessary = (recentCrashes > maxResumedCrashes && maxResumedCrashes != -1);
 
   // Bug 731613 - Don't check if the last startup was a crash if XRE_PROFILE_PATH is set.  After
   // profile manager, the profile lock's mod. time has been changed so can't be used on this startup.
   // After a restart, it's safe to assume the last startup was successful.
   char *xreProfilePath = PR_GetEnv("XRE_PROFILE_PATH");
   if (xreProfilePath) {
     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // time of last successful startup
-  PRInt32 lastSuccessfulStartup;
+  int32_t lastSuccessfulStartup;
   rv = Preferences::GetInt(kPrefLastSuccess, &lastSuccessfulStartup);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRInt32 lockSeconds = (PRInt32)(replacedLockTime / PR_MSEC_PER_SEC);
+  int32_t lockSeconds = (int32_t)(replacedLockTime / PR_MSEC_PER_SEC);
 
   // started close enough to good startup so call it good
   if (lockSeconds <= lastSuccessfulStartup + MAX_STARTUP_BUFFER
       && lockSeconds >= lastSuccessfulStartup - MAX_STARTUP_BUFFER) {
     GetAutomaticSafeModeNecessary(aIsSafeModeNecessary);
     return NS_OK;
   }
 
@@ -948,26 +948,26 @@ nsAppStartup::TrackStartupCrashEnd()
 
   // Use the timestamp of XRE_main as an approximation for the lock file timestamp.
   // See MAX_STARTUP_BUFFER for the buffer time period.
   nsresult rv;
   PRTime mainTime = StartupTimeline::Get(StartupTimeline::MAIN);
   if (mainTime <= 0) {
     NS_WARNING("Could not get StartupTimeline::MAIN time.");
   } else {
-    PRInt32 lockFileTime = (PRInt32)(mainTime / PR_USEC_PER_SEC);
+    int32_t lockFileTime = (int32_t)(mainTime / PR_USEC_PER_SEC);
     rv = Preferences::SetInt(kPrefLastSuccess, lockFileTime);
     if (NS_FAILED(rv)) NS_WARNING("Could not set startup crash detection pref.");
   }
 
   if (inSafeMode && mIsSafeModeNecessary) {
     // On a successful startup in automatic safe mode, allow the user one more crash
     // in regular mode before returning to safe mode.
-    PRInt32 maxResumedCrashes = 0;
-    PRInt32 prefType;
+    int32_t maxResumedCrashes = 0;
+    int32_t prefType;
     rv = Preferences::GetDefaultRootBranch()->GetPrefType(kPrefMaxResumedCrashes, &prefType);
     NS_ENSURE_SUCCESS(rv, rv);
     if (prefType == nsIPrefBranch::PREF_INT) {
       rv = Preferences::GetInt(kPrefMaxResumedCrashes, &maxResumedCrashes);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     rv = Preferences::SetInt(kPrefRecentCrashes, maxResumedCrashes);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -978,15 +978,15 @@ nsAppStartup::TrackStartupCrashEnd()
   }
   nsCOMPtr<nsIPrefService> prefs = Preferences::GetService();
   rv = prefs->SavePrefFile(nsnull); // flush prefs to disk since we are tracking crashes
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsAppStartup::RestartInSafeMode(PRUint32 aQuitMode)
+nsAppStartup::RestartInSafeMode(uint32_t aQuitMode)
 {
   PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   this->Quit(aQuitMode | nsIAppStartup::eRestart);
 
   return NS_OK;
 }
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -23,49 +23,43 @@
   <body id="lic-info" class="aboutPageWideContainer">
     <h1><a id="top"></a>about:license</h1>
 
     <div>
 
 #ifdef APP_LICENSE_BLOCK
 #includesubst @APP_LICENSE_BLOCK@
 #endif
-
+    
     <p>All of the <b>source code</b> to this product is 
-       available under licenses which are both 
+       <a href="https://developer.mozilla.org/en/Mozilla_Source_Code_%28Mercurial%29">available</a> 
+       under licenses which are both 
        <a href="http://www.gnu.org/philosophy/free-sw.html">free</a> and 
        <a href="http://www.opensource.org/docs/definition.php">open source</a>.
-#ifdef SOURCE_REPO
-#ifdef SOURCE_CHANGESET
-       The specific source code for this product is identified by the URL
-       <a href="@SOURCE_REPO@/rev/@SOURCE_CHANGESET@">@SOURCE_REPO@/rev/@SOURCE_CHANGESET@</a>,
-       and you can read 
-       <a href="https://developer.mozilla.org/en/Mozilla_Source_Code_%28Mercurial%29">instructions 
-       on how to download and build it for yourself</a>.
-#endif
-#endif
+       Most of it is available under the Mozilla Public License 2.0 (MPL).
     </p>
 
-    <p>More specifically, most of the source code is available under the 
-       <a href="about:license#mpl">Mozilla Public License 2.0</a> (MPL).
-       The MPL has a 
-       <a href="http://www.mozilla.org/MPL/2.0/FAQ.html">FAQ</a> to help
-       you understand it. The remainder of the software which is not 
-       under the MPL is available under one of a variety of other 
-       permissive licenses. Those that require reproduction 
+    <ul>
+      <li><a href="about:license#mpl">Mozilla Public License 2.0</a>
+      <ul>
+        <li><a href="http://www.mozilla.org/MPL/2.0/FAQ.html">MPL 2.0 FAQ</a></li>
+      </ul>
+      </li>
+    </ul>
+    
+    <p>The remainder of the software which is not under the MPL is available
+       under one of
+       a variety of other licenses. Those that require reproduction 
        of the license text in the distribution are given below. 
        (Note: your copy of this product may not contain code covered by one 
        or more of the licenses listed here, depending on the exact product 
        and version you choose.)
     </p>
     
     <ul>
-      <li><a href="about:license#mpl">Mozilla Public License 2.0</a>
-      <br><br>
-      </li>
       <li><a href="about:license#android">Android Open Source License</a></li>
       <li><a href="about:license#angle">ANGLE License</a></li>
       <li><a href="about:license#apache">Apache License 2.0</a></li>
       <li><a href="about:license#apple">Apple License</a></li>
       <li><a href="about:license#apple-mozilla">Apple/Mozilla NPRuntime License</a></li>
       <li><a href="about:license#apple-torch">Apple/Torch Mobile License</a></li>
       <li><a href="about:license#breakpad">Breakpad License</a></li>
       <li><a href="about:license#bspatch">bspatch License</a></li>
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -247,17 +247,17 @@ Java_org_mozilla_gecko_GeckoAppShell_not
         return NS_OK;
       }
 
     private:
       SmsMessageData mMessageData;
     };
 
     SmsMessageData message(0, eDeliveryState_Received, nsJNIString(aSender, jenv), EmptyString(),
-                           nsJNIString(aBody, jenv), aTimestamp);
+                           nsJNIString(aBody, jenv), aTimestamp, false);
 
     nsCOMPtr<nsIRunnable> runnable = new NotifySmsReceivedRunnable(message);
     NS_DispatchToMainThread(runnable);
 }
 
 NS_EXPORT PRInt32 JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_saveMessageInSentbox(JNIEnv* jenv, jclass,
                                                           jstring aReceiver,
@@ -333,17 +333,17 @@ Java_org_mozilla_gecko_GeckoAppShell_not
     private:
       SmsMessageData mMessageData;
       PRInt32        mRequestId;
       PRUint64       mProcessId;
     };
 
     SmsMessageData message(aId, eDeliveryState_Sent, EmptyString(),
                            nsJNIString(aReceiver, jenv),
-                           nsJNIString(aBody, jenv), aTimestamp);
+                           nsJNIString(aBody, jenv), aTimestamp, true);
 
     nsCOMPtr<nsIRunnable> runnable = new NotifySmsSentRunnable(message, aRequestId, aProcessId);
     NS_DispatchToMainThread(runnable);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifySmsDelivered(JNIEnv* jenv, jclass,
                                                         jint aId,
@@ -370,17 +370,17 @@ Java_org_mozilla_gecko_GeckoAppShell_not
       }
 
     private:
       SmsMessageData mMessageData;
     };
 
     SmsMessageData message(aId, eDeliveryState_Sent, EmptyString(),
                            nsJNIString(aReceiver, jenv),
-                           nsJNIString(aBody, jenv), aTimestamp);
+                           nsJNIString(aBody, jenv), aTimestamp, true);
 
     nsCOMPtr<nsIRunnable> runnable = new NotifySmsDeliveredRunnable(message);
     NS_DispatchToMainThread(runnable);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifySmsSendFailed(JNIEnv* jenv, jclass,
                                                          jint aError,
@@ -476,18 +476,19 @@ Java_org_mozilla_gecko_GeckoAppShell_not
       PRInt32        mRequestId;
       PRUint64       mProcessId;
     };
 
     nsJNIString receiver = nsJNIString(aReceiver, jenv);
     DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
                                              : eDeliveryState_Sent;
 
+    // TODO Need to add the message `read` parameter value. Bug 748391
     SmsMessageData message(aId, state, nsJNIString(aSender, jenv), receiver,
-                           nsJNIString(aBody, jenv), aTimestamp);
+                           nsJNIString(aBody, jenv), aTimestamp, true);
 
     nsCOMPtr<nsIRunnable> runnable = new NotifyGetSmsRunnable(message, aRequestId, aProcessId);
     NS_DispatchToMainThread(runnable);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifyGetSmsFailed(JNIEnv* jenv, jclass,
                                                         jint aError,
@@ -731,18 +732,19 @@ Java_org_mozilla_gecko_GeckoAppShell_not
       PRUint64       mProcessId;
     };
 
 
     nsJNIString receiver = nsJNIString(aReceiver, jenv);
     DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
                                              : eDeliveryState_Sent;
 
+    // TODO Need to add the message `read` parameter value. Bug 748391
     SmsMessageData message(aMessageId, state, nsJNIString(aSender, jenv),
-                           receiver, nsJNIString(aBody, jenv), aTimestamp);
+                           receiver, nsJNIString(aBody, jenv), aTimestamp, true);
 
     nsCOMPtr<nsIRunnable> runnable =
       new NotifyCreateMessageListRunnable(aListId, message, aRequestId, aProcessId);
     NS_DispatchToMainThread(runnable);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifyGotNextMessage(JNIEnv* jenv, jclass,
@@ -790,19 +792,20 @@ Java_org_mozilla_gecko_GeckoAppShell_not
       PRInt32        mRequestId;
       PRUint64       mProcessId;
     };
 
 
     nsJNIString receiver = nsJNIString(aReceiver, jenv);
     DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
                                              : eDeliveryState_Sent;
-
+ 
+    // TODO Need to add the message `read` parameter value. Bug 748391
     SmsMessageData message(aMessageId, state, nsJNIString(aSender, jenv),
-                           receiver, nsJNIString(aBody, jenv), aTimestamp);
+                           receiver, nsJNIString(aBody, jenv), aTimestamp, true);
 
     nsCOMPtr<nsIRunnable> runnable =
       new NotifyGotNextMessageRunnable(message, aRequestId, aProcessId);
     NS_DispatchToMainThread(runnable);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_notifyReadingMessageListFailed(JNIEnv* jenv, jclass,
--- a/xpcom/system/nsIXULAppInfo.idl
+++ b/xpcom/system/nsIXULAppInfo.idl
@@ -36,17 +36,17 @@
 
 #include "nsISupports.idl"
 
 /**
  * A scriptable interface to the nsXULAppAPI structure. See nsXULAppAPI.h for
  * a detailed description of each attribute.
  */
 
-[scriptable, uuid(a61ede2a-ef09-11d9-a5ce-001124787b2e)]
+[scriptable, uuid(1518e7d2-022a-4dae-b02e-bbe7ffcf2145)]
 interface nsIXULAppInfo : nsISupports
 {
   /**
    * @see nsXREAppData.vendor
    * @returns an empty string if nsXREAppData.vendor is not set.
    */
   readonly attribute ACString vendor;