Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 12 Jul 2013 15:51:48 -0400
changeset 138381 35fca04046338dd43273add82a3ead5ba60c0d7f
parent 138345 50434a448009a5dfe1448408854250b8a25e82a1 (current diff)
parent 138380 3433a021847b587aaa2525817abdc0b91b350278 (diff)
child 138382 becae65c3e08009e031a71d9fbd6a8f1106d5161
push id30945
push userryanvm@gmail.com
push dateFri, 12 Jul 2013 19:51:45 +0000
treeherdermozilla-inbound@35fca0404633 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound.
dom/network/tests/marionette/test_mobile_iccinfo.js
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -670,17 +670,17 @@ function getListForElement(element) {
 
 // Create a plain text document encode from the focused element.
 function getDocumentEncoder(element) {
   let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"]
                 .createInstance(Ci.nsIDocumentEncoder);
   let flags = Ci.nsIDocumentEncoder.SkipInvisibleContent |
               Ci.nsIDocumentEncoder.OutputRaw |
               Ci.nsIDocumentEncoder.OutputLFLineBreak |
-              Ci.nsIDocumentEncoder.OutputDropInvisibleBreak;
+              Ci.nsIDocumentEncoder.OutputNonTextContentAsPlaceholder;
   encoder.init(element.ownerDocument, "text/plain", flags);
   return encoder;
 }
 
 // Get the visible content text of a content editable element
 function getContentEditableText(element) {
   let doc = element.ownerDocument;
   let range = doc.createRange();
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "8f402e9a5c9a8ba8b5b7d6345952fbb5a7db2d69", 
+    "revision": "93d613b62a4697824f918b3625da100d2e177eaa", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -46,16 +46,17 @@ MOCHITEST_FILES += \
         subtst_contextmenu.html \
         test_contextmenu.html \
         $(NULL)
 endif
 
 # The following tests are disabled because they are unreliable:
 #   browser_bug423833.js is bug 428712
 #   browser_sanitize-download-history.js is bug 432425
+#   browser_aboutHome.js is bug 890409
 #
 # browser_sanitizeDialog_treeView.js is disabled until the tree view is added
 # back to the clear recent history dialog (sanitize.xul), if it ever is (bug
 # 480169)
 
 # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
 
 # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
@@ -66,17 +67,16 @@ MOCHITEST_BROWSER_FILES = \
                  app_bug575561.html \
                  app_subframe_bug575561.html \
                  authenticate.sjs \
                  blockNoPlugins.xml \
                  blockPluginHard.xml \
                  blockPluginVulnerableNoUpdate.xml \
                  blockPluginVulnerableUpdatable.xml \
                  browser_aboutHealthReport.js \
-                 browser_aboutHome.js \
                  browser_aboutSyncProgress.js \
                  browser_addKeywordSearch.js \
                  browser_addon_bar_aomlistener.js \
                  browser_addon_bar_close_button.js \
                  browser_addon_bar_shortcut.js \
                  browser_alltabslistener.js \
                  browser_blob-channelname.js \
                  browser_bug304198.js \
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -84,87 +84,55 @@ let gTests = [
        "Search engine logo's alt text is a nonempty string");
 
     isnot(altText, "undefined",
           "Search engine logo's alt text shouldn't be the string 'undefined'");
   }
 },
 
 {
-  desc: "Check that performing a search fires a search event.",
-  setup: function () { },
-  run: function () {
-    let deferred = Promise.defer();
-    let doc = gBrowser.contentDocument;
-
-    doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
-      is(e.detail, doc.documentElement.getAttribute("searchEngineName"), "Detail is search engine name");
-
-      gBrowser.stop();
-      deferred.resolve();
-    }, true, true);
-
-    doc.getElementById("searchText").value = "it works";
-    doc.getElementById("searchSubmit").click();
-    return deferred.promise;
-  }
-},
-
-{
-  desc: "Check that performing a search records to Firefox Health Report.",
+  desc: "Check that performing a search fires a search event and records to " +
+        "Firefox Health Report.",
   setup: function () { },
   run: function () {
     try {
       let cm = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
       cm.getCategoryEntry("healthreport-js-provider-default", "SearchesProvider");
     } catch (ex) {
       // Health Report disabled, or no SearchesProvider.
       return Promise.resolve();
     }
 
+    let numSearchesBefore = 0;
     let deferred = Promise.defer();
     let doc = gBrowser.contentDocument;
 
     // We rely on the listener in browser.js being installed and fired before
     // this one. If this ever changes, we should add an executeSoon() or similar.
     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
-      executeSoon(gBrowser.stop.bind(gBrowser));
-      let reporter = Components.classes["@mozilla.org/datareporting/service;1"]
-                                       .getService()
-                                       .wrappedJSObject
-                                       .healthReporter;
-      ok(reporter, "Health Reporter instance available.");
-
-      reporter.onInit().then(function onInit() {
-        let provider = reporter.getProvider("org.mozilla.searches");
-        ok(provider, "Searches provider is available.");
-
-        let engineName = doc.documentElement.getAttribute("searchEngineName");
-        let id = Services.search.getEngineByName(engineName).identifier;
+      let engineName = doc.documentElement.getAttribute("searchEngineName");
+      is(e.detail, engineName, "Detail is search engine name");
 
-        let m = provider.getMeasurement("counts", 2);
-        m.getValues().then(function onValues(data) {
-          let now = new Date();
-          ok(data.days.hasDay(now), "Have data for today.");
+      gBrowser.stop();
 
-          let day = data.days.getDay(now);
-          let field = id + ".abouthome";
-          ok(day.has(field), "Have data for about home on this engine.");
-
-          // Note the search from the previous test.
-          is(day.get(field), 2, "Have searches recorded.");
-
-          deferred.resolve();
-        });
-
+      getNumberOfSearches().then(num => {
+        is(num, numSearchesBefore + 1, "One more search recorded.");
+        deferred.resolve();
       });
     }, true, true);
 
-    doc.getElementById("searchText").value = "a search";
-    doc.getElementById("searchSubmit").click();
+    // Get the current number of recorded searches.
+    getNumberOfSearches().then(num => {
+      numSearchesBefore = num;
+
+      info("Perform a search.");
+      doc.getElementById("searchText").value = "a search";
+      doc.getElementById("searchSubmit").click();
+    });
+
     return deferred.promise;
   }
 },
 
 {
   desc: "Check snippets map is cleared if cached version is old",
   setup: function (aSnippetsMap)
   {
@@ -417,8 +385,59 @@ function promiseBrowserAttributes(aTab)
       }
     }
   });
   info("Add attributes observer");
   observer.observe(docElt, { attributes: true });
 
   return deferred.promise;
 }
+
+/**
+ * Retrieves the number of about:home searches recorded for the current day.
+ *
+ * @return {Promise} Returns a promise resolving to the number of searches.
+ */
+function getNumberOfSearches() {
+  let reporter = Components.classes["@mozilla.org/datareporting/service;1"]
+                                   .getService()
+                                   .wrappedJSObject
+                                   .healthReporter;
+  ok(reporter, "Health Reporter instance available.");
+
+  return reporter.onInit().then(function onInit() {
+    let provider = reporter.getProvider("org.mozilla.searches");
+    ok(provider, "Searches provider is available.");
+
+    let m = provider.getMeasurement("counts", 2);
+    return m.getValues().then(data => {
+      let now = new Date();
+      let yday = new Date(now);
+      yday.setDate(yday.getDate() - 1);
+
+      // Add the number of searches recorded yesterday to the number of searches
+      // recorded today. This makes the test not fail intermittently when it is
+      // run at midnight and we accidentally compare the number of searches from
+      // different days. Tests are always run with an empty profile so there
+      // are no searches from yesterday, normally. Should the test happen to run
+      // past midnight we make sure to count them in as well.
+      return getNumberOfSearchesByDate(data, now) +
+             getNumberOfSearchesByDate(data, yday);
+    });
+  });
+}
+
+function getNumberOfSearchesByDate(aData, aDate) {
+  if (aData.days.hasDay(aDate)) {
+    let doc = gBrowser.contentDocument;
+    let engineName = doc.documentElement.getAttribute("searchEngineName");
+    let id = Services.search.getEngineByName(engineName).identifier;
+
+    let day = aData.days.getDay(aDate);
+    let field = id + ".abouthome";
+
+    if (day.has(field)) {
+      return day.get(field) || 0;
+    }
+  }
+
+  return 0; // No records found.
+}
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -455,17 +455,21 @@ let SessionStoreInternal = {
           });
         }
       }
       catch (ex) { debug("The session file is invalid: " + ex); }
     }
 
     // A Lazy getter for the sessionstore.js backup promise.
     XPCOMUtils.defineLazyGetter(this, "_backupSessionFileOnce", function () {
-      return _SessionFile.createBackupCopy();
+      // We're creating a backup of sessionstore.js by moving it to .bak
+      // because that's a lot faster than creating a copy. sessionstore.js
+      // would be overwritten shortly afterwards anyway so we can save time
+      // and just move instead of copy.
+      return _SessionFile.moveToBackupPath();
     });
 
     // at this point, we've as good as resumed the session, so we can
     // clear the resume_session_once flag, if it's set
     if (this._loadState != STATE_QUITTING &&
         this._prefBranch.getBoolPref("sessionstore.resume_session_once"))
       this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
 
@@ -499,22 +503,22 @@ let SessionStoreInternal = {
     let buildID = Services.appinfo.platformBuildID;
     let latestBackup = this._prefBranch.getCharPref(PREF_UPGRADE);
     if (latestBackup == buildID) {
       return Promise.resolve();
     }
     return Task.spawn(function task() {
       try {
         // Perform background backup
-        yield _SessionFile.createUpgradeBackupCopy("-" + buildID);
+        yield _SessionFile.createBackupCopy("-" + buildID);
 
         this._prefBranch.setCharPref(PREF_UPGRADE, buildID);
 
         // In case of success, remove previous backup.
-        yield _SessionFile.removeUpgradeBackup("-" + latestBackup);
+        yield _SessionFile.removeBackupCopy("-" + latestBackup);
       } catch (ex) {
         debug("Could not perform upgrade backup " + ex);
         debug(ex.stack);
       }
     }.bind(this));
   },
 
   _initEncoding : function ssi_initEncoding() {
@@ -767,22 +771,22 @@ let SessionStoreInternal = {
           Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
         } else {
           TelemetryTimestamps.add("sessionRestoreRestoring");
           // make sure that the restored tabs are first in the window
           this._initialState._firstTabs = true;
           this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
           this.restoreWindow(aWindow, this._initialState,
                              this._isCmdLineEmpty(aWindow, this._initialState));
-
-          // _loadState changed from "stopped" to "running"
-          // force a save operation so that crashes happening during startup are correctly counted
-          this._initialState.session.state = STATE_RUNNING_STR;
-          this._saveStateObject(this._initialState);
           this._initialState = null;
+
+          // _loadState changed from "stopped" to "running". Save the session's
+          // load state immediately so that crashes happening during startup
+          // are correctly counted.
+          _SessionFile.writeLoadStateOnceAfterStartup(STATE_RUNNING_STR);
         }
       }
       else {
         // Nothing to restore, notify observers things are complete.
         Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
 
         // the next delayed save request should execute immediately
         this._lastSaveTime -= this._interval;
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/src/SessionWorker.js
@@ -0,0 +1,221 @@
+/* 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/. */
+
+/**
+ * A worker dedicated to handle I/O for Session Store.
+ */
+
+"use strict";
+
+importScripts("resource://gre/modules/osfile.jsm");
+
+let File = OS.File;
+let Encoder = new TextEncoder();
+let Decoder = new TextDecoder();
+
+/**
+ * Communications with the controller.
+ *
+ * Accepts messages:
+ * {fun:function_name, args:array_of_arguments_or_null, id: custom_id}
+ *
+ * Sends messages:
+ * {ok: result, id: custom_id} / {fail: serialized_form_of_OS.File.Error,
+ *                                id: custom_id}
+ */
+self.onmessage = function (msg) {
+  let data = msg.data;
+  if (!(data.fun in Agent)) {
+    throw new Error("Cannot find method " + data.fun);
+  }
+
+  let result;
+  let id = data.id;
+
+  try {
+    result = Agent[data.fun].apply(Agent, data.args);
+  } catch (ex if ex instanceof OS.File.Error) {
+    // Instances of OS.File.Error know how to serialize themselves
+    // (deserialization ensures that we end up with OS-specific
+    // instances of |OS.File.Error|)
+    self.postMessage({fail: OS.File.Error.toMsg(ex), id: id});
+    return;
+  }
+
+  // Other exceptions do not, and should be propagated through DOM's
+  // built-in mechanism for uncaught errors, although this mechanism
+  // may lose interesting information.
+  self.postMessage({ok: result, id: id});
+};
+
+let Agent = {
+  // The initial session string as read from disk.
+  initialState: null,
+
+  // Boolean that tells whether we already wrote
+  // the loadState to disk once after startup.
+  hasWrittenLoadStateOnce: false,
+
+  // The path to sessionstore.js
+  path: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"),
+
+  // The path to sessionstore.bak
+  backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"),
+
+  /**
+   * This method is only intended to be called by _SessionFile.syncRead() and
+   * can be removed when we're not supporting synchronous SessionStore
+   * initialization anymore. When sessionstore.js is read from disk
+   * synchronously the state string must be supplied to the worker manually by
+   * calling this method.
+   */
+  setInitialState: function (aState) {
+    // _SessionFile.syncRead() should not be called after startup has finished.
+    // Thus we also don't support any setInitialState() calls after we already
+    // wrote the loadState to disk.
+    if (this.hasWrittenLoadStateOnce) {
+      throw new Error("writeLoadStateOnceAfterStartup() must only be called once.");
+    }
+
+    // Initial state might have been filled by read() already but yet we might
+    // be called by _SessionFile.syncRead() before SessionStore.jsm had a chance
+    // to call writeLoadStateOnceAfterStartup(). It's safe to ignore
+    // setInitialState() calls if this happens.
+    if (!this.initialState) {
+      this.initialState = aState;
+    }
+  },
+
+  /**
+   * Read the session from disk.
+   * In case sessionstore.js does not exist, attempt to read sessionstore.bak.
+   */
+  read: function () {
+    for (let path of [this.path, this.backupPath]) {
+      try {
+        return this.initialState = Decoder.decode(File.read(path));
+      } catch (ex if isNoSuchFileEx(ex)) {
+        // Ignore exceptions about non-existent files.
+      }
+    }
+
+    // No sessionstore data files found. Return an empty string.
+    return "";
+  },
+
+  /**
+   * Write the session to disk.
+   */
+  write: function (stateString) {
+    let bytes = Encoder.encode(stateString);
+    return File.writeAtomic(this.path, bytes, {tmpPath: this.path + ".tmp"});
+  },
+
+  /**
+   * Writes the session state to disk again but changes session.state to
+   * 'running' before doing so. This is intended to be called only once, shortly
+   * after startup so that we detect crashes on startup correctly.
+   */
+  writeLoadStateOnceAfterStartup: function (loadState) {
+    if (this.hasWrittenLoadStateOnce) {
+      throw new Error("writeLoadStateOnceAfterStartup() must only be called once.");
+    }
+
+    if (!this.initialState) {
+      throw new Error("writeLoadStateOnceAfterStartup() must not be called " +
+                      "without a valid session state or before it has been " +
+                      "read from disk.");
+    }
+
+    // Make sure we can't call this function twice.
+    this.hasWrittenLoadStateOnce = true;
+
+    let state;
+    try {
+      state = JSON.parse(this.initialState);
+    } finally {
+      this.initialState = null;
+    }
+
+    state.session = state.session || {};
+    state.session.state = loadState;
+    return this.write(JSON.stringify(state));
+  },
+
+  /**
+   * Moves sessionstore.js to sessionstore.bak.
+   */
+  moveToBackupPath: function () {
+    try {
+      return File.move(this.path, this.backupPath);
+    } catch (ex if isNoSuchFileEx(ex)) {
+      // Ignore exceptions about non-existent files.
+      return true;
+    }
+  },
+
+  /**
+   * Creates a copy of sessionstore.js.
+   */
+  createBackupCopy: function (ext) {
+    try {
+      return File.copy(this.path, this.backupPath + ext);
+    } catch (ex if isNoSuchFileEx(ex)) {
+      // Ignore exceptions about non-existent files.
+      return true;
+    }
+  },
+
+  /**
+   * Removes a backup copy.
+   */
+  removeBackupCopy: function (ext) {
+    try {
+      return File.remove(this.backupPath + ext);
+    } catch (ex if isNoSuchFileEx(ex)) {
+      // Ignore exceptions about non-existent files.
+      return true;
+    }
+  },
+
+  /**
+   * Wipes all files holding session data from disk.
+   */
+  wipe: function () {
+    let exn;
+
+    // Erase session state file
+    try {
+      File.remove(this.path);
+    } catch (ex if isNoSuchFileEx(ex)) {
+      // Ignore exceptions about non-existent files.
+    } catch (ex) {
+      // Don't stop immediately.
+      exn = ex;
+    }
+
+    // Erase any backup, any file named "sessionstore.bak[-buildID]".
+    let iter = new File.DirectoryIterator(OS.Constants.Path.profileDir);
+    for (let entry in iter) {
+      if (!entry.isDir && entry.path.startsWith(this.backupPath)) {
+        try {
+          File.remove(entry.path);
+        } catch (ex) {
+          // Don't stop immediately.
+          exn = exn || ex;
+        }
+      }
+    }
+
+    if (exn) {
+      throw exn;
+    }
+
+    return true;
+  }
+};
+
+function isNoSuchFileEx(aReason) {
+  return aReason instanceof OS.File.Error && aReason.becauseNoSuchFile;
+}
--- a/browser/components/sessionstore/src/_SessionFile.jsm
+++ b/browser/components/sessionstore/src/_SessionFile.jsm
@@ -27,88 +27,85 @@ this.EXPORTED_SYMBOLS = ["_SessionFile"]
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
   "@mozilla.org/base/telemetry;1", "nsITelemetry");
-
-// An encoder to UTF-8.
-XPCOMUtils.defineLazyGetter(this, "gEncoder", function () {
-  return new TextEncoder();
-});
-// A decoder.
-XPCOMUtils.defineLazyGetter(this, "gDecoder", function () {
-  return new TextDecoder();
-});
+XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
+  "resource://gre/modules/Deprecated.jsm");
 
 this._SessionFile = {
   /**
-   * A promise fulfilled once initialization (either synchronous or
-   * asynchronous) is complete.
-   */
-  promiseInitialized: function SessionFile_initialized() {
-    return SessionFileInternal.promiseInitialized;
-  },
-  /**
    * Read the contents of the session file, asynchronously.
    */
-  read: function SessionFile_read() {
+  read: function () {
     return SessionFileInternal.read();
   },
   /**
    * Read the contents of the session file, synchronously.
    */
-  syncRead: function SessionFile_syncRead() {
+  syncRead: function () {
+    Deprecated.warning(
+      "syncRead is deprecated and will be removed in a future version",
+      "https://bugzilla.mozilla.org/show_bug.cgi?id=532150")
     return SessionFileInternal.syncRead();
   },
   /**
    * Write the contents of the session file, asynchronously.
    */
-  write: function SessionFile_write(aData) {
+  write: function (aData) {
     return SessionFileInternal.write(aData);
   },
   /**
+   * Writes the initial state to disk again only to change the session's load
+   * state. This must only be called once, it will throw an error otherwise.
+   */
+  writeLoadStateOnceAfterStartup: function (aLoadState) {
+    return SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState);
+  },
+  /**
    * Create a backup copy, asynchronously.
    */
-  createBackupCopy: function SessionFile_createBackupCopy() {
-    return SessionFileInternal.createBackupCopy();
+  moveToBackupPath: function () {
+    return SessionFileInternal.moveToBackupPath();
   },
   /**
    * Create a backup copy, asynchronously.
    * This is designed to perform backup on upgrade.
    */
-  createUpgradeBackupCopy: function(ext) {
-    return SessionFileInternal.createUpgradeBackupCopy(ext);
+  createBackupCopy: function (ext) {
+    return SessionFileInternal.createBackupCopy(ext);
   },
   /**
    * Remove a backup copy, asynchronously.
    * This is designed to clean up a backup on upgrade.
    */
-  removeUpgradeBackup: function(ext) {
-    return SessionFileInternal.removeUpgradeBackup(ext);
+  removeBackupCopy: function (ext) {
+    return SessionFileInternal.removeBackupCopy(ext);
   },
   /**
    * Wipe the contents of the session file, asynchronously.
    */
-  wipe: function SessionFile_wipe() {
+  wipe: function () {
     return SessionFileInternal.wipe();
   }
 };
 
 Object.freeze(_SessionFile);
 
 /**
  * Utilities for dealing with promises and Task.jsm
@@ -143,37 +140,32 @@ const TaskUtils = {
    */
   spawn: function spawn(gen) {
     return this.captureErrors(Task.spawn(gen));
   }
 };
 
 let SessionFileInternal = {
   /**
-   * A promise fulfilled once initialization is complete
-   */
-  promiseInitialized: Promise.defer(),
-
-  /**
    * The path to sessionstore.js
    */
   path: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"),
 
   /**
    * The path to sessionstore.bak
    */
   backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"),
 
   /**
    * Utility function to safely read a file synchronously.
    * @param aPath
    *        A path to read the file from.
    * @returns string if successful, undefined otherwise.
    */
-  readAuxSync: function ssfi_readAuxSync(aPath) {
+  readAuxSync: function (aPath) {
     let text;
     try {
       let file = new FileUtils.File(aPath);
       let chan = NetUtil.newChannel(file);
       let stream = chan.open();
       text = NetUtil.readInputStreamToString(stream, stream.available(),
         {charset: "utf-8"});
     } catch (e if e.result == Components.results.NS_ERROR_FILE_NOT_FOUND) {
@@ -192,195 +184,96 @@ let SessionFileInternal = {
    * This function is meant to serve as a fallback in case of race
    * between a synchronous usage of the API and asynchronous
    * initialization.
    *
    * In case if sessionstore.js file does not exist or is corrupted (something
    * happened between backup and write), attempt to read the sessionstore.bak
    * instead.
    */
-  syncRead: function ssfi_syncRead() {
+  syncRead: function () {
     // Start measuring the duration of the synchronous read.
     TelemetryStopwatch.start("FX_SESSION_RESTORE_SYNC_READ_FILE_MS");
     // First read the sessionstore.js.
     let text = this.readAuxSync(this.path);
     if (typeof text === "undefined") {
       // If sessionstore.js does not exist or is corrupted, read sessionstore.bak.
       text = this.readAuxSync(this.backupPath);
     }
     // Finish the telemetry probe and return an empty string.
     TelemetryStopwatch.finish("FX_SESSION_RESTORE_SYNC_READ_FILE_MS");
-    return text || "";
-  },
+    text = text || "";
 
-  /**
-   * Utility function to safely read a file asynchronously.
-   * @param aPath
-   *        A path to read the file from.
-   * @param aReadOptions
-   *        Read operation options.
-   *        |outExecutionDuration| option will be reused and can be
-   *        incrementally updated by the worker process.
-   * @returns string if successful, undefined otherwise.
-   */
-  readAux: function ssfi_readAux(aPath, aReadOptions) {
-    let self = this;
-    return TaskUtils.spawn(function () {
-      let text;
-      try {
-        let bytes = yield OS.File.read(aPath, undefined, aReadOptions);
-        text = gDecoder.decode(bytes);
-        // If the file is read successfully, add a telemetry probe based on
-        // the updated duration value of the |outExecutionDuration| option.
-        let histogram = Telemetry.getHistogramById(
-          "FX_SESSION_RESTORE_READ_FILE_MS");
-        histogram.add(aReadOptions.outExecutionDuration);
-      } catch (ex if self._isNoSuchFile(ex)) {
-        // Ignore exceptions about non-existent files.
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-      throw new Task.Result(text);
-    });
+    // The worker needs to know the initial state read from
+    // disk so that writeLoadStateOnceAfterStartup() works.
+    SessionWorker.post("setInitialState", [text]);
+    return text;
   },
 
-  /**
-   * Read the sessionstore file asynchronously.
-   *
-   * In case sessionstore.js file does not exist or is corrupted (something
-   * happened between backup and write), attempt to read the sessionstore.bak
-   * instead.
-   */
-  read: function ssfi_read() {
-    let self = this;
-    return TaskUtils.spawn(function task() {
-      // Specify |outExecutionDuration| option to hold the combined duration of
-      // the asynchronous reads off the main thread (of both sessionstore.js and
-      // sessionstore.bak, if necessary). If sessionstore.js does not exist or
-      // is corrupted, |outExecutionDuration| will register the time it took to
-      // attempt to read the file. It will then be subsequently incremented by
-      // the read time of sessionsore.bak.
-      let readOptions = {
-        outExecutionDuration: null
-      };
-      // First read the sessionstore.js.
-      let text = yield self.readAux(self.path, readOptions);
-      if (typeof text === "undefined") {
-        // If sessionstore.js does not exist or is corrupted, read the
-        // sessionstore.bak.
-        text = yield self.readAux(self.backupPath, readOptions);
-      }
-      // Return either the content of the sessionstore.bak if it was read
-      // successfully or an empty string otherwise.
-      throw new Task.Result(text || "");
-    });
+  read: function () {
+    return SessionWorker.post("read").then(msg => msg.ok);
   },
 
-  write: function ssfi_write(aData) {
+  write: function (aData) {
     let refObj = {};
-    let self = this;
     return TaskUtils.spawn(function task() {
       TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
       TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
 
-      let bytes = gEncoder.encode(aData);
-
       try {
-        let promise = OS.File.writeAtomic(self.path, bytes, {tmpPath: self.path + ".tmp"});
+        let promise = SessionWorker.post("write", [aData]);
         // At this point, we measure how long we stop the main thread
         TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
 
         // Now wait for the result and measure how long we had to wait for the result
         yield promise;
         TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
       } catch (ex) {
         TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
         TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
-        Cu.reportError("Could not write session state file " + self.path
-                       + ": " + aReason);
-      }
-    });
-  },
-
-  createBackupCopy: function ssfi_createBackupCopy() {
-    let backupCopyOptions = {
-      outExecutionDuration: null
-    };
-    let self = this;
-    return TaskUtils.spawn(function task() {
-      try {
-        yield OS.File.move(self.path, self.backupPath, backupCopyOptions);
-        Telemetry.getHistogramById("FX_SESSION_RESTORE_BACKUP_FILE_MS").add(
-          backupCopyOptions.outExecutionDuration);
-      } catch (ex if self._isNoSuchFile(ex)) {
-        // Ignore exceptions about non-existent files.
-      } catch (ex) {
-        Cu.reportError("Could not backup session state file: " + ex);
-        throw ex;
-      }
-    });
-  },
-
-  createUpgradeBackupCopy: function(ext) {
-    return TaskUtils.spawn(function task() {
-      try {
-        yield OS.File.copy(this.path, this.backupPath + ext);
-      } catch (ex if this._isNoSuchFile(ex)) {
-        // Ignore exceptions about non-existent files.
-      } catch (ex) {
-        Cu.reportError("Could not backup session state file to " +
-                       dest + ": " + ex);
-        throw ex;
+        Cu.reportError("Could not write session state file " + this.path
+                       + ": " + ex);
       }
     }.bind(this));
   },
 
-  removeUpgradeBackup: function(ext) {
-    return TaskUtils.spawn(function task() {
-      try {
-        yield OS.File.remove(this.backupPath + ext);
-      } catch (ex if this._isNoSuchFile(ex)) {
-        // Ignore exceptions about non-existent files.
-      }
-    }.bind(this));
+  writeLoadStateOnceAfterStartup: function (aLoadState) {
+    return SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]);
+  },
+
+  moveToBackupPath: function () {
+    return SessionWorker.post("moveToBackupPath");
+  },
+
+  createBackupCopy: function (ext) {
+    return SessionWorker.post("createBackupCopy", [ext]);
+  },
+
+  removeBackupCopy: function (ext) {
+    return SessionWorker.post("removeBackupCopy", [ext]);
   },
 
-  wipe: function ssfi_wipe() {
-    let self = this;
-    return TaskUtils.spawn(function task() {
-      let exn;
-      // Erase session state file
-      try {
-        yield OS.File.remove(self.path);
-      } catch (ex if self._isNoSuchFile(ex)) {
-        // Ignore exceptions about non-existent files.
-      } catch (ex) {
-        // Report error, don't stop immediately
-        Cu.reportError("Could not remove session state file: " + ex);
-        exn = ex;
-      }
+  wipe: function () {
+    return SessionWorker.post("wipe");
+  }
+};
 
-      // Erase any backup, any file named "sessionstore.bak[-buildID]".
-      let iterator = new OS.File.DirectoryIterator(OS.Constants.Path.profileDir);
-      for (let promise of iterator) {
-        let entry = yield promise;
-        if (!entry.isDir && entry.path.startsWith(self.backupPath)) {
-          try {
-            yield OS.File.remove(entry.path);
-          } catch (ex) {
-            // Report error, don't stop immediately
-            Cu.reportError("Could not remove backup file " + entry.path + " : " + ex);
-            exn = exn || ex;
+// Interface to a dedicated thread handling I/O
+let SessionWorker = (function () {
+  let worker = new PromiseWorker("resource:///modules/sessionstore/SessionWorker.js",
+    OS.Shared.LOG.bind("SessionWorker"));
+  return {
+    post: function post(...args) {
+      let promise = worker.post.apply(worker, args);
+      return promise.then(
+        null,
+        function onError(error) {
+          // Decode any serialized error
+          if (error instanceof PromiseWorker.WorkerError) {
+            throw OS.File.Error.fromMsg(error.data);
+          } else {
+            throw error;
           }
         }
-      }
-
-      if (exn) {
-        throw exn;
-      }
-    });
-
-  },
-
-  _isNoSuchFile: function ssfi_isNoSuchFile(aReason) {
-    return aReason instanceof OS.File.Error && aReason.becauseNoSuchFile;
-  }
-};
+      );
+    }
+  };
+})();
--- a/browser/components/sessionstore/src/moz.build
+++ b/browser/components/sessionstore/src/moz.build
@@ -11,11 +11,12 @@ EXTRA_COMPONENTS += [
 ]
 
 JS_MODULES_PATH = 'modules/sessionstore'
 
 EXTRA_JS_MODULES = [
     'DocumentUtils.jsm',
     'SessionMigration.jsm',
     'SessionStorage.jsm',
+    'SessionWorker.js',
     'XPathGenerator.jsm',
     '_SessionFile.jsm',
 ]
--- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -191,28 +191,23 @@ function test() {
     setPrefs();
 
     // Prepare a window; open it and add more tabs
     let options = {};
     if (aIsPrivateWindow) {
       options = {private: true};
     }
 
-    let newWin = OpenBrowserWindow(options);
-    newWin.addEventListener("load", function(aEvent) {
-      newWin.removeEventListener("load", arguments.callee, false);
-      newWin.gBrowser.addEventListener("load", function(aEvent) {
-        newWin.gBrowser.removeEventListener("load", arguments.callee, true);
-        TEST_URLS.forEach(function (url) {
-          newWin.gBrowser.addTab(url);
-        });
+    whenNewWindowLoaded(options, function (newWin) {
+      TEST_URLS.forEach(function (url) {
+        newWin.gBrowser.addTab(url);
+      });
 
-        executeSoon(function() testFn(newWin));
-      }, true);
-    }, false);
+      executeSoon(() => testFn(newWin));
+    });
   }
 
   /**
    * Test 1: Normal in-session restore
    * @note: Non-Mac only
    */
   function testOpenCloseNormal(nextFn) {
     setupTestAndRun(false, function(newWin) {
@@ -225,71 +220,59 @@ function test() {
       ok(!newWin.closed, "First close request was denied");
       if (!newWin.closed) {
         newWin.BrowserTryToCloseWindow();
         ok(newWin.closed, "Second close request was granted");
       }
 
       // Open a new window
       // The previously closed window should be restored
-      newWin = OpenBrowserWindow({});
-      newWin.addEventListener("load", function() {
-        this.removeEventListener("load", arguments.callee, true);
-        executeSoon(function() {
-          is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
-             "Restored window in-session with otherpopup windows around");
+      whenNewWindowLoaded({}, function (newWin) {
+        is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
+           "Restored window in-session with otherpopup windows around");
 
-          // Cleanup
-          newWin.close();
+        // Cleanup
+        newWin.close();
 
-          // Next please
-          executeSoon(nextFn);
-        });
-      }, true);
+        // Next please
+        executeSoon(nextFn);
+      });
     });
   }
 
   /**
    * Test 2: PrivateBrowsing in-session restore
    * @note: Non-Mac only
    */
   function testOpenClosePrivateBrowsing(nextFn) {
     setupTestAndRun(false, function(newWin) {
       // Close the window
       newWin.BrowserTryToCloseWindow();
 
       // Enter private browsing mode
       // Open a new window.
       // The previously closed window should NOT be restored
-      newWin = OpenBrowserWindow({private: true});
-      newWin.addEventListener("load", function() {
-        this.removeEventListener("load", arguments.callee, true);
-        executeSoon(function() {
-          is(newWin.gBrowser.browsers.length, 1,
-             "Did not restore in private browing mode");
+      whenNewWindowLoaded({private: true}, function (newWin) {
+        is(newWin.gBrowser.browsers.length, 1,
+           "Did not restore in private browing mode");
 
-          // Cleanup
-          newWin.BrowserTryToCloseWindow();
+        // Cleanup
+        newWin.BrowserTryToCloseWindow();
 
-          // Exit private browsing mode again
-          newWin = OpenBrowserWindow({});
-          newWin.addEventListener("load", function() {
-            this.removeEventListener("load", arguments.callee, true);
-            executeSoon(function() {
-              is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
-                 "Restored after leaving private browsing again");
+        // Exit private browsing mode again
+        whenNewWindowLoaded({}, function (newWin) {
+          is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
+             "Restored after leaving private browsing again");
 
-              newWin.close();
+          newWin.close();
 
-              // Next please
-              executeSoon(nextFn);
-            });
-          }, true);
+          // Next please
+          executeSoon(nextFn);
         });
-      }, true);
+      });
     });
   }
 
   /**
    * Test 3: Open some popup windows to check those aren't restored, but
    *         the browser window is
    * @note: Non-Mac only
    */
@@ -307,31 +290,27 @@ function test() {
           newWin.BrowserTryToCloseWindow();
 
           // Close the popup window
           // The test is successful when not this popup window is restored
           // but instead newWin
           popup2.close();
 
           // open a new window the previously closed window should be restored to
-          newWin = OpenBrowserWindow({});
-          newWin.addEventListener("load", function() {
-            this.removeEventListener("load", arguments.callee, true);
-            executeSoon(function() {
-              is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
-                 "Restored window and associated tabs in session");
+          whenNewWindowLoaded({}, function (newWin) {
+            is(newWin.gBrowser.browsers.length, TEST_URLS.length + 1,
+               "Restored window and associated tabs in session");
 
-              // Cleanup
-              newWin.close();
-              popup.close();
+            // Cleanup
+            newWin.close();
+            popup.close();
 
-              // Next please
-              executeSoon(nextFn);
-            });
-          }, true);
+            // Next please
+            executeSoon(nextFn);
+          });
         }, true);
       }, false);
     });
   }
 
   /**
    * Test 4: Open some popup window to check it isn't restored.
    *         Instead nothing at all should be restored
@@ -361,32 +340,28 @@ function test() {
           is(popup.gBrowser.browsers.length, 2,
              "Did not restore to the popup window (2)");
 
           // Close the popup window
           // The test is successful when not this popup window is restored
           // but instead a new window is opened without restoring anything
           popup.close();
 
-          let newWin = OpenBrowserWindow({});
-          newWin.addEventListener("load", function() {
-            newWin.removeEventListener("load", arguments.callee, true);
-            executeSoon(function() {
-              isnot(newWin.gBrowser.browsers.length, 2,
-                    "Did not restore the popup window");
-              is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1,
-                 "Did not restore the popup window (2)");
+          whenNewWindowLoaded({}, function (newWin) {
+            isnot(newWin.gBrowser.browsers.length, 2,
+                  "Did not restore the popup window");
+            is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1,
+               "Did not restore the popup window (2)");
 
-              // Cleanup
-              newWin.close();
+            // Cleanup
+            newWin.close();
 
-              // Next please
-              executeSoon(nextFn);
-            });
-          }, true);
+            // Next please
+            executeSoon(nextFn);
+          });
         }, true);
       }, false);
     }, true);
   }
 
     /**
    * Test 5: Open some windows and do undoCloseWindow. This should prevent any
    *         restoring later in the test
@@ -397,37 +372,33 @@ function test() {
       setupTestAndRun(false, function(newWin2) {
         newWin.BrowserTryToCloseWindow();
         newWin2.BrowserTryToCloseWindow();
 
         browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
 
         newWin = undoCloseWindow(0);
 
-        newWin2 = OpenBrowserWindow({});
-        newWin2.addEventListener("load", function() {
-          newWin2.removeEventListener("load", arguments.callee, true);
-          executeSoon(function() {
-            is(newWin2.gBrowser.browsers.length, 1,
-               "Did not restore, as undoCloseWindow() was last called");
-            is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1,
-               "Did not restore, as undoCloseWindow() was last called (2)");
+        whenNewWindowLoaded({}, function (newWin2) {
+          is(newWin2.gBrowser.browsers.length, 1,
+             "Did not restore, as undoCloseWindow() was last called");
+          is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1,
+             "Did not restore, as undoCloseWindow() was last called (2)");
 
-            browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup");
+          browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup");
 
-            // Cleanup
-            newWin.close();
-            newWin2.close();
+          // Cleanup
+          newWin.close();
+          newWin2.close();
 
-            browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
+          browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
 
-            // Next please
-            executeSoon(nextFn);
-          });
-        }, true);
+          // Next please
+          executeSoon(nextFn);
+        });
       });
     });
   }
 
   /**
    * Test 7: Check whether the right number of notifications was received during
    *         the tests
    */
--- a/browser/components/sessionstore/test/browser_524745.js
+++ b/browser/components/sessionstore/test/browser_524745.js
@@ -5,42 +5,39 @@
 function test() {
   /** Test for Bug 524745 **/
 
   let uniqKey = "bug524745";
   let uniqVal = Date.now();
 
   waitForExplicitFinish();
 
-  let window_B = openDialog(location, "_blank", "chrome,all,dialog=no");
-  window_B.addEventListener("load", function(aEvent) {
-    window_B.removeEventListener("load", arguments.callee, false);
-
-      waitForFocus(function() {
-        // Add identifying information to window_B
-        ss.setWindowValue(window_B, uniqKey, uniqVal);
-        let state = JSON.parse(ss.getBrowserState());
-        let selectedWindow = state.windows[state.selectedWindow - 1];
-        is(selectedWindow.extData && selectedWindow.extData[uniqKey], uniqVal,
-           "selectedWindow is window_B");
+  whenNewWindowLoaded({ private: false }, function (window_B) {
+    waitForFocus(function() {
+      // Add identifying information to window_B
+      ss.setWindowValue(window_B, uniqKey, uniqVal);
+      let state = JSON.parse(ss.getBrowserState());
+      let selectedWindow = state.windows[state.selectedWindow - 1];
+      is(selectedWindow.extData && selectedWindow.extData[uniqKey], uniqVal,
+         "selectedWindow is window_B");
 
-        // Now minimize window_B. The selected window shouldn't have the secret data
-        window_B.minimize();
-        waitForFocus(function() {
-          state = JSON.parse(ss.getBrowserState());
-          selectedWindow = state.windows[state.selectedWindow - 1];
-          ok(!selectedWindow.extData || !selectedWindow.extData[uniqKey],
-             "selectedWindow is not window_B after minimizing it");
+      // Now minimize window_B. The selected window shouldn't have the secret data
+      window_B.minimize();
+      waitForFocus(function() {
+        state = JSON.parse(ss.getBrowserState());
+        selectedWindow = state.windows[state.selectedWindow - 1];
+        ok(!selectedWindow.extData || !selectedWindow.extData[uniqKey],
+           "selectedWindow is not window_B after minimizing it");
 
-          // Now minimize the last open window (assumes no other tests left windows open)
-          window.minimize();
-          state = JSON.parse(ss.getBrowserState());
-          is(state.selectedWindow, 0,
-             "selectedWindow should be 0 when all windows are minimized");
+        // Now minimize the last open window (assumes no other tests left windows open)
+        window.minimize();
+        state = JSON.parse(ss.getBrowserState());
+        is(state.selectedWindow, 0,
+           "selectedWindow should be 0 when all windows are minimized");
 
-          // Cleanup
-          window.restore();
-          window_B.close();
-          finish();
-        });
-      }, window_B);
-  }, false);
+        // Cleanup
+        window.restore();
+        window_B.close();
+        finish();
+      });
+    }, window_B);
+  });
 }
--- a/browser/components/sessionstore/test/browser_819510_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_819510_perwindowpb.js
@@ -169,33 +169,17 @@ function forceWriteState(aCallback) {
       aSubject.QueryInterface(Ci.nsISupportsString);
       aCallback(JSON.parse(aSubject.data));
     }
   }, "sessionstore-state-write", false);
   Services.prefs.setIntPref("browser.sessionstore.interval", 0);
 }
 
 function testOnWindow(aIsPrivate, aCallback) {
-  let win = OpenBrowserWindow({private: aIsPrivate});
-  let gotLoad = false;
-  let gotActivate = false;
-  win.addEventListener("activate", function onActivate() {
-    win.removeEventListener("activate", onActivate, false);
-    gotActivate = true;
-    if (gotLoad) {
-      executeSoon(function() { aCallback(win) });
-    }
-  }, false);
-  win.addEventListener("load", function onLoad() {
-    win.removeEventListener("load", onLoad, false);
-    gotLoad = true;
-    if (gotActivate) {
-      executeSoon(function() { aCallback(win) });
-    }
-  }, false);
+  whenNewWindowLoaded({private: aIsPrivate}, aCallback);
 }
 
 function waitForTabLoad(aWin, aURL, aCallback) {
   aWin.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     aWin.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     executeSoon(aCallback);
   }, true);
   aWin.gBrowser.selectedBrowser.loadURI(aURL);
--- a/browser/components/sessionstore/test/browser_833286_atomic_backup.js
+++ b/browser/components/sessionstore/test/browser_833286_atomic_backup.js
@@ -73,19 +73,19 @@ function testWriteNoBackup() {
   ok(ssExists, "sessionstore.js should exist.");
   ok(!ssBackupExists, "sessionstore.bak should not have been created, yet");
 
   // Save sessionstore.js data to compare to the sessionstore.bak data in the
   // next test.
   let array = yield OS.File.read(path);
   gSSData = gDecoder.decode(array);
 
-  // Manually trigger _SessionFile.createBackupCopy since the backup once
+  // Manually trigger _SessionFile.moveToBackupPath since the backup once
   // promise is already resolved and backup would not be triggered again.
-  yield _SessionFile.createBackupCopy();
+  yield _SessionFile.moveToBackupPath();
 
   nextTest(testWriteBackup);
 }
 
 function testWriteBackup() {
   // Ensure sessionstore.bak is finally created.
   let ssExists = yield OS.File.exists(path);
   let ssBackupExists = yield OS.File.exists(backupPath);
@@ -135,9 +135,9 @@ function testNoWriteBackup() {
 
   // Read sessionstore.bak data.
   let array = yield OS.File.read(backupPath);
   let ssBakData = gDecoder.decode(array);
   // Ensure the sessionstore.bak did not change.
   is(ssBakData, gSSBakData, "sessionstore.bak is unchanged.");
 
   executeSoon(finish);
-}
\ No newline at end of file
+}
--- a/browser/components/sessionstore/test/browser_dying_cache.js
+++ b/browser/components/sessionstore/test/browser_dying_cache.js
@@ -9,20 +9,21 @@ function test() {
  * This test ensures that after closing a window we keep its state data around
  * as long as something keeps a reference to it. It should only be possible to
  * read data after closing - writing should fail.
  */
 
 function runTests() {
   // Open a new window.
   let win = OpenBrowserWindow();
-  yield whenWindowLoaded(win);
+  yield whenDelayedStartupFinished(win, next);
 
   // Load some URL in the current tab.
-  win.gBrowser.selectedBrowser.loadURI("about:robots");
+  let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
+  win.gBrowser.selectedBrowser.loadURIWithFlags("about:robots", flags);
   yield whenBrowserLoaded(win.gBrowser.selectedBrowser);
 
   // Open a second tab and close the first one.
   let tab = win.gBrowser.addTab("about:mozilla");
   yield whenBrowserLoaded(tab.linkedBrowser);
   win.gBrowser.removeTab(win.gBrowser.tabs[0]);
 
   // Make sure our window is still tracked by sessionstore
--- a/browser/components/sessionstore/test/browser_input.js
+++ b/browser/components/sessionstore/test/browser_input.js
@@ -14,17 +14,17 @@ function test() {
  * changed to be re-collected.
  */
 
 function runTests() {
   // Create a dummy window that is regarded as active. We need to do this
   // because we always collect data for tabs of active windows no matter if
   // the window is dirty or not.
   let win = OpenBrowserWindow();
-  yield waitForLoad(win);
+  yield whenDelayedStartupFinished(win, next);
 
   // Create a tab with some form fields.
   let tab = gBrowser.selectedTab = gBrowser.addTab(URL);
   let browser = gBrowser.selectedBrowser;
   yield waitForLoad(browser);
 
   // All windows currently marked as dirty will be written to disk
   // and thus marked clean afterwards.
--- a/browser/components/sessionstore/test/browser_pageshow.js
+++ b/browser/components/sessionstore/test/browser_pageshow.js
@@ -18,17 +18,17 @@ function test() {
 const URL = "data:text/html,<h1>first</h1>";
 const URL2 = "data:text/html,<h1>second</h1>";
 
 function runTests() {
   // Create a dummy window that is regarded as active. We need to do this
   // because we always collect data for tabs of active windows no matter if
   // the window is dirty or not.
   let win = OpenBrowserWindow();
-  yield waitForLoad(win);
+  yield whenDelayedStartupFinished(win, next);
 
   // Create a tab with two history entries.
   let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
   yield loadURI(URL);
   yield loadURI(URL2);
 
   // All windows currently marked as dirty will be written to disk
   // and thus marked clean afterwards.
--- a/browser/components/sessionstore/test/browser_windowRestore_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_windowRestore_perwindowpb.js
@@ -8,19 +8,17 @@ function test() {
   waitForExplicitFinish();
 
   // Purging the list of closed windows
   while(ss.getClosedWindowCount() > 0)
     ss.forgetClosedWindow(0);
 
   // Load a private window, then close it 
   // and verify it doesn't get remembered for restoring
-  var win = OpenBrowserWindow({private: true});
-
-  whenWindowLoaded(win, function onload() {
+  whenNewWindowLoaded({private: true}, function (win) {
     info("The private window got loaded");
     win.addEventListener("SSWindowClosing", function onclosing() {
       win.removeEventListener("SSWindowClosing", onclosing, false);
       executeSoon(function () {
         is (ss.getClosedWindowCount(), 0,
             "The private window should not have been stored");
         finish();
       });
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -284,47 +284,42 @@ registerCleanupFunction(function () {
 function closeAllButPrimaryWindow() {
   for (let win in BrowserWindowIterator()) {
     if (win != window) {
       win.close();
     }
   }
 }
 
+/**
+ * When opening a new window it is not sufficient to wait for its load event.
+ * We need to use whenDelayedStartupFinshed() here as the browser window's
+ * delayedStartup() routine is executed one tick after the window's load event
+ * has been dispatched. browser-delayed-startup-finished might be deferred even
+ * further if parts of the window's initialization process take more time than
+ * expected (e.g. reading a big session state from disk).
+ */
 function whenNewWindowLoaded(aOptions, aCallback) {
   let win = OpenBrowserWindow(aOptions);
-  let gotLoad = false;
-  let gotActivate = (Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager).activeWindow == win);
-
-  function maybeRunCallback() {
-    if (gotLoad && gotActivate) {
-      win.BrowserChromeTest.runWhenReady(function() {
-        executeSoon(function() { aCallback(win); });
-      });
-    }
-  }
+  whenDelayedStartupFinished(win, () => aCallback(win));
+  return win;
+}
 
-  if (!gotActivate) {
-    win.addEventListener("activate", function onActivate() {
-      info("Got activate.");
-      win.removeEventListener("activate", onActivate, false);
-      gotActivate = true;
-      maybeRunCallback();
-    }, false);
-  } else {
-    info("Was activated.");
-  }
-
-  win.addEventListener("load", function onLoad() {
-    info("Got load");
-    win.removeEventListener("load", onLoad, false);
-    gotLoad = true;
-    maybeRunCallback();
-  }, false);
-  return win;
+/**
+ * This waits for the browser-delayed-startup-finished notification of a given
+ * window. It indicates that the windows has loaded completely and is ready to
+ * be used for testing.
+ */
+function whenDelayedStartupFinished(aWindow, aCallback) {
+  Services.obs.addObserver(function observer(aSubject, aTopic) {
+    if (aWindow == aSubject) {
+      Services.obs.removeObserver(observer, aTopic);
+      executeSoon(aCallback);
+    }
+  }, "browser-delayed-startup-finished", false);
 }
 
 /**
  * The test runner that controls the execution flow of our tests.
  */
 let TestRunner = {
   _iter: null,
 
--- a/browser/components/sessionstore/test/unit/test_backup.js
+++ b/browser/components/sessionstore/test/unit/test_backup.js
@@ -14,29 +14,29 @@ function run_test() {
 
 let pathStore;
 function pathBackup(ext) {
   return OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak" + ext);
 }
 
 // Ensure that things proceed smoothly if there is no file to back up
 add_task(function test_nothing_to_backup() {
-  yield _SessionFile.createUpgradeBackupCopy("");
+  yield _SessionFile.createBackupCopy("");
 });
 
 // Create a file, back it up, remove it
 add_task(function test_do_backup() {
   let content = "test_1";
   let ext = ".upgrade_test_1";
   yield OS.File.writeAtomic(pathStore, content, {tmpPath: pathStore + ".tmp"});
 
   do_print("Ensuring that the backup is created");
-  yield _SessionFile.createUpgradeBackupCopy(ext);
+  yield _SessionFile.createBackupCopy(ext);
   do_check_true((yield OS.File.exists(pathBackup(ext))));
 
   let data = yield OS.File.read(pathBackup(ext));
   do_check_eq((new TextDecoder()).decode(data), content);
 
   do_print("Ensuring that we can remove the backup");
-  yield _SessionFile.removeUpgradeBackup(ext);
+  yield _SessionFile.removeBackupCopy(ext);
   do_check_false((yield OS.File.exists(pathBackup(ext))));
 });
 
--- a/browser/config/tooltool-manifests/linux32/asan.manifest
+++ b/browser/config/tooltool-manifests/linux32/asan.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170890"
+"clang_version": "r185949"
 }, 
 {
-"filename": "setup.sh", 
+"size": 47, 
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
 "algorithm": "sha512", 
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
-"size": 47
+"filename": "setup.sh"
 }, 
 {
-"filename": "clang.tar.bz2", 
+"size": 72573411, 
+"digest": "491753968f34d1bd3c58280688349499a92f31a118eb6f28e86746be62615004370394b8e1b10d48dc3fba4bc6d4fbb4ce6c7dbc4fadb39447de9aa55573c58e", 
 "algorithm": "sha512", 
-"digest": "0bcfc19f05cc0f042befb3823c7ecce9ba411b152921aa29e97e7adc846e0258fd7da521b1620cb1e61a19d2fcac9b60e6d613c922b6c153e01b9b0766651d09", 
-"size": 62708281
+"filename": "clang.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/asan.manifest
+++ b/browser/config/tooltool-manifests/linux64/asan.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170890"
-},
+"clang_version": "r185949"
+}, 
 {
-"filename": "setup.sh",
-"algorithm": "sha512",
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
-"size": 47
-},
+"size": 47, 
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
+"algorithm": "sha512", 
+"filename": "setup.sh"
+}, 
 {
-"filename": "clang.tar.bz2",
-"algorithm": "sha512",
-"digest": "e14ccefd965372a57c540647b2b99e21a4aa82f81a8b9a9e18dac7cba4c3436181bef0dfab8c51bcb5c343f504a693fdcfbe7d609f10291b5dd65ab059979d29",
-"size": 63034761
+"size": 73050713, 
+"digest": "2c5c26a44402f974c2a4ccffb07ea1ac2d01d84dc3e4281fef6e047a62606811a16534d034477dfd9be055a07d931b17ca4e883c8edcd1f8d3a8c91b150e2288", 
+"algorithm": "sha512", 
+"filename": "clang.tar.bz2"
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/asan.manifest
+++ b/browser/config/tooltool-manifests/macosx64/asan.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r170890"
-},
+"clang_version": "r185949"
+}, 
 {
-"size": 47,
-"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
-"algorithm": "sha512",
+"size": 47, 
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa", 
+"algorithm": "sha512", 
 "filename": "setup.sh"
-},
+}, 
 {
-"size": 56126352,
-"digest": "e156e2a39abd5bf272ee30748a6825f22ddd27565b097c66662a2a6f2e9892bc5b4bf30a3552dffbe867dbfc39e7ee086e0b2cd7935f6ea216c0cf936178a88f",
-"algorithm": "sha512",
+"size": 61779086, 
+"digest": "b2f2861da7583e859b4fb40e1304dd284df02466c909893684341b16e2f58c4c100891504938cf62f26ac82254b9e87135ba98f8196dd26e9b58d3242f1cf811", 
+"algorithm": "sha512", 
 "filename": "clang.tar.bz2"
 }
 ]
--- a/browser/devtools/profiler/commands.js
+++ b/browser/devtools/profiler/commands.js
@@ -64,17 +64,17 @@ gcli.addCommand({
   exec: function (args, context) {
     function start() {
       let panel = getPanel(context, "jsprofiler");
 
       if (panel.recordingProfile)
         throw gcli.lookup("profilerAlreadyStarted2");
 
       panel.toggleRecording();
-      return gcli.lookup("profilerStarted");
+      return gcli.lookup("profilerStarted2");
     }
 
     return gDevTools.showToolbox(context.environment.target, "jsprofiler")
       .then(start);
   }
 });
 
 /*
--- a/browser/devtools/profiler/test/browser_profiler_cmd.js
+++ b/browser/devtools/profiler/test/browser_profiler_cmd.js
@@ -43,17 +43,17 @@ function testProfilerStart() {
   gPanel.once("started", function () {
     is(gPanel.profiles.size, 1, "There is a new profile");
     is(gPanel.getProfileByName("Profile 1"), gPanel.recordingProfile, "Recording profile is OK");
     ok(!gPanel.activeProfile, "There's no active profile yet");
     cmd("profiler start", gcli.lookup("profilerAlreadyStarted2"));
     deferred.resolve();
   });
 
-  cmd("profiler start", gcli.lookup("profilerStarted"));
+  cmd("profiler start", gcli.lookup("profilerStarted2"));
   return deferred.promise;
 }
 
 function testProfilerList() {
   cmd("profiler list", /^.*Profile\s1\s\*.*$/);
 }
 
 function testProfilerStop() {
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -1469,17 +1469,19 @@ let SyntaxTreeVisitor = {
       if (aCallbacks.onNode(aNode, aParent) === false) {
         return;
       }
     }
     if (aCallbacks.onArrayExpression) {
       aCallbacks.onArrayExpression(aNode);
     }
     for (let element of aNode.elements) {
-      if (element) {
+      // TODO: remove the typeof check when support for SpreadExpression is
+      // added (bug 890913).
+      if (element && typeof this[element.type] == "function") {
         this[element.type](element, aNode, aCallbacks);
       }
     }
   },
 
   /**
    * An object expression. A literal property in an object expression can have
    * either a string or number as its value. Ordinary property initializers
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -1246,19 +1246,19 @@ profilerAlreadyStarted2=Profile has alre
 profilerNotFound=Profile not found
 
 # LOCALIZATION NOTE (profilerNotStarted) A message that is displayed whenever
 # an operation cannot be completed because the profile in question has not been
 # started yet. It also contains a hint to use the 'profile start' command to
 # start the profiler.
 profilerNotStarted3=Profiler has not been started yet. Use 'profile start' to start profiling
 
-# LOCALIZATION NOTE (profilerStarted) A very short string that indicates that
+# LOCALIZATION NOTE (profilerStarted2) A very short string that indicates that
 # we have started recording.
-profilerStarted=Recording...
+profilerStarted2=Recording…
 
 # LOCALIZATION NOTE (profilerNotReady) A message that is displayed whenever
 # an operation cannot be completed because the profiler has not been opened yet.
 profilerNotReady=For this command to work you need to open the profiler first
 
 # LOCALIZATION NOTE (listenDesc) A very short string used to describe the
 # function of the 'listen' command.
 listenDesc=Open a remote debug port
new file mode 100644
--- /dev/null
+++ b/build/unix/build-clang/clang-trunk.json
@@ -0,0 +1,11 @@
+{
+    "llvm_revision": "185949",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "patches": {
+        "macosx64": ["llvm-debug-frame.patch"],
+        "linux64": ["llvm-debug-frame.patch", "no-sse-on-linux.patch"],
+        "linux32": ["llvm-debug-frame.patch", "no-sse-on-linux.patch"]
+    }
+}
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -16,16 +16,18 @@
 #include "BluetoothUuid.h"
 
 #include "MobileConnection.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
+#include "nsIDOMIccInfo.h"
+#include "nsIIccProvider.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyProvider.h"
 #include "nsRadioInterfaceLayer.h"
 
 /**
  * BRSF bitmask of AG supported features. See 4.34.1 "Bluetooth Defined AT
  * Capabilities" in Bluetooth hands-free profile 1.6
@@ -629,22 +631,22 @@ BluetoothHfpManager::HandleVoiceConnecti
     NS_WARNING("The operator name was longer than 16 characters. We cut it.");
     mOperatorName.Left(mOperatorName, 16);
   }
 }
 
 void
 BluetoothHfpManager::HandleIccInfoChanged()
 {
-  nsCOMPtr<nsIMobileConnectionProvider> connection =
+  nsCOMPtr<nsIIccProvider> icc =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
-  NS_ENSURE_TRUE_VOID(connection);
+  NS_ENSURE_TRUE_VOID(icc);
 
-  nsIDOMMozMobileICCInfo* iccInfo;
-  connection->GetIccInfo(&iccInfo);
+  nsIDOMMozIccInfo* iccInfo;
+  icc->GetIccInfo(&iccInfo);
   NS_ENSURE_TRUE_VOID(iccInfo);
   iccInfo->GetMsisdn(mMsisdn);
 }
 
 void
 BluetoothHfpManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -116,9 +116,23 @@ CameraPreviewMediaStream::SetCurrentFram
     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   }
 
   if (mFrameCallback) {
     mFrameCallback->OnNewFrame(aIntrinsicSize, aImage);
   }
 }
 
+void
+CameraPreviewMediaStream::ClearCurrentFrame()
+{
+  MutexAutoLock lock(mMutex);
+
+  for (uint32_t i = 0; i < mVideoOutputs.Length(); ++i) {
+    VideoFrameContainer* output = mVideoOutputs[i];
+    output->ClearCurrentFrame();
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate);
+    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  }
 }
+
+}
--- a/dom/camera/CameraPreviewMediaStream.h
+++ b/dom/camera/CameraPreviewMediaStream.h
@@ -37,16 +37,17 @@ public:
   virtual void RemoveVideoOutput(VideoFrameContainer* aContainer) MOZ_OVERRIDE;
   virtual void ChangeExplicitBlockerCount(int32_t aDelta) MOZ_OVERRIDE;
   virtual void AddListener(MediaStreamListener* aListener) MOZ_OVERRIDE;
   virtual void RemoveListener(MediaStreamListener* aListener) MOZ_OVERRIDE;
   virtual void Destroy();
 
   // Call these on any thread.
   void SetCurrentFrame(const gfxIntSize& aIntrinsicSize, Image* aImage);
+  void ClearCurrentFrame();
 
   void SetFrameCallback(CameraPreviewFrameCallback* aCallback) {
     mFrameCallback = aCallback;
   }
 
 protected:
   // mMutex protects all the class' fields.
   // This class is not registered to MediaStreamGraph.
--- a/dom/camera/DOMCameraPreview.cpp
+++ b/dom/camera/DOMCameraPreview.cpp
@@ -282,16 +282,18 @@ DOMCameraPreview::SetStateStopped()
 
 void
 DOMCameraPreview::Stopped(bool aForced)
 {
   if (mState != STOPPING && !aForced) {
     return;
   }
 
+  mInput->ClearCurrentFrame();
+
   DOM_CAMERA_LOGI("Dispatching preview stream stopped\n");
   nsCOMPtr<nsIRunnable> stopped = new PreviewControl(this, PreviewControl::STOPPED);
   nsresult rv = NS_DispatchToMainThread(stopped);
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("failed to decrement reference count (%d), MEMORY LEAK!\n", rv);
   }
 }
 
--- a/dom/icc/interfaces/moz.build
+++ b/dom/icc/interfaces/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'SimToolKit.idl',
     'nsIDOMIccCardLockErrorEvent.idl',
+    'nsIDOMIccInfo.idl',
     'nsIDOMIccManager.idl',
     'nsIIccProvider.idl',
     'nsINavigatorIccManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_icc'
 
 XPIDL_FLAGS += [
new file mode 100644
--- /dev/null
+++ b/dom/icc/interfaces/nsIDOMIccInfo.idl
@@ -0,0 +1,45 @@
+/* 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, uuid(a45c0fe0-c911-11e2-8b8b-0800200c9a66)]
+interface nsIDOMMozIccInfo : nsISupports
+{
+   /**
+   * Integrated Circuit Card Identifier.
+   */
+  readonly attribute DOMString iccid;
+
+  /**
+   * Mobile Country Code (MCC) of the subscriber's home network.
+   */
+  readonly attribute DOMString mcc;
+
+  /**
+   * Mobile Network Code (MNC) of the subscriber's home network.
+   */
+  readonly attribute DOMString mnc;
+
+  /**
+   * Service Provider Name (SPN) of the subscriber's home network.
+   */
+  readonly attribute DOMString spn;
+
+  /**
+   * Network name must be a part of displayed carrier name.
+   */
+  readonly attribute boolean isDisplayNetworkNameRequired;
+
+  /**
+   * Service provider name must be a part of displayed carrier name.
+   */
+  readonly attribute boolean isDisplaySpnRequired;
+
+  /**
+   * Mobile Station ISDN Number (MSISDN) of the subscriber's, aka
+   * his phone number.
+   */
+  readonly attribute DOMString msisdn;
+};
--- a/dom/icc/interfaces/nsIDOMIccManager.idl
+++ b/dom/icc/interfaces/nsIDOMIccManager.idl
@@ -1,20 +1,21 @@
 /* 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"
 #include "SimToolKit.idl"
 
-interface nsIDOMEventListener;
+interface nsIDOMContact;
 interface nsIDOMDOMRequest;
-interface nsIDOMContact;
+interface nsIDOMEventListener;
+interface nsIDOMMozIccInfo;
 
-[scriptable, builtinclass, uuid(5f405112-4da9-4d4d-942c-4da3cb7928e1)]
+[scriptable, builtinclass, uuid(d362bf60-c910-11e2-8b8b-0800200c9a66)]
 interface nsIDOMMozIccManager : nsIDOMEventTarget
 {
   /**
    * STK Menu Presentation types.
    */
   const unsigned short STK_MENU_TYPE_NOT_SPECIFIED      = 0x00;
   const unsigned short STK_MENU_TYPE_DATA_VALUES        = 0x01;
   const unsigned short STK_MENU_TYPE_NAVIGATION_OPTIONS = 0x03;
@@ -270,16 +271,29 @@ interface nsIDOMMozIccManager : nsIDOMEv
   [implicit_jscontext] attribute jsval onstkcommand;
 
   /**
    * 'stksessionend' event is notified whenever STK Session is terminated by
    * ICC.
    */
   [implicit_jscontext] attribute jsval onstksessionend;
 
+  // UICC Card Information.
+
+  /**
+   * Information stored in the device's ICC card.
+   */
+  readonly attribute nsIDOMMozIccInfo iccInfo;
+
+  /**
+   * The 'iccinfochange' event is notified whenever the icc info object
+   * changes.
+   */
+  [implicit_jscontext] attribute jsval oniccinfochange;
+
   // UICC Card State.
 
   /**
    * Indicates the state of the device's ICC card.
    *
    * Possible values: null, 'unknown', 'absent', 'pinRequired', 'pukRequired',
    * 'networkLocked', 'corporateLocked', 'serviceProviderLocked', 'ready'.
    */
--- a/dom/icc/interfaces/nsIIccProvider.idl
+++ b/dom/icc/interfaces/nsIIccProvider.idl
@@ -1,43 +1,50 @@
 /* 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 nsIDOMWindow;
+interface nsIDOMContact;
 interface nsIDOMDOMRequest;
-interface nsIDOMContact;
+interface nsIDOMMozIccInfo;
+interface nsIDOMWindow;
 
-[scriptable, uuid(5902d9b0-c2be-11e2-8b8b-0800200c9a66)]
+[scriptable, uuid(82d25440-c913-11e2-8b8b-0800200c9a66)]
 interface nsIIccListener : nsISupports
 {
   void notifyStkCommand(in DOMString aMessage);
   void notifyStkSessionEnd();
   void notifyIccCardLockError(in DOMString lockType,
                               in unsigned long retryCount);
   void notifyCardStateChanged();
+  void notifyIccInfoChanged();
 };
 
 /**
  * XPCOM component (in the content process) that provides the ICC information.
  */
-[scriptable, uuid(7131dfbe-9a2c-434d-b6b8-3eebf491ce1a)]
+[scriptable, uuid(52fa6780-c913-11e2-8b8b-0800200c9a66)]
 interface nsIIccProvider : nsISupports
 {
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'mobileconnection' permission is allowed to register.
    */
   void registerIccMsg(in nsIIccListener listener);
   void unregisterIccMsg(in nsIIccListener listener);
 
   /**
+   * UICC Information
+   */
+  readonly attribute nsIDOMMozIccInfo iccInfo;
+
+  /**
    * Card State
    */
   readonly attribute DOMString cardState;
 
   /**
    * STK interfaces.
    */
   void sendStkResponse(in nsIDOMWindow window,
--- a/dom/icc/src/IccManager.cpp
+++ b/dom/icc/src/IccManager.cpp
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Services.h"
 #include "nsIDOMClassInfo.h"
 #include "nsIDOMIccCardLockErrorEvent.h"
+#include "nsIDOMIccInfo.h"
 #include "GeneratedEvents.h"
 #include "IccManager.h"
 #include "SimToolKit.h"
 #include "StkCommandEvent.h"
 
 #define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
 
 using namespace mozilla::dom::icc;
@@ -125,16 +126,28 @@ IccManager::SendStkEventDownload(const J
     return NS_ERROR_FAILURE;
   }
 
   mProvider->SendStkEventDownload(GetOwner(), aEvent);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+IccManager::GetIccInfo(nsIDOMMozIccInfo** aIccInfo)
+{
+  *aIccInfo = nullptr;
+
+  if (!mProvider) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return mProvider->GetIccInfo(aIccInfo);
+}
+
+NS_IMETHODIMP
 IccManager::GetCardState(nsAString& cardState)
 {
   cardState.SetIsVoid(true);
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
   return mProvider->GetCardState(cardState);
@@ -232,16 +245,17 @@ IccManager::UpdateContact(const nsAStrin
 
   return mProvider->UpdateContact(GetOwner(), aContactType, aContact, aPin2, aRequest);
 }
 
 NS_IMPL_EVENT_HANDLER(IccManager, stkcommand)
 NS_IMPL_EVENT_HANDLER(IccManager, stksessionend)
 NS_IMPL_EVENT_HANDLER(IccManager, icccardlockerror)
 NS_IMPL_EVENT_HANDLER(IccManager, cardstatechange)
+NS_IMPL_EVENT_HANDLER(IccManager, iccinfochange)
 
 // nsIIccListener
 
 NS_IMETHODIMP
 IccManager::NotifyStkCommand(const nsAString& aMessage)
 {
   nsRefPtr<StkCommandEvent> event = StkCommandEvent::Create(this, aMessage);
   NS_ASSERTION(event, "This should never fail!");
@@ -269,9 +283,15 @@ IccManager::NotifyIccCardLockError(const
 
   return DispatchTrustedEvent(ce);
 }
 
 NS_IMETHODIMP
 IccManager::NotifyCardStateChanged()
 {
   return DispatchTrustedEvent(NS_LITERAL_STRING("cardstatechange"));
+}
+
+NS_IMETHODIMP
+IccManager::NotifyIccInfoChanged()
+{
+  return DispatchTrustedEvent(NS_LITERAL_STRING("iccinfochange"));
 }
\ No newline at end of file
rename from dom/network/tests/marionette/test_mobile_iccinfo.js
rename to dom/icc/tests/marionette/test_icc_info.js
--- a/dom/network/tests/marionette/test_mobile_iccinfo.js
+++ b/dom/icc/tests/marionette/test_icc_info.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 30000;
 
 SpecialPowers.addPermission("mobileconnection", true, document);
 
-let connection = navigator.mozMobileConnection;
-ok(connection instanceof MozMobileConnection,
-   "connection is instanceof " + connection.constructor);
+let icc = navigator.mozIccManager;
+ok(icc instanceof MozIccManager,
+   "icc is instanceof " + icc.constructor);
 
 let emulatorCmdPendingCount = 0;
 function sendEmulatorCommand(cmd, callback) {
   emulatorCmdPendingCount++;
   runEmulatorCmd(cmd, function (result) {
     emulatorCmdPendingCount--;
     is(result[result.length - 1], "OK");
     callback(result);
@@ -23,55 +23,57 @@ function setEmulatorMccMnc(mcc, mnc) {
   let cmd = "operator set 0 Android,Android," + mcc + mnc;
   sendEmulatorCommand(cmd, function (result) {
     let re = new RegExp("" + mcc + mnc + "$");
     ok(result[0].match(re), "MCC/MNC should be changed.");
   });
 }
 
 function waitForIccInfoChange(callback) {
-  connection.addEventListener("iccinfochange", function handler() {
-    connection.removeEventListener("iccinfochange", handler);
+  icc.addEventListener("iccinfochange", function handler() {
+    icc.removeEventListener("iccinfochange", handler);
     callback();
   });
 }
 
 function finalize() {
   SpecialPowers.removePermission("mobileconnection", document);
   finish();
 }
 
+let iccInfo = icc.iccInfo;
+
 // The emulator's hard coded iccid value.
 // See it here {B2G_HOME}/external/qemu/telephony/sim_card.c#L299.
-is(connection.iccInfo.iccid, 89014103211118510720);
+is(iccInfo.iccid, 89014103211118510720);
 
 // The emulator's hard coded mcc and mnc codes.
 // See it here {B2G_HOME}/external/qemu/telephony/android_modem.c#L2465.
-is(connection.iccInfo.mcc, 310);
-is(connection.iccInfo.mnc, 260);
-is(connection.iccInfo.spn, "Android");
+is(iccInfo.mcc, 310);
+is(iccInfo.mnc, 260);
+is(iccInfo.spn, "Android");
 // Phone number is hardcoded in MSISDN
 // See {B2G_HOME}/external/qemu/telephony/sim_card.c, in asimcard_io()
-is(connection.iccInfo.msisdn, "15555215554");
+is(iccInfo.msisdn, "15555215554");
 
 // Test display condition change.
 function testDisplayConditionChange(func, caseArray, oncomplete) {
   (function do_call(index) {
     let next = index < (caseArray.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
     caseArray[index].push(next);
     func.apply(null, caseArray[index]);
   })(0);
 }
 
 function testSPN(mcc, mnc, expectedIsDisplayNetworkNameRequired,
                   expectedIsDisplaySpnRequired, callback) {
   waitForIccInfoChange(function() {
-    is(connection.iccInfo.isDisplayNetworkNameRequired,
+    is(iccInfo.isDisplayNetworkNameRequired,
        expectedIsDisplayNetworkNameRequired);
-    is(connection.iccInfo.isDisplaySpnRequired,
+    is(iccInfo.isDisplaySpnRequired,
        expectedIsDisplaySpnRequired);
     // operatorchange will be ignored if we send commands too soon.
     window.setTimeout(callback, 100);
   });
   setEmulatorMccMnc(mcc, mnc);
 }
 
 testDisplayConditionChange(testSPN, [
--- a/dom/network/interfaces/nsIDOMMobileConnection.idl
+++ b/dom/network/interfaces/nsIDOMMobileConnection.idl
@@ -1,23 +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/. */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMEventListener;
 interface nsIDOMDOMRequest;
-interface nsIDOMMozMobileICCInfo;
 interface nsIDOMMozMobileConnectionInfo;
 interface nsIDOMMozMobileNetworkInfo;
 interface nsIDOMMozMobileCellInfo;
 interface nsIDOMMozMobileCFInfo;
 
-[scriptable, builtinclass, uuid(dc010230-c2bc-11e2-8b8b-0800200c9a66)]
+[scriptable, builtinclass, uuid(fd26e2e0-c910-11e2-8b8b-0800200c9a66)]
 interface nsIDOMMozMobileConnection : nsIDOMEventTarget
 {
   const long ICC_SERVICE_CLASS_VOICE = (1 << 0);
   const long ICC_SERVICE_CLASS_DATA = (1 << 1);
   const long ICC_SERVICE_CLASS_FAX = (1 << 2);
   const long ICC_SERVICE_CLASS_SMS = (1 << 3);
   const long ICC_SERVICE_CLASS_DATA_SYNC = (1 << 4);
   const long ICC_SERVICE_CLASS_DATA_ASYNC = (1 << 5);
@@ -51,21 +50,16 @@ interface nsIDOMMozMobileConnection : ns
    * Indicates the number of retries remaining when cardState equals 'pinRequired'
    * or 'pukRequired'.  0 denotes the retry count is unavailable.
    *
    * Value is undefined for other cardState values.
    */
   readonly attribute long retryCount;
 
   /**
-   * Information stored in the device's ICC card.
-   */
-  readonly attribute nsIDOMMozMobileICCInfo iccInfo;
-
-  /**
    * Information about the voice connection.
    */
   readonly attribute nsIDOMMozMobileConnectionInfo voice;
 
   /**
    * Information about the data connection.
    */
   readonly attribute nsIDOMMozMobileConnectionInfo data;
@@ -227,22 +221,16 @@ interface nsIDOMMozMobileConnection : ns
    *
    *
    * Otherwise, the request's onerror will be called, and the request's error
    * will be either 'RadioNotAvailable', 'RequestNotSupported',
    * or 'GenericFailure'.
    */
   nsIDOMDOMRequest getCallWaitingOption();
 
-   /**
-   * The 'iccinfochange' event is notified whenever the icc info object
-   * changes.
-   */
-  [implicit_jscontext] attribute jsval oniccinfochange;
-
   /**
    * The 'voicechange' event is notified whenever the voice connection object
    * changes.
    */
   [implicit_jscontext] attribute jsval onvoicechange;
 
   /**
    * The 'datachange' event is notified whenever the data connection object
@@ -375,56 +363,16 @@ interface nsIDOMMozMobileCellInfo: nsISu
   readonly attribute unsigned short gsmLocationAreaCode;
 
   /**
    * Mobile Cell ID for GSM/WCDMA networks.
    */
   readonly attribute unsigned long gsmCellId;
 };
 
-[scriptable, uuid(10d5c5a2-d43f-4f94-8657-cf7ccabbab6e)]
-interface nsIDOMMozMobileICCInfo : nsISupports
-{
-   /**
-   * Integrated Circuit Card Identifier.
-   */
-  readonly attribute DOMString iccid;
-
-  /**
-   * Mobile Country Code (MCC) of the subscriber's home network.
-   */
-  readonly attribute DOMString mcc;
-
-  /**
-   * Mobile Network Code (MNC) of the subscriber's home network.
-   */
-  readonly attribute DOMString mnc;
-
-  /**
-   * Service Provider Name (SPN) of the subscriber's home network.
-   */
-  readonly attribute DOMString spn;
-
-  /**
-   * Network name must be a part of displayed carrier name.
-   */
-  readonly attribute boolean isDisplayNetworkNameRequired;
-
-  /**
-   * Service provider name must be a part of displayed carrier name.
-   */
-  readonly attribute boolean isDisplaySpnRequired;
-
-  /**
-   * Mobile Station ISDN Number (MSISDN) of the subscriber's, aka
-   * his phone number.
-   */
-  readonly attribute DOMString msisdn;
-};
-
 [scriptable, uuid(d1b35ad8-99aa-47cc-ab49-2e72b00e39df)]
 interface nsIDOMMozMobileCFInfo : nsISupports
 {
   /**
    * Call forwarding rule status.
    *
    * It will be either not active (false), or active (true).
    *
--- a/dom/network/interfaces/nsIMobileConnectionProvider.idl
+++ b/dom/network/interfaces/nsIMobileConnectionProvider.idl
@@ -1,55 +1,52 @@
 /* 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 nsIDOMMozMobileICCInfo;
+interface nsIDOMDOMRequest;
+interface nsIDOMMozMobileCFInfo;
 interface nsIDOMMozMobileConnectionInfo;
 interface nsIDOMMozMobileNetworkInfo;
-interface nsIDOMMozMobileCFInfo;
-interface nsIDOMDOMRequest;
 interface nsIDOMWindow;
 
-[scriptable, uuid(529864f0-c2be-11e2-8b8b-0800200c9a66)]
+[scriptable, uuid(74361840-c913-11e2-8b8b-0800200c9a66)]
 interface nsIMobileConnectionListener : nsISupports
 {
   void notifyVoiceChanged();
   void notifyDataChanged();
-  void notifyIccInfoChanged();
   void notifyUssdReceived(in DOMString message,
                           in boolean sessionEnded);
   void notifyDataError(in DOMString message);
   void notifyCFStateChange(in boolean success,
                            in unsigned short action,
                            in unsigned short reason,
                            in DOMString number,
                            in unsigned short timeSeconds,
                            in unsigned short serviceClass);
 };
 
 /**
  * XPCOM component (in the content process) that provides the mobile
  * network information.
  */
-[scriptable, uuid(66e7ac90-c2be-11e2-8b8b-0800200c9a66)]
+[scriptable, uuid(477d93f0-c913-11e2-8b8b-0800200c9a66)]
 interface nsIMobileConnectionProvider : nsISupports
 {
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'mobileconnection' permission is allowed to register.
    */
   void registerMobileConnectionMsg(in nsIMobileConnectionListener listener);
   void unregisterMobileConnectionMsg(in nsIMobileConnectionListener listener);
 
   readonly attribute long retryCount;
-  readonly attribute nsIDOMMozMobileICCInfo iccInfo;
   readonly attribute nsIDOMMozMobileConnectionInfo voiceConnectionInfo;
   readonly attribute nsIDOMMozMobileConnectionInfo dataConnectionInfo;
   readonly attribute DOMString networkSelectionMode;
 
   nsIDOMDOMRequest getNetworks(in nsIDOMWindow window);
   nsIDOMDOMRequest selectNetwork(in nsIDOMWindow window, in nsIDOMMozMobileNetworkInfo network);
   nsIDOMDOMRequest selectNetworkAutomatically(in nsIDOMWindow window);
 
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -62,17 +62,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MobileConnection)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozMobileConnection)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozMobileConnection)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(MobileConnection, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MobileConnection, nsDOMEventTargetHelper)
 
-NS_IMPL_EVENT_HANDLER(MobileConnection, iccinfochange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, voicechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, datachange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, ussdreceived)
 NS_IMPL_EVENT_HANDLER(MobileConnection, dataerror)
 NS_IMPL_EVENT_HANDLER(MobileConnection, cfstatechange)
 
 MobileConnection::MobileConnection()
 {
@@ -168,27 +167,16 @@ MobileConnection::GetRetryCount(int32_t*
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
     return NS_OK;
   }
   return mProvider->GetRetryCount(retryCount);
 }
 
 NS_IMETHODIMP
-MobileConnection::GetIccInfo(nsIDOMMozMobileICCInfo** aIccInfo)
-{
-  *aIccInfo = nullptr;
-
-  if (!mProvider || !CheckPermission("mobileconnection")) {
-    return NS_OK;
-  }
-  return mProvider->GetIccInfo(aIccInfo);
-}
-
-NS_IMETHODIMP
 MobileConnection::GetVoice(nsIDOMMozMobileConnectionInfo** voice)
 {
   *voice = nullptr;
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
     return NS_OK;
   }
   return mProvider->GetVoiceConnectionInfo(voice);
@@ -412,26 +400,16 @@ MobileConnection::NotifyDataChanged()
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   return DispatchTrustedEvent(NS_LITERAL_STRING("datachange"));
 }
 
 NS_IMETHODIMP
-MobileConnection::NotifyIccInfoChanged()
-{
-  if (!CheckPermission("mobileconnection")) {
-    return NS_OK;
-  }
-
-  return DispatchTrustedEvent(NS_LITERAL_STRING("iccinfochange"));
-}
-
-NS_IMETHODIMP
 MobileConnection::NotifyUssdReceived(const nsAString& aMessage,
                                      bool aSessionEnded)
 {
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMEvent> event;
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -19,16 +19,17 @@
 
 #include "GonkGPSGeolocationProvider.h"
 #include "SystemWorkerManager.h"
 #include "mozilla/Preferences.h"
 #include "nsGeoPosition.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsINetworkManager.h"
 #include "nsIRadioInterfaceLayer.h"
+#include "nsIDOMIccInfo.h"
 #include "nsIDOMMobileConnection.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 
 #ifdef AGPS_TYPE_INVALID
 #define AGPS_HAVE_DUAL_APN
@@ -417,17 +418,17 @@ GonkGPSGeolocationProvider::RequestSetID
   if (rilCtx) {
     nsAutoString id;
     if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
       type = AGPS_SETID_TYPE_IMSI;
       rilCtx->GetImsi(id);
     }
 
     if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
-      nsCOMPtr<nsIDOMMozMobileICCInfo> iccInfo;
+      nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
       rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
       if (iccInfo) {
         type = AGPS_SETID_TYPE_MSISDN;
         iccInfo->GetMsisdn(id);
       }
     }
 
     NS_ConvertUTF16toUTF8 idBytes(id);
@@ -448,17 +449,17 @@ GonkGPSGeolocationProvider::SetReference
   mRadioInterface->GetRilContext(getter_AddRefs(rilCtx));
 
   AGpsRefLocation location;
 
   // TODO: Bug 772750 - get mobile connection technology from rilcontext
   location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
 
   if (rilCtx) {
-    nsCOMPtr<nsIDOMMozMobileICCInfo> iccInfo;
+    nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
     rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
     if (iccInfo) {
       nsresult result;
       nsAutoString mcc, mnc;
 
       iccInfo->GetMcc(mcc);
       iccInfo->GetMnc(mnc);
 
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -40,18 +40,18 @@ if (DEBUG) {
     dump("-*- RILContentHelper: " + s + "\n");
   };
 } else {
   debug = function (s) {};
 }
 
 const RILCONTENTHELPER_CID =
   Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
-const MOBILEICCINFO_CID =
-  Components.ID("{8649c12f-f8f4-4664-bbdd-7d115c23e2a7}");
+const ICCINFO_CID =
+  Components.ID("{fab2c0f0-d73a-11e2-8b8b-0800200c9a66}");
 const MOBILECONNECTIONINFO_CID =
   Components.ID("{a35cfd39-2d93-4489-ac7d-396475dacb27}");
 const MOBILENETWORKINFO_CID =
   Components.ID("{a6c8416c-09b4-46d1-bf29-6520d677d085}");
 const MOBILECELLINFO_CID =
   Components.ID("{5e809018-68c0-4c54-af0b-2a9b8f748c45}");
 const VOICEMAILSTATUS_CID=
   Components.ID("{5467f2eb-e214-43ea-9b89-67711241ec8e}");
@@ -130,28 +130,28 @@ function MobileIccCardLockRetryCount(opt
   this.success = options.success;
 }
 MobileIccCardLockRetryCount.prototype = {
   __exposedProps__ : {lockType: 'r',
                       retryCount: 'r',
                       success: 'r'}
 };
 
-function MobileICCInfo() {}
-MobileICCInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozMobileICCInfo]),
-  classID:        MOBILEICCINFO_CID,
+function IccInfo() {}
+IccInfo.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozIccInfo]),
+  classID:        ICCINFO_CID,
   classInfo:      XPCOMUtils.generateCI({
-    classID:          MOBILEICCINFO_CID,
-    classDescription: "MobileICCInfo",
+    classID:          ICCINFO_CID,
+    classDescription: "IccInfo",
     flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozMobileICCInfo]
+    interfaces:       [Ci.nsIDOMMozIccInfo]
   }),
 
-  // nsIDOMMozMobileICCInfo
+  // nsIDOMMozIccInfo
 
   iccid: null,
   mcc: null,
   mnc: null,
   spn: null,
   msisdn: null
 };
 
@@ -376,17 +376,17 @@ DOMMMIError.prototype = {
   },
 };
 
 function RILContentHelper() {
   this.rilContext = {
     cardState:            RIL.GECKO_CARDSTATE_UNKNOWN,
     retryCount:           0,
     networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
-    iccInfo:              new MobileICCInfo(),
+    iccInfo:              new IccInfo(),
     voiceConnectionInfo:  new MobileConnectionInfo(),
     dataConnectionInfo:   new MobileConnectionInfo()
   };
   this.voicemailInfo = new VoicemailInfo();
 
   this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
   Services.obs.addObserver(this, "xpcom-shutdown", false);
@@ -1332,18 +1332,17 @@ RILContentHelper.prototype = {
           this._deliverEvent("_iccListeners",
                              "notifyCardStateChanged",
                              null);
         }
         break;
       }
       case "RIL:IccInfoChanged":
         this.updateInfo(msg.json.data, this.rilContext.iccInfo);
-        this._deliverEvent("_mobileConnectionListeners",
-                           "notifyIccInfoChanged", null);
+        this._deliverEvent("_iccListeners", "notifyIccInfoChanged", null);
         break;
       case "RIL:VoiceInfoChanged":
         this.updateConnectionInfo(msg.json.data,
                                   this.rilContext.voiceConnectionInfo);
         this._deliverEvent("_mobileConnectionListeners",
                            "notifyVoiceChanged",
                            null);
         break;
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -605,22 +605,30 @@ XPCOMUtils.defineLazyGetter(RadioInterfa
   } catch (e) {
     return 1;
   }
 });
 
 function RadioInterface(options) {
   this.clientId = options.clientId;
 
-  this.dataCallSettings = {};
-  this.dataNetworkInterface = new RILNetworkInterface(this, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
-  this.dataCallSettingsMMS = {};
-  this.mmsNetworkInterface = new RILNetworkInterface(this, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS);
-  this.dataCallSettingsSUPL = {};
-  this.suplNetworkInterface = new RILNetworkInterface(this, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL);
+  this.dataCallSettings = {
+    oldEnabled: false,
+    enabled: false,
+    roamingEnabled: false
+  };
+
+  // apnSettings is used to keep all APN settings.
+  // byApn[] makes it easier to get the APN settings via APN, user
+  // name, and password.
+  // byType[] makes it easier to get the APN settings via APN types.
+  this.apnSettings = {
+      byType: {},
+      byAPN: {}
+  };
 
   if (DEBUG) this.debug("Starting RIL Worker[" + this.clientId + "]");
   this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js");
   this.worker.onerror = this.onerror.bind(this);
   this.worker.onmessage = this.onmessage.bind(this);
 
   // Pass initial options to ril_worker.
   options.rilMessageType = "setInitialOptions";
@@ -672,49 +680,19 @@ function RadioInterface(options) {
   // value at boot time.
   let lock = gSettingsService.createLock();
   lock.get("ril.radio.disabled", this);
 
   // Read preferred network type from the setting DB.
   lock.get("ril.radio.preferredNetworkType", this);
 
   // Read the APN data from the settings DB.
-  lock.get("ril.data.apn", this);
-  lock.get("ril.data.user", this);
-  lock.get("ril.data.passwd", this);
-  lock.get("ril.data.authtype", this);
-  lock.get("ril.data.httpProxyHost", this);
-  lock.get("ril.data.httpProxyPort", this);
   lock.get("ril.data.roaming_enabled", this);
   lock.get("ril.data.enabled", this);
-  this._dataCallSettingsToRead = ["ril.data.enabled",
-                                  "ril.data.roaming_enabled",
-                                  "ril.data.apn",
-                                  "ril.data.user",
-                                  "ril.data.passwd",
-                                  "ril.data.authtype",
-                                  "ril.data.httpProxyHost",
-                                  "ril.data.httpProxyPort"];
-
-  // Read secondary APNs from the settings DB.
-  lock.get("ril.mms.apn", this);
-  lock.get("ril.mms.user", this);
-  lock.get("ril.mms.passwd", this);
-  lock.get("ril.mms.authtype", this);
-  lock.get("ril.mms.httpProxyHost", this);
-  lock.get("ril.mms.httpProxyPort", this);
-  lock.get("ril.mms.mmsc", this);
-  lock.get("ril.mms.mmsproxy", this);
-  lock.get("ril.mms.mmsport", this);
-  lock.get("ril.supl.apn", this);
-  lock.get("ril.supl.user", this);
-  lock.get("ril.supl.passwd", this);
-  lock.get("ril.supl.authtype", this);
-  lock.get("ril.supl.httpProxyHost", this);
-  lock.get("ril.supl.httpProxyPort", this);
+  lock.get("ril.data.apnSettings", this);
 
   // Read the 'time.nitz.automatic-update.enabled' setting to see if
   // we need to adjust the system clock time and time zone by NITZ.
   lock.get(kTimeNitzAutomaticUpdateEnabled, this);
 
   // Set "time.nitz.available" to false when starting up.
   this.setNitzAvailable(false);
 
@@ -1213,18 +1191,22 @@ RadioInterface.prototype = {
   updateDataConnection: function updateDataConnection(newInfo) {
     let dataInfo = this.rilContext.data;
     dataInfo.state = newInfo.state;
     dataInfo.roaming = newInfo.roaming;
     dataInfo.emergencyCallsOnly = newInfo.emergencyCallsOnly;
     dataInfo.type = newInfo.type;
     // For the data connection, the `connected` flag indicates whether
     // there's an active data call.
-    dataInfo.connected = (this.getDataCallStateByType("default") ==
-                          RIL.GECKO_NETWORK_STATE_CONNECTED);
+    let apnSetting = this.apnSettings.byType.default;
+    dataInfo.connected = false;
+    if (apnSetting) {
+      dataInfo.connected = (this.getDataCallStateByType("default") ==
+                            RIL.GECKO_NETWORK_STATE_CONNECTED);
+    }
 
     // Make sure we also reset the operator and signal strength information
     // if we drop off the network.
     if (newInfo.regState == RIL.NETWORK_CREG_STATE_UNKNOWN) {
       dataInfo.network = null;
       dataInfo.signalStrength = null;
       dataInfo.relSignalStrength = null;
     }
@@ -1243,19 +1225,23 @@ RadioInterface.prototype = {
     this.updateRILNetworkInterface();
   },
 
   /**
    * Handle data errors
    */
   handleDataCallError: function handleDataCallError(message) {
     // Notify data call error only for data APN
-    if (message.apn == this.dataCallSettings.apn) {
-      gMessageManager.sendMobileConnectionMessage("RIL:DataError",
-                                                  this.clientId, message);
+    if (this.apnSettings.byType.default) {
+      let apnSetting = this.apnSettings.byType.default;
+      if (message.apn == apnSetting.apn &&
+          apnSetting.iface.inConnectedTypes("default")) {
+        gMessageManager.sendMobileConnectionMessage("RIL:DataError",
+                                                    this.clientId, message);
+      }
     }
 
     this._deliverDataCallCallback("dataCallError", [message]);
   },
 
   _preferredNetworkType: null,
   setPreferredNetworkType: function setPreferredNetworkType(value) {
     let networkType = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(value);
@@ -1425,21 +1411,95 @@ RadioInterface.prototype = {
       this.setRadioEnabled(true);
     }
     if (this.rilContext.radioState == RIL.GECKO_RADIOSTATE_READY &&
         !this._radioEnabled) {
       this.setRadioEnabled(false);
     }
   },
 
+  /**
+   * This function will do the following steps:
+   * 1. Clear the old APN settings.
+   * 2. Combine APN, user name, and password as the key of byAPN{} and store
+   *    corresponding APN setting into byApn{}, which makes it easiler to get
+   *    the APN setting.
+   * 3. Use APN type as the index of byType{} and store the link of
+   *    corresponding APN setting into byType{}, which makes it easier to get
+   *    the APN setting via APN types.
+   */
+  updateApnSettings: function updateApnSettings(allApnSettings) {
+    // TODO: Support multi-SIM, bug 799023.
+    let simNumber = 1;
+    for (let simId = 0; simId < simNumber; simId++) {
+      let thisSimApnSettings = allApnSettings[simId];
+      if (!thisSimApnSettings) {
+        return;
+      }
+
+      // Clear old APN settings.
+      for each (let apnSetting in this.apnSettings.byAPN) {
+        // Clear all connections of this APN settings.
+        for each (let type in apnSetting.types) {
+          if (this.getDataCallStateByType(type) ==
+              RIL.GECKO_NETWORK_STATE_CONNECTED) {
+            this.deactivateDataCallByType(type);
+          }
+        }
+        if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
+          gNetworkManager.unregisterNetworkInterface(apnSetting.iface);
+        }
+        this.unregisterDataCallCallback(apnSetting.iface);
+        delete apnSetting.iface;
+      }
+      this.apnSettings.byAPN = {};
+      this.apnSettings.byType = {};
+
+      // Create new APN settings.
+      for (let apnIndex = 0; thisSimApnSettings[apnIndex]; apnIndex++) {
+        let inputApnSetting = thisSimApnSettings[apnIndex];
+        if (!this.validateApnSetting(inputApnSetting)) {
+          continue;
+        }
+
+        // Combine APN, user name, and password as the key of byAPN{} to get
+        // the corresponding APN setting.
+        let apnKey = inputApnSetting.apn + (inputApnSetting.user || '') +
+                     (inputApnSetting.password || '');
+        if (!this.apnSettings.byAPN[apnKey]) {
+          this.apnSettings.byAPN[apnKey] = {};
+          this.apnSettings.byAPN[apnKey] = inputApnSetting;
+          this.apnSettings.byAPN[apnKey].iface =
+            new RILNetworkInterface(this, this.apnSettings.byAPN[apnKey]);
+        } else {
+          this.apnSettings.byAPN[apnKey].types.push(inputApnSetting.types);
+        }
+        for each (let type in inputApnSetting.types) {
+          this.apnSettings.byType[type] = {};
+          this.apnSettings.byType[type] = this.apnSettings.byAPN[apnKey];
+        }
+      }
+    }
+  },
+
+  /**
+   * Check if we get all necessary APN data.
+   */
+  validateApnSetting: function validateApnSetting(apnSetting) {
+    return (apnSetting &&
+            apnSetting.apn &&
+            apnSetting.types &&
+            apnSetting.types.length);
+  },
+
   updateRILNetworkInterface: function updateRILNetworkInterface() {
-    if (this._dataCallSettingsToRead.length) {
+    let apnSetting = this.apnSettings.byType.default;
+    if (!this.validateApnSetting(apnSetting)) {
       if (DEBUG) {
-        this.debug("We haven't read completely the APN data from the " +
-                   "settings DB yet. Wait for that.");
+        this.debug("We haven't gotten completely the APN data.");
       }
       return;
     }
 
     // This check avoids data call connection if the radio is not ready
     // yet after toggling off airplane mode.
     if (this.rilContext.radioState != RIL.GECKO_RADIOSTATE_READY) {
       if (DEBUG) {
@@ -1448,17 +1508,17 @@ RadioInterface.prototype = {
       return;
     }
 
     // We only watch at "ril.data.enabled" flag changes for connecting or
     // disconnecting the data call. If the value of "ril.data.enabled" is
     // true and any of the remaining flags change the setting application
     // should turn this flag to false and then to true in order to reload
     // the new values and reconnect the data call.
-    if (this._oldRilDataEnabledState == this.dataCallSettings.enabled) {
+    if (this.dataCallSettings.oldEnabled == this.dataCallSettings.enabled) {
       if (DEBUG) {
         this.debug("No changes for ril.data.enabled flag. Nothing to do.");
       }
       return;
     }
 
     let defaultDataCallState = this.getDataCallStateByType("default");
     if (defaultDataCallState == RIL.GECKO_NETWORK_STATE_CONNECTING ||
@@ -1485,27 +1545,34 @@ RadioInterface.prototype = {
     if (gNetworkManager.active &&
         gNetworkManager.active.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
       wifi_active = true;
     }
 
     let defaultDataCallConnected = defaultDataCallState ==
                                    RIL.GECKO_NETWORK_STATE_CONNECTED;
     if (defaultDataCallConnected &&
-        (!this.dataCallSettings.enabled || wifi_active ||
-         (dataInfo.roaming && !this.dataCallSettings.roaming_enabled))) {
+        (!this.dataCallSettings.enabled ||
+         (dataInfo.roaming && !this.dataCallSettings.roamingEnabled))) {
       if (DEBUG) this.debug("Data call settings: disconnect data call.");
       this.deactivateDataCallByType("default");
       return;
     }
+
+    if (defaultDataCallConnected && wifi_active) {
+      if (DEBUG) this.debug("Disconnect data call when Wifi is connected.");
+      this.deactivateDataCallByType("default");
+      return;
+    }
+
     if (!this.dataCallSettings.enabled || defaultDataCallConnected) {
       if (DEBUG) this.debug("Data call settings: nothing to do.");
       return;
     }
-    if (dataInfo.roaming && !this.dataCallSettings.roaming_enabled) {
+    if (dataInfo.roaming && !this.dataCallSettings.roamingEnabled) {
       if (DEBUG) this.debug("We're roaming, but data roaming is disabled.");
       return;
     }
     if (wifi_active) {
       if (DEBUG) this.debug("Don't connect data call when Wifi is connected.");
       return;
     }
     if (this._changingRadioPower) {
@@ -1975,25 +2042,32 @@ RadioInterface.prototype = {
     }.bind(this));
   },
 
   /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
     let data = this.rilContext.data;
-
-    if (datacall.ifname && datacall.apn == this.dataCallSettings.apn) {
-      data.connected = false;
-      if (this.dataNetworkInterface.inConnectedTypes("default") &&
-          datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED) {
-        data.connected = true;
+    let apnSetting = this.apnSettings.byType.default;
+    let dataCallConnected =
+        (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTED);
+    if (apnSetting && datacall.ifname) {
+      if (dataCallConnected && datacall.apn == apnSetting.apn &&
+          apnSetting.iface.inConnectedTypes("default")) {
+        data.connected = dataCallConnected;
+        gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
+                                                     this.clientId, data);
+        data.apn = datacall.apn;
+      } else if (!dataCallConnected && datacall.apn == data.apn) {
+        data.connected = dataCallConnected;
+        delete data.apn;
+        gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
+                                                     this.clientId, data);
       }
-      gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
-                                                  this.clientId, data);
     }
 
     this._deliverDataCallCallback("dataCallStateChanged",
                                   [datacall]);
   },
 
   /**
    * Handle data call list.
@@ -2075,18 +2149,18 @@ RadioInterface.prototype = {
                           oldIccInfo.isDisplayNetworkNameRequired != message.isDisplayNetworkNameRequired ||
                           oldIccInfo.isDisplaySpnRequired != message.isDisplaySpnRequired ||
                           oldIccInfo.msisdn != message.msisdn;
     if (!iccInfoChanged) {
       return;
     }
     // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
     // when the MCC or MNC codes have changed.
-    gMessageManager.sendMobileConnectionMessage("RIL:IccInfoChanged",
-                                                this.clientId, message);
+    gMessageManager.sendIccMessage("RIL:IccInfoChanged",
+                                   this.clientId, message);
 
     // Update lastKnownHomeNetwork.
     if (message.mcc && message.mnc) {
       try {
         Services.prefs.setCharPref("ril.lastKnownHomeNetwork",
                                    message.mcc + "-" + message.mnc);
       } catch (e) {}
     }
@@ -2210,19 +2284,21 @@ RadioInterface.prototype = {
             disabled: value
           });
         }
         break;
       case "xpcom-shutdown":
         // Cancel the timer for the call-ring wake lock.
         this._cancelCallRingWakeLockTimer();
         // Shutdown all RIL network interfaces
-        this.dataNetworkInterface.shutdown();
-        this.mmsNetworkInterface.shutdown();
-        this.suplNetworkInterface.shutdown();
+        for each (let apnSetting in this.apnSettings.byAPN) {
+          if (apnSetting.iface) {
+            apnSetting.iface.shutdown();
+          }
+        }
         Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
         Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
         Services.obs.removeObserver(this, kScreenStateChangedTopic);
         Services.prefs.removeObserver(kCellBroadcastDisabled, this);
         break;
       case kSysClockChangeObserverTopic:
         if (this._lastNitzMessage) {
@@ -2242,22 +2318,20 @@ RadioInterface.prototype = {
   // Flag to determine the radio state to start with when we boot up. It
   // corresponds to the 'ril.radio.disabled' setting from the UI.
   _radioEnabled: null,
 
   // Flag to ignore any radio power change requests during We're changing
   // the radio power.
   _changingRadioPower: false,
 
-  // APN data for making data calls.
+  // Data calls setting.
   dataCallSettings: null,
-  dataCallSettingsMMS: null,
-  dataCallSettingsSUPL: null,
-  _dataCallSettingsToRead: null,
-  _oldRilDataEnabledState: null,
+
+  apnSettings: null,
 
   // Flag to determine whether to use NITZ. It corresponds to the
   // 'time.nitz.automatic-update.enabled' setting from the UI.
   _nitzAutomaticUpdateEnabled: null,
 
   // Remember the last NITZ message so that we can set the time based on
   // the network immediately when users enable network-based time.
   _lastNitzMessage: null,
@@ -2289,56 +2363,32 @@ RadioInterface.prototype = {
         this._radioEnabled = !aResult;
         this._ensureRadioState();
         break;
       case "ril.radio.preferredNetworkType":
         if (DEBUG) this.debug("'ril.radio.preferredNetworkType' is now " + aResult);
         this.setPreferredNetworkType(aResult);
         break;
       case "ril.data.enabled":
-        this._oldRilDataEnabledState = this.dataCallSettings.enabled;
-        // Fall through.
-      case "ril.data.roaming_enabled":
-      case "ril.data.apn":
-      case "ril.data.user":
-      case "ril.data.passwd":
-      case "ril.data.authtype":
-      case "ril.data.httpProxyHost":
-      case "ril.data.httpProxyPort":
-        let key = aName.slice(9);
-        this.dataCallSettings[key] = aResult;
-        if (DEBUG) {
-          this.debug("'" + aName + "'" + " is now " + this.dataCallSettings[key]);
-        }
-        let index = this._dataCallSettingsToRead.indexOf(aName);
-        if (index != -1) {
-          this._dataCallSettingsToRead.splice(index, 1);
-        }
+        if (DEBUG) this.debug("'ril.data.enabled' is now " + aResult);
+        this.dataCallSettings.oldEnabled = this.dataCallSettings.enabled;
+        this.dataCallSettings.enabled = aResult;
         this.updateRILNetworkInterface();
         break;
-      case "ril.mms.apn":
-      case "ril.mms.user":
-      case "ril.mms.passwd":
-      case "ril.mms.authtype":
-      case "ril.mms.httpProxyHost":
-      case "ril.mms.httpProxyPort":
-      case "ril.mms.mmsc":
-      case "ril.mms.mmsproxy":
-      case "ril.mms.mmsport":
-        key = aName.slice(8);
-        this.dataCallSettingsMMS[key] = aResult;
+      case "ril.data.roaming_enabled":
+        if (DEBUG) this.debug("'ril.data.roaming_enabled' is now " + aResult);
+        this.dataCallSettings.roamingEnabled = aResult;
+        this.updateRILNetworkInterface();
         break;
-      case "ril.supl.apn":
-      case "ril.supl.user":
-      case "ril.supl.passwd":
-      case "ril.supl.authtype":
-      case "ril.supl.httpProxyHost":
-      case "ril.supl.httpProxyPort":
-        key = aName.slice(9);
-        this.dataCallSettingsSUPL[key] = aResult;
+      case "ril.data.apnSettings":
+        if (DEBUG) this.debug("'ril.data.apnSettings' is now " + JSON.stringify(aResult));
+        if (aResult) {
+          this.updateApnSettings(aResult);
+          this.updateRILNetworkInterface();
+        }
         break;
       case kTimeNitzAutomaticUpdateEnabled:
         this._nitzAutomaticUpdateEnabled = aResult;
 
         // Set the latest cached NITZ time if the setting is enabled.
         if (this._nitzAutomaticUpdateEnabled && this._lastNitzMessage) {
           this.setNitzTime(this._lastNitzMessage);
         }
@@ -2355,18 +2405,23 @@ RadioInterface.prototype = {
   handleError: function handleError(aErrorMessage) {
     if (DEBUG) this.debug("There was an error while reading RIL settings.");
 
     // Default radio to on.
     this._radioEnabled = true;
     this._ensureRadioState();
 
     // Clean data call setting.
-    this.dataCallSettings = {};
+    this.dataCallSettings.oldEnabled = false;
     this.dataCallSettings.enabled = false;
+    this.dataCallSettings.roamingEnabled = false;
+    this.apnSettings = {
+      byType: {},
+      byAPN: {},
+    };
   },
 
   // nsIRadioWorker
 
   worker: null,
 
   // nsIRadioInterface
 
@@ -3156,163 +3211,99 @@ RadioInterface.prototype = {
       } catch (e) {
         if (DEBUG) {
           this.debug("callback handler for " + name + " threw an exception: " + e);
         }
       }
     }
   },
 
-  /**
-   * Determine whether secondary APN goes through default APN.
-   */
-  usingDefaultAPN: function usingDefaultAPN(apntype) {
-    switch (apntype) {
-      case "mms":
-        return (this.dataCallSettingsMMS.apn == this.dataCallSettings.apn);
-      case "supl":
-        return (this.dataCallSettingsSUPL.apn == this.dataCallSettings.apn);
-      default:
-        return false;
+  setupDataCallByType: function setupDataCallByType(apntype) {
+    let apnSetting = this.apnSettings.byType[apntype];
+    if (!apnSetting) {
+      return;
     }
-  },
-
-  setupDataCallBySharedApn: function setupDataCallBySharedApn(apntype) {
-    this.dataNetworkInterface.connect(this.dataCallSettings, apntype);
-
+
+    let dataInfo = this.rilContext.data;
+    if (dataInfo.state != RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED ||
+        dataInfo.type == RIL.GECKO_MOBILE_CONNECTION_STATE_UNKNOWN) {
+      return;
+    }
+
+    apnSetting.iface.connect(apntype);
     // We just call connect() function, so this interface should be in
     // connecting state. If this interface is already in connected state, we
     // are sure that this interface have successfully established connection
     // for other data call types before we call connect() function for current
     // data call type. In this circumstance, we have to directly update the
     // necessary data call and interface information to RILContentHelper
-    // and network manager.
-    if (this.dataNetworkInterface.connected) {
-      let dataInfo = this.rilContext.data;
+    // and network manager for current data call type.
+    if (apnSetting.iface.connected) {
       if (apntype == "default" && !dataInfo.connected) {
         dataInfo.connected = true;
         gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
                                                     this.clientId, dataInfo);
       }
 
       // Update the interface status via-registration if the interface has
       // already been registered in the network manager.
-      if (this.dataNetworkInterface.name in gNetworkManager.networkInterfaces) {
-        gNetworkManager.unregisterNetworkInterface(this.dataNetworkInterface);
+      if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
+        gNetworkManager.unregisterNetworkInterface(apnSetting.iface);
       }
-      gNetworkManager.registerNetworkInterface(this.dataNetworkInterface);
-
-      Services.obs.notifyObservers(this.dataNetworkInterface,
+      gNetworkManager.registerNetworkInterface(apnSetting.iface);
+
+      Services.obs.notifyObservers(apnSetting.iface,
                                    kNetworkInterfaceStateChangedTopic,
                                    null);
     }
   },
 
-  setupDataCallByType: function setupDataCallByType(apntype) {
-    // If it's a shared apn type then we can only reuse the
-    // dataNetworkInterface in current design.
-    if (this.usingDefaultAPN(apntype) ||
-        (apntype == "default" && this.usingDefaultAPN("mms")) ||
-        (apntype == "default" && this.usingDefaultAPN("supl"))) {
-      this.setupDataCallBySharedApn(apntype);
+  deactivateDataCallByType: function deactivateDataCallByType(apntype) {
+    let apnSetting = this.apnSettings.byType[apntype];
+    if (!apnSetting) {
       return;
     }
-    switch (apntype) {
-      case "default":
-        this.dataNetworkInterface.connect(this.dataCallSettings, apntype);
-        break;
-      case "mms":
-        this.mmsNetworkInterface.connect(this.dataCallSettingsMMS, apntype);
-        break;
-      case "supl":
-        this.suplNetworkInterface.connect(this.dataCallSettingsSUPL, apntype);
-        break;
-      default:
-        if (DEBUG) this.debug("Unsupported APN type " + apntype);
-        break;
-    }
-  },
-
-  deactivateDataCallBySharedApn: function deactivateDataCallBySharedApn(apntype) {
-    this.dataNetworkInterface.disconnect(apntype);
-
+
+    apnSetting.iface.disconnect(apntype);
     // We just call disconnect() function, so this interface should be in
     // disconnecting state. If this interface is still in connected state, we
     // are sure that other data call types still need this connection of this
     // interface. In this circumstance, we have to directly update the
     // necessary data call and interface information to RILContentHelper
-    // and network manager.
-    if (this.dataNetworkInterface.connectedTypes.length &&
-        this.dataNetworkInterface.connected) {
+    // and network manager for current data call type.
+    if (apnSetting.iface.connectedTypes.length && apnSetting.iface.connected) {
       let dataInfo = this.rilContext.data;
       if (apntype == "default" && dataInfo.connected) {
         dataInfo.connected = false;
         gMessageManager.sendMobileConnectionMessage("RIL:DataInfoChanged",
                                                     this.clientId, dataInfo);
       }
 
       // Update the interface status via-registration if the interface has
       // already been registered in the network manager.
-      if (this.dataNetworkInterface.name in gNetworkManager.networkInterfaces) {
-        gNetworkManager.unregisterNetworkInterface(this.dataNetworkInterface);
+      if (apnSetting.iface.name in gNetworkManager.networkInterfaces) {
+        gNetworkManager.unregisterNetworkInterface(apnSetting.iface);
       }
-      gNetworkManager.registerNetworkInterface(this.dataNetworkInterface);
-
-      Services.obs.notifyObservers(this.dataNetworkInterface,
+      gNetworkManager.registerNetworkInterface(apnSetting.iface);
+
+      Services.obs.notifyObservers(apnSetting.iface,
                                    kNetworkInterfaceStateChangedTopic,
                                    null);
     }
   },
 
-  deactivateDataCallByType: function deactivateDataCallByType(apntype) {
-    // If it's a shared apn type then we can only reuse the
-    // dataNetworkInterface in current design.
-    if (this.usingDefaultAPN(apntype) ||
-        (apntype == "default" && this.usingDefaultAPN("mms")) ||
-        (apntype == "default" && this.usingDefaultAPN("supl"))) {
-      this.deactivateDataCallBySharedApn(apntype);
-      return;
-    }
-    switch (apntype) {
-      case "default":
-        this.dataNetworkInterface.disconnect(apntype);
-        break;
-      case "mms":
-        this.mmsNetworkInterface.disconnect(apntype);
-        break;
-      case "supl":
-        this.suplNetworkInterface.disconnect(apntype);
-        break;
-      default:
-        if (DEBUG) this.debug("Unsupported APN type " + apntype);
-        break;
+  getDataCallStateByType: function getDataCallStateByType(apntype) {
+    let apnSetting = this.apnSettings.byType[apntype];
+    if (!apnSetting) {
+       return RIL.GECKO_NETWORK_STATE_UNKNOWN;
     }
-  },
-
-  getDataCallStateByType: function getDataCallStateByType(apntype) {
-    // If it's a shared apn type then we can only reuse the
-    // dataNetworkInterface in current design.
-    if (this.usingDefaultAPN(apntype) ||
-        (apntype == "default" && this.usingDefaultAPN("mms")) ||
-        (apntype == "default" && this.usingDefaultAPN("supl"))) {
-      if (this.dataNetworkInterface.inConnectedTypes(apntype)) {
-         return this.dataNetworkInterface.state;
-      }
-      return RIL.GECKO_NETWORK_STATE_UNKNOWN;
+    if (!apnSetting.iface.inConnectedTypes(apntype)) {
+      return RIL.GECKO_NETWORK_STATE_DISCONNECTED;
     }
-    switch (apntype) {
-      case "default":
-        return this.dataNetworkInterface.state;
-      case "mms":
-        return this.mmsNetworkInterface.state;
-      case "supl":
-        return this.suplNetworkInterface.state;
-      default:
-        return RIL.GECKO_NETWORK_STATE_UNKNOWN;
-    }
+    return apnSetting.iface.state;
   },
 
   setupDataCall: function setupDataCall(radioTech, apn, user, passwd, chappap, pdptype) {
     this.worker.postMessage({rilMessageType: "setupDataCall",
                              radioTech: radioTech,
                              apn: apn,
                              user: user,
                              passwd: passwd,
@@ -3356,19 +3347,19 @@ RadioInterface.prototype = {
   },
 
   updateIccContact: function updateIccContact(message) {
     message.rilMessageType = "updateICCContact";
     this.worker.postMessage(message);
   },
 };
 
-function RILNetworkInterface(radioInterface, type) {
+function RILNetworkInterface(radioInterface, apnSetting) {
   this.radioInterface = radioInterface;
-  this.initType = type;
+  this.apnSetting = apnSetting;
 }
 
 RILNetworkInterface.prototype = {
   classID:   RILNETWORKINTERFACE_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILNETWORKINTERFACE_CID,
                                     classDescription: "RILNetworkInterface",
                                     interfaces: [Ci.nsINetworkInterface,
                                                  Ci.nsIRILDataCallback]}),
@@ -3427,45 +3418,49 @@ RILNetworkInterface.prototype = {
   netmask: null,
 
   broadcast: null,
 
   dns1: null,
 
   dns2: null,
 
-  httpProxyHost: null,
-
-  httpProxyPort: null,
+  get httpProxyHost() {
+    return this.apnSetting.proxy || '';
+  },
+
+  get httpProxyPort() {
+    return this.apnSetting.port || '';
+  },
 
   debug: function debug(s) {
     dump("-*- RILNetworkInterface[" + this.radioInterface.clientId + ":" +
          this.type + "]: " + s + "\n");
   },
 
   // nsIRILDataCallback
 
   dataCallError: function dataCallError(message) {
-    if (message.apn != this.dataCallSettings.apn) {
+    if (message.apn != this.apnSetting.apn) {
       return;
     }
     if (DEBUG) this.debug("Data call error on APN: " + message.apn);
     this.reset();
   },
 
   dataCallStateChanged: function dataCallStateChanged(datacall) {
     if (this.cid && this.cid != datacall.cid) {
     // If data call for this connection existed but cid mismatched,
     // it means this datacall state change is not for us.
       return;
     }
     // If data call for this connection does not exist, it could be state
     // change for new data call.  We only update data call state change
     // if APN name matched.
-    if (!this.cid && datacall.apn != this.dataCallSettings.apn) {
+    if (!this.cid && datacall.apn != this.apnSetting.apn) {
       return;
     }
     if (DEBUG) {
       this.debug("Data call ID: " + datacall.cid + ", interface name: " +
                  datacall.ifname + ", APN name: " + datacall.apn);
     }
     if (this.connecting &&
         (datacall.state == RIL.GECKO_NETWORK_STATE_CONNECTING ||
@@ -3496,22 +3491,24 @@ RILNetworkInterface.prototype = {
       return;
     }
 
     this.state = datacall.state;
 
     // In case the data setting changed while the datacall was being started or
     // ended, let's re-check the setting and potentially adjust the datacall
     // state again.
-    if (this == this.radioInterface.dataNetworkInterface) {
+    if (this.radioInterface.apnSettings.byType.default &&
+        (this.radioInterface.apnSettings.byType.default.apn ==
+         this.apnSetting.apn)) {
       this.radioInterface.updateRILNetworkInterface();
     }
 
     if (this.state == RIL.GECKO_NETWORK_STATE_UNKNOWN &&
-       this.registeredAsNetworkInterface) {
+        this.registeredAsNetworkInterface) {
       gNetworkManager.unregisterNetworkInterface(this);
       this.registeredAsNetworkInterface = false;
       this.cid = null;
       this.connectedTypes = [];
       return;
     }
 
     Services.obs.notifyObservers(this,
@@ -3523,32 +3520,32 @@ RILNetworkInterface.prototype = {
   },
 
   // Helpers
 
   cid: null,
   registeredAsDataCallCallback: false,
   registeredAsNetworkInterface: false,
   connecting: false,
-  dataCallSettings: {},
+  apnSetting: {},
 
   // APN failed connections. Retry counter
   apnRetryCounter: 0,
 
   connectedTypes: [],
 
   inConnectedTypes: function inConnectedTypes(type) {
     return this.connectedTypes.indexOf(type) != -1;
   },
 
   get connected() {
     return this.state == RIL.GECKO_NETWORK_STATE_CONNECTED;
   },
 
-  connect: function connect(options, apntype) {
+  connect: function connect(apntype) {
     if (apntype && !this.inConnectedTypes(apntype)) {
       this.connectedTypes.push(apntype);
     }
 
     if (this.connecting || this.connected) {
       return;
     }
 
@@ -3558,50 +3555,42 @@ RILNetworkInterface.prototype = {
       return;
     }
 
     if (!this.registeredAsDataCallCallback) {
       this.radioInterface.registerDataCallCallback(this);
       this.registeredAsDataCallCallback = true;
     }
 
-    if (options) {
-      // Save the APN data locally for using them in connection retries.
-      this.dataCallSettings = options;
-    }
-
-    if (!this.dataCallSettings.apn) {
+    if (!this.apnSetting.apn) {
       if (DEBUG) this.debug("APN name is empty, nothing to do.");
       return;
     }
 
-    this.httpProxyHost = this.dataCallSettings.httpProxyHost;
-    this.httpProxyPort = this.dataCallSettings.httpProxyPort;
-
     if (DEBUG) {
       this.debug("Going to set up data connection with APN " +
-                 this.dataCallSettings.apn);
+                 this.apnSetting.apn);
     }
     let radioTechType = this.radioInterface.rilContext.data.type;
     let radioTechnology = RIL.GECKO_RADIO_TECH.indexOf(radioTechType);
-    let authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(this.dataCallSettings["authtype"]);
+    let authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(this.apnSetting.authtype);
     // Use the default authType if the value in database is invalid.
     // For the case that user might not select the authentication type.
     if (authType == -1) {
       if (DEBUG) {
-        this.debug("Invalid authType " + this.dataCallSettings.authtype);
+        this.debug("Invalid authType " + this.apnSetting.authtype);
       }
       authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(RIL.GECKO_DATACALL_AUTH_DEFAULT);
     }
     this.radioInterface.setupDataCall(radioTechnology,
-                                       this.dataCallSettings.apn,
-                                       this.dataCallSettings.user,
-                                       this.dataCallSettings.passwd,
-                                       authType,
-                                       "IP");
+                                      this.apnSetting.apn,
+                                      this.apnSetting.user,
+                                      this.apnSetting.password,
+                                      authType,
+                                      "IP");
     this.connecting = true;
   },
 
   reset: function reset() {
     let apnRetryTimer;
     this.connecting = false;
     // We will retry the connection in increasing times
     // based on the function: time = A * numer_of_retries^2 + B
@@ -3636,17 +3625,18 @@ RILNetworkInterface.prototype = {
       this.connectedTypes.splice(index, 1);
     }
 
     if (this.connectedTypes.length) {
       return;
     }
 
     if (this.state == RIL.GECKO_NETWORK_STATE_DISCONNECTING ||
-        this.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED) {
+        this.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED ||
+        this.state == RIL.GECKO_NETWORK_STATE_UNKNOWN) {
       return;
     }
     let reason = RIL.DATACALL_DEACTIVATE_NO_REASON;
     if (DEBUG) this.debug("Going to disconnet data connection " + this.cid);
     this.radioInterface.deactivateDataCall(this.cid, reason);
   },
 
   // Entry method for timer events. Used to reconnect to a failed APN
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -1,18 +1,18 @@
 /* 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 nsIDOMMozIccInfo;
 interface nsIDOMMozMobileConnectionInfo;
-interface nsIDOMMozMobileICCInfo;
+interface nsIDOMMozSmsSegmentInfo;
 interface nsIMobileMessageCallback;
-interface nsIDOMMozSmsSegmentInfo;
 
 [scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)]
 interface nsIRILDataCallInfo : nsISupports
 {
   /**
    * Current data call state, one of the
    * nsINetworkInterface::NETWORK_STATE_* constants.
    */
@@ -54,30 +54,30 @@ interface nsIRILDataCallback : nsISuppor
 [scriptable, uuid(c0c5cb9f-6372-4b5a-b74c-baacc2da5e4f)]
 interface nsIVoicemailInfo : nsISupports
 {
   readonly attribute DOMString number;
 
   readonly attribute DOMString displayName;
 };
 
-[scriptable, uuid(2f1c8055-322e-490a-b1e1-4ccd5d546b3c)]
+[scriptable, uuid(95e1be50-c912-11e2-8b8b-0800200c9a66)]
 interface nsIRilContext : nsISupports
 {
   readonly attribute DOMString radioState;
 
   readonly attribute DOMString cardState;
 
   readonly attribute long retryCount;
 
   readonly attribute DOMString imsi;
 
   readonly attribute DOMString networkSelectionMode;
 
-  readonly attribute nsIDOMMozMobileICCInfo iccInfo;
+  readonly attribute nsIDOMMozIccInfo iccInfo;
 
   readonly attribute nsIDOMMozMobileConnectionInfo voice;
 
   readonly attribute nsIDOMMozMobileConnectionInfo data;
 };
 
 [scriptable, uuid(6dde3eaf-243d-4afa-abdb-95c94c2b1c7a)]
 interface nsIRadioInterface : nsISupports
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -832,35 +832,37 @@ GrallocDeprecatedTextureHostOGL::SetBuff
   // only done for hacky fix in gecko 23 for bug 862324.
   // Doing this in SwapTextures is not enough, as the crash could occur right after SetBuffer.
   RegisterDeprecatedTextureHostAtGrallocBufferActor(this, *mBuffer);
 }
 
 LayerRenderState
 GrallocDeprecatedTextureHostOGL::GetRenderState()
 {
-  if (mBuffer && IsSurfaceDescriptorValid(*mBuffer)) {
+  if (mGraphicBuffer.get()) {
 
     uint32_t flags = mFlags & NeedsYFlip ? LAYER_RENDER_STATE_Y_FLIPPED : 0;
 
     /*
      * The 32 bit format of gralloc buffer is created as RGBA8888 or RGBX888 by default.
      * For software rendering (non-GL rendering), the content is drawn with BGRA
      * or BGRX. Therefore, we need to pass the RBSwapped flag for HW composer to swap format.
      *
      * For GL rendering content, the content format is RGBA or RGBX which is the same as
      * the pixel format of gralloc buffer and no need for the RBSwapped flag.
      */
 
     if (mIsRBSwapped) {
       flags |= LAYER_RENDER_STATE_FORMAT_RB_SWAP;
     }
 
+    nsIntSize bufferSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight());
+
     return LayerRenderState(mGraphicBuffer.get(),
-                            mBuffer->get_SurfaceDescriptorGralloc().size(),
+                            bufferSize,
                             flags);
   }
 
   return LayerRenderState();
 }
 #endif // MOZ_WIDGET_GONK
 
 already_AddRefed<gfxImageSurface>
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -729,17 +729,16 @@ AdjustSystemClock(int64_t aDeltaMillisec
   ScopedClose autoClose(fd);
   if (fd < 0) {
     HAL_LOG(("Failed to open /dev/alarm: %s", strerror(errno)));
     return;
   }
 
   if (ioctl(fd, ANDROID_ALARM_SET_RTC, &now) < 0) {
     HAL_LOG(("ANDROID_ALARM_SET_RTC failed: %s", strerror(errno)));
-    return;
   }
 
   hal::NotifySystemClockChange(aDeltaMilliseconds);
 }
 
 static int32_t
 GetTimezoneOffset()
 {
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -2844,26 +2844,26 @@ function fetch(aURL, aOptions={ loadFrom
 
   switch (scheme) {
     case "file":
     case "chrome":
     case "resource":
       try {
         NetUtil.asyncFetch(url, function onFetch(aStream, aStatus) {
           if (!Components.isSuccessCode(aStatus)) {
-            deferred.reject("Request failed: " + url);
+            deferred.reject(new Error("Request failed: " + url));
             return;
           }
 
           let source = NetUtil.readInputStreamToString(aStream, aStream.available());
           deferred.resolve(source);
           aStream.close();
         });
       } catch (ex) {
-        deferred.reject("Request failed: " + url);
+        deferred.reject(new Error("Request failed: " + url));
       }
       break;
 
     default:
       let channel;
       try {
         channel = Services.io.newChannel(url, null, null);
       } catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
@@ -2871,25 +2871,25 @@ function fetch(aURL, aOptions={ loadFrom
         // newChannel won't be able to handle it.
         url = "file:///" + url;
         channel = Services.io.newChannel(url, null, null);
       }
       let chunks = [];
       let streamListener = {
         onStartRequest: function(aRequest, aContext, aStatusCode) {
           if (!Components.isSuccessCode(aStatusCode)) {
-            deferred.reject("Request failed: " + url);
+            deferred.reject(new Error("Request failed: " + url));
           }
         },
         onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
           chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
         },
         onStopRequest: function(aRequest, aContext, aStatusCode) {
           if (!Components.isSuccessCode(aStatusCode)) {
-            deferred.reject("Request failed: " + url);
+            deferred.reject(new Error("Request failed: " + url));
             return;
           }
 
           charset = channel.contentCharset;
           deferred.resolve(chunks.join(""));
         }
       };
 
@@ -2929,12 +2929,13 @@ function convertToUnicode(aString, aChar
  * Report the given error in the error console and to stdout.
  *
  * @param Error aError
  *        The error object you wish to report.
  * @param String aPrefix
  *        An optional prefix for the reported error message.
  */
 function reportError(aError, aPrefix="") {
+  dbg_assert(aError instanceof Error, "Must pass Error objects to reportError");
   let msg = aPrefix + aError.message + ":\n" + aError.stack;
   Cu.reportError(msg);
   dumpn(msg);
 }
--- a/toolkit/devtools/server/main.js
+++ b/toolkit/devtools/server/main.js
@@ -19,18 +19,17 @@ const DBG_STRINGS_URI = "chrome://global
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 addDebuggerToGlobal(this);
 
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
-const { defer, resolve, reject, all } = promise;
+loadSubScript.call(this, "resource://gre/modules/commonjs/sdk/core/promise.js");
 
 Cu.import("resource://gre/modules/devtools/SourceMap.jsm");
 
 loadSubScript.call(this, "resource://gre/modules/devtools/DevToolsUtils.js");
 
 function dumpn(str) {
   if (wantLogging) {
     dump("DBG-SERVER: " + str + "\n");
@@ -193,21 +192,21 @@ var DebuggerServer = {
     this._allowConnection = aAllowConnectionCallback ?
                             aAllowConnectionCallback :
                             this._defaultAllowConnection;
   },
 
   get initialized() this._initialized,
 
   /**
-   * Performs cleanup tasks before shutting down the debugger server, if no
-   * connections are currently open. Such tasks include clearing any actor
-   * constructors added at runtime. This method should be called whenever a
-   * debugger server is no longer useful, to avoid memory leaks. After this
-   * method returns, the debugger server must be initialized again before use.
+   * Performs cleanup tasks before shutting down the debugger server. Such tasks
+   * include clearing any actor constructors added at runtime. This method
+   * should be called whenever a debugger server is no longer useful, to avoid
+   * memory leaks. After this method returns, the debugger server must be
+   * initialized again before use.
    */
   destroy: function DS_destroy() {
     if (!this._initialized) {
       return;
     }
 
     for (let connID of Object.getOwnPropertyNames(this._connections)) {
       this._connections[connID].close();