Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 18 Oct 2013 15:40:08 -0400
changeset 166151 5861a2de43d355d16b1581466d689667ff29027e
parent 166150 3b459fed3fc63cad9ecce02a93e5697aad0a7739 (current diff)
parent 166121 565c8f1ba90bc285c2f2c80f2ed6193461920a56 (diff)
child 166152 fc7cc3c1dccfb9892e14b5639ccb533aba270156
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.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.
browser/components/certerror/test/moz.build
browser/components/dirprovider/tests/moz.build
browser/components/downloads/test/browser/moz.build
browser/components/downloads/test/moz.build
browser/components/feeds/test/chrome/Makefile.in
browser/components/feeds/test/chrome/moz.build
browser/components/feeds/test/moz.build
browser/components/migration/tests/moz.build
browser/components/places/tests/browser/Makefile.in
browser/components/places/tests/browser/moz.build
browser/components/places/tests/chrome/moz.build
browser/components/places/tests/moz.build
browser/components/preferences/in-content/tests/moz.build
browser/components/preferences/tests/moz.build
browser/components/privatebrowsing/test/browser/moz.build
browser/components/privatebrowsing/test/moz.build
browser/components/safebrowsing/content/test/moz.build
browser/components/safebrowsing/moz.build
browser/components/search/test/moz.build
browser/components/sessionstore/test/Makefile.in
browser/components/sessionstore/test/moz.build
browser/components/shell/test/moz.build
browser/components/tabview/Makefile.in
browser/components/tabview/test/moz.build
browser/components/test/moz.build
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1254,33 +1254,72 @@ window.addEventListener('ContentStart', 
       channel: aData
     });
     shell.visibleNormalAudioActive = (aData == 'normal');
 }, "visible-audio-channel-changed", false);
 })();
 
 (function recordingStatusTracker() {
   let gRecordingActiveCount = 0;
+  let gRecordingActiveProcesses = {};
 
-  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+  let recordingHandler = function(aSubject, aTopic, aData) {
     let oldCount = gRecordingActiveCount;
-    if (aData == "starting") {
-      gRecordingActiveCount += 1;
-    } else if (aData == "shutdown") {
-      gRecordingActiveCount -= 1;
+
+    let processId = (!aSubject) ? 'main'
+                                : aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID');
+    if (processId && !gRecordingActiveProcesses.hasOwnProperty(processId)) {
+      gRecordingActiveProcesses[processId] = 0;
     }
 
-    // We need to track changes from 1 <-> 0
-    if (gRecordingActiveCount + oldCount == 1) {
+    let currentActive = gRecordingActiveProcesses[processId];
+    switch (aData) {
+      case 'starting':
+        gRecordingActiveCount++;
+        currentActive++;
+        break;
+      case 'shutdown':
+        // Bug 928206 will make shutdown be sent even if no starting.
+        if (currentActive > 0) {
+          gRecordingActiveCount--;
+          currentActive--;
+        }
+        break;
+      case 'content-shutdown':
+        gRecordingActiveCount -= currentActive;
+        currentActive = 0;
+        break;
+    }
+
+    if (currentActive > 0) {
+      gRecordingActiveProcesses[processId] = currentActive;
+    } else {
+      delete gRecordingActiveProcesses[processId];
+    }
+
+    // We need to track changes from N <-> 0
+    if ((oldCount === 0 && gRecordingActiveCount > 0) ||
+        (gRecordingActiveCount === 0 && oldCount > 0)) {
       shell.sendChromeEvent({
         type: 'recording-status',
-        active: (gRecordingActiveCount == 1)
+        active: (gRecordingActiveCount > 0)
       });
     }
-}, "recording-device-events", false);
+  };
+  Services.obs.addObserver(recordingHandler, 'recording-device-events', false);
+  Services.obs.addObserver(recordingHandler, 'recording-device-ipc-events', false);
+
+  Services.obs.addObserver(function(aSubject, aTopic, aData) {
+    // send additional recording events if content process is being killed
+    let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
+    let childId = aSubject.get('childID');
+    if (gRecordingActiveProcesses.hasOwnProperty(childId) >= 0) {
+      Services.obs.notifyObservers(aSubject, 'recording-device-ipc-events', 'content-shutdown');
+    }
+  }, 'ipc:content-shutdown', false);
 })();
 
 (function volumeStateTracker() {
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     shell.sendChromeEvent({
       type: 'volume-state-changed',
       active: (aData == 'Shared')
     });
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "154bb18c48ff06e41fb7ba24d8f72d520919646f", 
+    "revision": "723401621af1bfabb671ec53ac7cafd62b700cb9", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/components/certerror/moz.build
+++ b/browser/components/certerror/moz.build
@@ -1,7 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['test']
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/certerror/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/components/dirprovider/moz.build
+++ b/browser/components/dirprovider/moz.build
@@ -1,20 +1,21 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['tests']
-
 MODULE = 'browserdir'
 
 EXPORTS.mozilla.browser += [
     'DirectoryProvider.h',
 ]
 
 CPP_SOURCES += [
     'DirectoryProvider.cpp',
 ]
 
 LIBRARY_NAME = 'browserdir_s'
 
+XPCSHELL_TESTS_MANIFESTS += [
+    'tests/unit/xpcshell.ini',
+]
deleted file mode 100644
--- a/browser/components/dirprovider/tests/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-MODULE = 'test_browserdir'
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
--- a/browser/components/downloads/moz.build
+++ b/browser/components/downloads/moz.build
@@ -1,8 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 DIRS += ['src']
-TEST_DIRS += ['test']
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/unit/xpcshell.ini',
+]
+
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/downloads/test/browser/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
deleted file mode 100644
--- a/browser/components/downloads/test/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-DIRS += ['browser']
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
--- a/browser/components/feeds/moz.build
+++ b/browser/components/feeds/moz.build
@@ -1,8 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 DIRS += ['public', 'src']
-TEST_DIRS += ['test']
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/unit/xpcshell.ini',
+]
+
+MOCHITEST_CHROME_MANIFESTS += [
+    'test/chrome/chrome.ini',
+]
+
+MOCHITEST_MANIFESTS += [
+    'test/mochitest.ini'
+]
deleted file mode 100644
--- a/browser/components/feeds/test/chrome/Makefile.in
+++ /dev/null
@@ -1,10 +0,0 @@
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# sample_feed.atom was copied from toolkit/components/places/tests/chrome
-MOCHITEST_FILES	= \
-		sample_feed.atom \
-		$(NULL)
-
--- a/browser/components/feeds/test/chrome/chrome.ini
+++ b/browser/components/feeds/test/chrome/chrome.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
+support-files = sample_feed.atom
 
 [test_423060.xul]
 [test_bug368464.html]
 [test_bug408328.html]
 [test_maxSniffing.html]
deleted file mode 100644
--- a/browser/components/feeds/test/chrome/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
-
deleted file mode 100644
--- a/browser/components/feeds/test/moz.build
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-DIRS += ['chrome']
-
-MODULE = 'test_browser_feeds'
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
-
-MOCHITEST_MANIFESTS += ['mochitest.ini']
-
--- a/browser/components/migration/moz.build
+++ b/browser/components/migration/moz.build
@@ -1,8 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 DIRS += ['public', 'src']
-TEST_DIRS += ['tests']
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'tests/unit/xpcshell.ini',
+]
deleted file mode 100644
--- a/browser/components/migration/tests/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -16,20 +16,16 @@ PARALLEL_DIRS += [
     'search',
     'sessionstore',
     'shell',
     'sidebar',
     'tabview',
     'migration',
 ]
 
-if CONFIG['MOZ_SAFE_BROWSING']:
-    PARALLEL_DIRS += ['safebrowsing']
-
-TEST_DIRS += ['test']
 DIRS += ['build']
 
 XPIDL_SOURCES += [
     'nsIBrowserGlue.idl',
     'nsIBrowserHandler.idl',
 ]
 
 XPIDL_MODULE = 'browsercompsbase'
@@ -43,8 +39,15 @@ EXTRA_PP_COMPONENTS += [
     'nsBrowserContentHandler.js',
     'nsBrowserGlue.js',
 ]
 
 EXTRA_JS_MODULES += [
     'distribution.js',
 ]
 
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini'
+]
+
+if CONFIG['MOZ_SAFE_BROWSING']:
+    BROWSER_CHROME_MANIFESTS += ['safebrowsing/content/test/browser.ini']
+
--- a/browser/components/places/moz.build
+++ b/browser/components/places/moz.build
@@ -1,9 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 DIRS += ['src']
-TEST_DIRS += ['tests']
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'tests/unit/xpcshell.ini',
+]
 
+MOCHITEST_CHROME_MANIFESTS += [
+    'tests/chrome/chrome.ini'
+]
+
+BROWSER_CHROME_MANIFESTS += [
+    'tests/browser/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/places/tests/browser/Makefile.in
+++ /dev/null
@@ -1,38 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-MOCHITEST_BROWSER_FILES = \
-	head.js \
-	browser_0_library_left_pane_migration.js \
-	browser_library_left_pane_fixnames.js \
-	browser_425884.js \
-	browser_475045.js \
-	browser_423515.js \
-	browser_410196_paste_into_tags.js \
-	browser_sort_in_library.js \
-	browser_library_open_leak.js \
-	browser_library_panel_leak.js \
-	browser_library_search.js \
-	browser_history_sidebar_search.js \
-	browser_bookmarksProperties.js \
-	$(filter disabled-for-very-frequent-oranges--bug-551540, browser_forgetthissite_single.js) \
-	browser_library_left_pane_commands.js \
-	browser_drag_bookmarks_on_toolbar.js \
-	browser_library_middleclick.js \
-	browser_library_views_liveupdate.js \
-	browser_views_liveupdate.js \
-	$(filter temporarily-disabled-for-breaking-the-treeview--bug-658744, browser_sidebarpanels_click.js) \
-	sidebarpanels_click_test_page.html \
-	browser_library_infoBox.js \
-	browser_markPageAsFollowedLink.js \
-	framedPage.html \
-	frameLeft.html \
-	frameRight.html \
-	browser_toolbar_migration.js \
-	browser_library_batch_delete.js \
-	browser_555547.js \
-	browser_416459_cut.js \
-	browser_library_downloads.js \
-	browser_library_left_pane_select_hierarchy.js \
-	$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser.ini
@@ -0,0 +1,47 @@
+# 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/.
+
+[DEFAULT]
+support-files =
+  head.js
+  framedPage.html
+  frameLeft.html
+  frameRight.html
+  sidebarpanels_click_test_page.html
+
+[browser_0_library_left_pane_migration.js]
+[browser_library_left_pane_fixnames.js]
+[browser_425884.js]
+[browser_475045.js]
+[browser_423515.js]
+[browser_410196_paste_into_tags.js]
+[browser_sort_in_library.js]
+[browser_library_open_leak.js]
+[browser_library_panel_leak.js]
+[browser_library_search.js]
+[browser_history_sidebar_search.js]
+[browser_bookmarksProperties.js]
+
+[browser_forgetthissite_single.js]
+# disabled for very frequent oranges - bug 551540
+skip-if = true
+
+[browser_library_left_pane_commands.js]
+[browser_drag_bookmarks_on_toolbar.js]
+[browser_library_middleclick.js]
+[browser_library_views_liveupdate.js]
+[browser_views_liveupdate.js]
+
+[browser_sidebarpanels_click.js]
+# temporarily disabled for breaking the treeview - bug 658744
+skip-if = true
+
+[browser_library_infoBox.js]
+[browser_markPageAsFollowedLink.js]
+[browser_toolbar_migration.js]
+[browser_library_batch_delete.js]
+[browser_555547.js]
+[browser_416459_cut.js]
+[browser_library_downloads.js]
+[browser_library_left_pane_select_hierarchy.js]
deleted file mode 100644
--- a/browser/components/places/tests/browser/moz.build
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
deleted file mode 100644
--- a/browser/components/places/tests/chrome/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
-
deleted file mode 100644
--- a/browser/components/places/tests/moz.build
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-DIRS += ['browser', 'chrome']
-
-MODULE = 'test_browser_places'
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
--- a/browser/components/preferences/in-content/moz.build
+++ b/browser/components/preferences/in-content/moz.build
@@ -1,7 +1,6 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['tests']
deleted file mode 100644
--- a/browser/components/preferences/in-content/tests/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/components/preferences/moz.build
+++ b/browser/components/preferences/moz.build
@@ -1,8 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 PARALLEL_DIRS += ['in-content']
-TEST_DIRS += ['tests']
+
+BROWSER_CHROME_MANIFESTS += [
+    'in-content/tests/browser.ini',
+    'tests/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/preferences/tests/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/components/privatebrowsing/moz.build
+++ b/browser/components/privatebrowsing/moz.build
@@ -1,10 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['test']
-
-MODULE = 'privatebrowsing'
-
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/browser/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-MODULE = 'test_privatebrowsing'
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-DIRS += ['browser']
-
-MODULE = 'test_privatebrowsing'
-
deleted file mode 100644
--- a/browser/components/safebrowsing/content/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
deleted file mode 100644
--- a/browser/components/safebrowsing/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-TEST_DIRS += ['content/test']
-
--- a/browser/components/search/moz.build
+++ b/browser/components/search/moz.build
@@ -1,7 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['test']
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/search/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -1,16 +1,22 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 DIRS += ['src']
-TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'nsISessionStartup.idl',
     'nsISessionStore.idl',
 ]
 
 MODULE = 'sessionstore'
 
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/unit/xpcshell.ini',
+]
+
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini',
+]
--- a/browser/components/sessionstore/src/_SessionFile.jsm
+++ b/browser/components/sessionstore/src/_SessionFile.jsm
@@ -222,41 +222,44 @@ let SessionFileInternal = {
     });
   },
 
   write: function (aData) {
     if (this._isClosed) {
       return Promise.reject(new Error("_SessionFile is closed"));
     }
     let refObj = {};
+
+    let isFinalWrite = false;
+    if (Services.startup.shuttingDown) {
+      // If shutdown has started, we will want to stop receiving
+      // write instructions.
+      isFinalWrite = this._isClosed = true;
+    }
+
     return this._latestWrite = TaskUtils.spawn(function task() {
       TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
 
       try {
         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 record how long the write took
         let msg = yield promise;
         this._recordTelemetry(msg.telemetry);
       } catch (ex) {
         TelemetryStopwatch.cancel("FX_SESSION_RESTORE_WRITE_FILE_LONGEST_OP_MS", refObj);
         Cu.reportError("Could not write session state file " + this.path
                        + ": " + ex);
       }
-      // At this stage, we are done writing. If shutdown has started,
-      // we will want to stop receiving write instructions.
-      if (Services.startup.shuttingDown) {
-        this._isClosed = true;
+
+      if (isFinalWrite) {
+        Services.obs.notifyObservers(null, "sessionstore-final-state-write-complete", "");
       }
-      // In rare cases, we may already have other writes pending,
-      // which we need to flush before shutdown proceeds. AsyncShutdown
-      // uses _latestWrite to determine what needs to be flushed during
-      // shutdown.
     }.bind(this));
   },
 
   writeLoadStateOnceAfterStartup: function (aLoadState) {
     SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
       this._recordTelemetry(msg.telemetry);
       return msg;
     }, Cu.reportError);
deleted file mode 100644
--- a/browser/components/sessionstore/test/Makefile.in
+++ /dev/null
@@ -1,165 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# browser_506482.js is disabled because of frequent failures (bug 538672)
-# browser_526613.js is disabled because of frequent failures (bug 534489)
-# browser_589246.js is disabled for leaking browser windows (bug 752467)
-# browser_580512.js is disabled for leaking browser windows (bug 752467)
-
-MOCHITEST_BROWSER_FILES = \
-	head.js \
-	browser_attributes.js \
-	browser_capabilities.js \
-	browser_dying_cache.js \
-	browser_form_restore_events.js \
-	browser_form_restore_events_sample.html \
-	browser_formdata_format.js \
-	browser_formdata_format_sample.html \
-	browser_input.js \
-	browser_input_sample.html \
-	browser_pageshow.js \
-	browser_sessionStorage.js \
-	browser_tabStateCache.js \
-	browser_upgrade_backup.js \
-	browser_windowRestore_perwindowpb.js \
-	browser_248970_b_perwindowpb.js \
-	browser_248970_b_sample.html \
-	browser_339445.js \
-	browser_339445_sample.html \
-	browser_345898.js \
-	browser_346337.js \
-	browser_346337_sample.html \
-	browser_350525.js \
-	browser_354894_perwindowpb.js \
-	browser_367052.js \
-	browser_393716.js \
-	browser_394759_basic.js \
-	browser_394759_behavior.js \
-	browser_394759_perwindowpb.js \
-	browser_394759_purge.js \
-	browser_408470.js \
-	browser_408470_sample.html \
-	browser_423132.js \
-	browser_423132_sample.html \
-	browser_447951.js \
-	browser_447951_sample.html \
-	browser_448741.js \
-	browser_454908.js \
-	browser_454908_sample.html \
-	browser_456342.js \
-	browser_456342_sample.xhtml \
-	browser_461634.js \
-	browser_463205.js \
-	browser_463205_helper.html \
-	browser_463205_sample.html \
-	browser_463206.js \
-	browser_463206_sample.html \
-	browser_464199.js \
-	browser_465215.js \
-	browser_465223.js \
-	browser_466937.js \
-	browser_466937_sample.html \
-	browser_467409-backslashplosion.js \
-	browser_477657.js \
-	browser_480148.js \
-	browser_480893.js \
-	browser_483330.js \
-	browser_485482.js \
-	browser_485482_sample.html \
-	browser_485563.js \
-	browser_490040.js \
-	browser_491168.js \
-	browser_491577.js \
-	browser_495495.js \
-	browser_500328.js \
-	browser_514751.js \
-	browser_522375.js \
-	browser_522545.js \
-	browser_524745.js \
-	browser_528776.js \
-	browser_579868.js \
-	browser_579879.js \
-	browser_581593.js \
-	browser_581937.js \
-	browser_586147.js \
-	browser_586068-apptabs.js \
-	browser_586068-apptabs_ondemand.js \
-	browser_586068-browser_state_interrupted.js \
-	browser_586068-cascade.js \
-	browser_586068-multi_window.js \
-	browser_586068-reload.js \
-	browser_586068-select.js \
-	browser_586068-window_state.js \
-	browser_586068-window_state_override.js \
-	browser_588426.js \
-	browser_590268.js \
-	browser_590563.js \
-	browser_595601-restore_hidden.js \
-	browser_597315.js \
-	browser_597315_index.html \
-	browser_597315_a.html \
-	browser_597315_b.html \
-	browser_597315_c.html \
-	browser_597315_c1.html \
-	browser_597315_c2.html \
-	browser_599909.js \
-	browser_600545.js \
-	browser_601955.js \
-	browser_607016.js \
-	browser_615394-SSWindowState_events.js \
-	browser_618151.js \
-	browser_623779.js \
-	browser_624727.js \
-	browser_625257.js \
-	browser_628270.js \
-	browser_635418.js \
-	browser_636279.js \
-	browser_637020.js \
-	browser_637020_slow.sjs \
-	browser_644409-scratchpads.js \
-	browser_645428.js \
-	browser_659591.js \
-	browser_662743.js \
-	browser_662743_sample.html \
-	browser_662812.js \
-	browser_665702-state_session.js \
-	browser_682507.js \
-	browser_687710.js \
-	browser_687710_2.js \
-	browser_694378.js \
-	browser_701377.js \
-	browser_705597.js \
-	browser_707862.js \
-	browser_739531.js \
-	browser_739531_sample.html \
-	browser_739805.js \
-	browser_819510_perwindowpb.js \
-	browser_833286_atomic_backup.js \
-	browser_916390_form_data_loss.js \
-	browser_916390_sample.html \
-	$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html) \
-	$(filter disabled-for-intermittent-failures--bug-766044, browser_459906_sample.html) \
-	$(filter disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html) \
-	$(NULL)
-
-# Disabled on Windows for frequent intermittent failures
-ifneq ($(OS_ARCH), WINNT)
-MOCHITEST_FILES += \
-	browser_464620_a.js \
-	browser_464620_a.html \
-	browser_464620_b.js \
-	browser_464620_b.html \
-	browser_464620_xd.html \
-	$(NULL)
-else
-$(filter disabled-for-intermittent-failures-on-windows--bug-552424, browser_464620_a.js)
-$(filter disabled-for-intermittent-failures-on-windows--bug-552424, browser_464620_b.js)
-endif
-
-ifneq ($(OS_ARCH),Darwin)
-MOCHITEST_BROWSER_FILES += \
-	browser_597071.js \
-	browser_625016.js \
-	$(NULL)
-endif
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser.ini
@@ -0,0 +1,164 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# browser_506482.js is disabled because of frequent failures (bug 538672)
+# browser_526613.js is disabled because of frequent failures (bug 534489)
+# browser_589246.js is disabled for leaking browser windows (bug 752467)
+# browser_580512.js is disabled for leaking browser windows (bug 752467)
+
+[DEFAULT]
+support-files =
+  head.js
+  browser_form_restore_events_sample.html
+  browser_formdata_format_sample.html
+  browser_input_sample.html
+  browser_248970_b_sample.html
+  browser_339445_sample.html
+  browser_346337_sample.html
+  browser_408470_sample.html
+  browser_423132_sample.html
+  browser_447951_sample.html
+  browser_454908_sample.html
+  browser_456342_sample.xhtml
+  browser_463205_helper.html
+  browser_463205_sample.html
+  browser_463206_sample.html
+  browser_466937_sample.html
+  browser_485482_sample.html
+  browser_597315_index.html
+  browser_597315_a.html
+  browser_597315_b.html
+  browser_597315_c.html
+  browser_597315_c1.html
+  browser_597315_c2.html
+  browser_662743_sample.html
+  browser_739531_sample.html
+  browser_916390_sample.html
+#NB: the following are disabled on Windows:
+  browser_464620_a.html
+  browser_464620_b.html 
+  browser_464620_xd.html
+
+
+#disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html
+#disabled-for-intermittent-failures--bug-766044, browser_459906_sample.html
+#disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html
+
+[browser_attributes.js]
+[browser_capabilities.js]
+[browser_dying_cache.js]
+[browser_form_restore_events.js]
+[browser_formdata_format.js]
+[browser_input.js]
+[browser_pageshow.js]
+[browser_sessionStorage.js]
+[browser_tabStateCache.js]
+[browser_upgrade_backup.js]
+[browser_windowRestore_perwindowpb.js]
+[browser_248970_b_perwindowpb.js]
+[browser_339445.js]
+[browser_345898.js]
+[browser_346337.js]
+[browser_350525.js]
+[browser_354894_perwindowpb.js]
+[browser_367052.js]
+[browser_393716.js]
+[browser_394759_basic.js]
+[browser_394759_behavior.js]
+[browser_394759_perwindowpb.js]
+[browser_394759_purge.js]
+[browser_408470.js]
+[browser_423132.js]
+[browser_447951.js]
+[browser_448741.js]
+[browser_454908.js]
+[browser_456342.js]
+[browser_461634.js]
+[browser_463205.js]
+[browser_463206.js]
+[browser_464199.js]
+[browser_465215.js]
+[browser_465223.js]
+[browser_466937.js]
+[browser_467409-backslashplosion.js]
+[browser_477657.js]
+[browser_480148.js]
+[browser_480893.js]
+[browser_483330.js]
+[browser_485482.js]
+[browser_485563.js]
+[browser_490040.js]
+[browser_491168.js]
+[browser_491577.js]
+[browser_495495.js]
+[browser_500328.js]
+[browser_514751.js]
+[browser_522375.js]
+[browser_522545.js]
+[browser_524745.js]
+[browser_528776.js]
+[browser_579868.js]
+[browser_579879.js]
+[browser_581593.js]
+[browser_581937.js]
+[browser_586147.js]
+[browser_586068-apptabs.js]
+[browser_586068-apptabs_ondemand.js]
+[browser_586068-browser_state_interrupted.js]
+[browser_586068-cascade.js]
+[browser_586068-multi_window.js]
+[browser_586068-reload.js]
+[browser_586068-select.js]
+[browser_586068-window_state.js]
+[browser_586068-window_state_override.js]
+[browser_588426.js]
+[browser_590268.js]
+[browser_590563.js]
+[browser_595601-restore_hidden.js]
+[browser_597315.js]
+[browser_599909.js]
+[browser_600545.js]
+[browser_601955.js]
+[browser_607016.js]
+[browser_615394-SSWindowState_events.js]
+[browser_618151.js]
+[browser_623779.js]
+[browser_624727.js]
+[browser_625257.js]
+[browser_628270.js]
+[browser_635418.js]
+[browser_636279.js]
+[browser_637020.js]
+[browser_637020_slow.sjs]
+[browser_644409-scratchpads.js]
+[browser_645428.js]
+[browser_659591.js]
+[browser_662743.js]
+[browser_662812.js]
+[browser_665702-state_session.js]
+[browser_682507.js]
+[browser_687710.js]
+[browser_687710_2.js]
+[browser_694378.js]
+[browser_701377.js]
+[browser_705597.js]
+[browser_707862.js]
+[browser_739531.js]
+[browser_739805.js]
+[browser_819510_perwindowpb.js]
+[browser_833286_atomic_backup.js]
+[browser_916390_form_data_loss.js]
+
+# Disabled on Windows for frequent intermittent failures
+[browser_464620_a.js]
+skip-if = os == "win"
+[browser_464620_b.js]
+skip-if = os == "win"
+
+# Disabled on OS X:
+[browser_597071.js]
+skip-if = os == "mac"
+[browser_625016.js]
+skip-if = os == "mac"
+
deleted file mode 100644
--- a/browser/components/sessionstore/test/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
--- a/browser/components/shell/moz.build
+++ b/browser/components/shell/moz.build
@@ -1,8 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
 DIRS += ['public', 'src']
-TEST_DIRS += ['test']
+
+XPCSHELL_TESTS_MANIFESTS += [
+    'test/unit/xpcshell.ini'
+]
+
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/shell/test/moz.build
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-MODULE = 'test_browser_shell'
-
-XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
deleted file mode 100644
--- a/browser/components/tabview/Makefile.in
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-include $(topsrcdir)/config/rules.mk
-
-libs::
-	$(NSINSTALL) $(srcdir)/modules/* $(FINAL_TARGET)/modules/tabview
--- a/browser/components/tabview/moz.build
+++ b/browser/components/tabview/moz.build
@@ -1,7 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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/.
 
-TEST_DIRS += ['test']
+EXTRA_JS_MODULES = ['modules/utils.jsm']
+JS_MODULES_PATH  = 'modules/tabview'
+
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser.ini',
+]
deleted file mode 100644
--- a/browser/components/tabview/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
deleted file mode 100644
--- a/browser/components/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/devtools/app-manager/webapps-store.js
+++ b/browser/devtools/app-manager/webapps-store.js
@@ -253,16 +253,20 @@ WebappsStore.prototype = {
       }
 
       request.type = "getIconAsDataURL";
       client.request(request, (res) => {
         if (res.url) {
           proxifiedApp.iconURL = res.url;
         }
       });
+
+      // This app may have been running while being installed, so check the list
+      // of running apps again to get the right answer.
+      this._getRunningApps();
     });
   },
 
   _onAppUninstall: function(manifest) {
     this.object.all = this.object.all.filter((app) => {
       return (app.manifestURL != manifest);
     });
   },
--- a/browser/devtools/framework/connect/connect.xhtml
+++ b/browser/devtools/framework/connect/connect.xhtml
@@ -40,11 +40,11 @@
       <p>&availableTabs;</p>
       <ul class="actors" id="tabActors"></ul>
       <p>&availableProcesses;</p>
       <ul class="actors" id="globalActors"></ul>
     </section>
     <section id="connecting">
       <p><img src="chrome://browser/skin/tabbrowser/loading.png"></img> &connecting;</p>
     </section>
-    <footer>&help2;</footer>
+    <footer>&remoteHelp;<a target='_' href='https://developer.mozilla.org/docs/Tools/Remote_Debugging'>&remoteDocumentation;</a>&remoteHelpSuffix;</footer>
   </body>
 </html>
--- a/browser/devtools/layoutview/test/browser.ini
+++ b/browser/devtools/layoutview/test/browser.ini
@@ -1,3 +1,4 @@
 [DEFAULT]
 
 [browser_layoutview.js]
+skip-if = true
--- a/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm
+++ b/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm
@@ -131,22 +131,21 @@ BreadcrumbsWidget.prototype = {
         this._selectedItem = node;
       } else {
         node.removeAttribute("checked");
       }
     }
 
     // Repeated calls to ensureElementIsVisible would interfere with each other
     // and may sometimes result in incorrect scroll positions.
-    this.window.clearTimeout(this._ensureVisibleTimeout);
-    this._ensureVisibleTimeout = this.window.setTimeout(() => {
+    setNamedTimeout("breadcrumb-select", ENSURE_SELECTION_VISIBLE_DELAY, () => {
       if (this._selectedItem) {
         this._list.ensureElementIsVisible(this._selectedItem);
       }
-    }, ENSURE_SELECTION_VISIBLE_DELAY);
+    });
   },
 
   /**
    * The underflow and overflow listener for the arrowscrollbox container.
    */
   _onUnderflow: function({ target }) {
     if (target != this._list) {
       return;
@@ -167,18 +166,17 @@ BreadcrumbsWidget.prototype = {
     target._scrollButtonDown.collapsed = false;
     target.setAttribute("overflows", "");
   },
 
   window: null,
   document: null,
   _parent: null,
   _list: null,
-  _selectedItem: null,
-  _ensureVisibleTimeout: null
+  _selectedItem: null
 };
 
 /**
  * A Breadcrumb constructor for the BreadcrumbsWidget.
  *
  * @param BreadcrumbsWidget aWidget
  *        The widget to contain this breadcrumb.
  * @param string | nsIDOMNode aContents
--- a/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
@@ -14,9 +14,16 @@
 <!ENTITY connect    "Connect">
 <!ENTITY connecting "Connecting…">
 <!ENTITY availableTabs "Available remote tabs:">
 <!ENTITY availableProcesses "Available remote processes:">
 <!ENTITY connectionError "Error:">
 <!ENTITY errorTimeout "Error: connection timeout.">
 <!ENTITY errorRefused "Error: connection refused.">
 <!ENTITY errorUnexpected "Unexpected error.">
-<!ENTITY help2 "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS, for example). Make sure that you have turned on the 'Remote debugging' option in the remote device. See the <a target='_' href='https://developer.mozilla.org/docs/Tools/Remote_Debugging'>documentation</a> for more.">
+
+<!-- LOCALIZATION NOTE (remoteHelp, remoteDocumentation, remoteHelpSuffix):
+these strings will be concatenated in a single label, remoteDocumentation will
+be used as text for a link to MDN. -->
+<!ENTITY remoteHelp "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS, for example). Make sure that you have turned on the 'Remote debugging' option in the remote device. For more, see the">
+<!ENTITY remoteDocumentation "documentation">
+<!ENTITY remoteHelpSuffix ".">
+
--- a/browser/metro/base/content/startui/TopSitesView.js
+++ b/browser/metro/base/content/startui/TopSitesView.js
@@ -162,19 +162,22 @@ TopSitesView.prototype = Util.extend(Obj
       throw new Error("Invalid Site object passed to TopSitesView updateTile");
     }
     this._updateFavicon(aTileNode, Util.makeURI(aSite.url));
 
     Task.spawn(function() {
       let filepath = PageThumbsStorage.getFilePathForURL(aSite.url);
       if (yield OS.File.exists(filepath)) {
         aSite.backgroundImage = 'url("'+PageThumbs.getThumbnailURL(aSite.url)+'")';
-        if ('backgroundImage' in aTileNode) {
+        // use the setter when available to update the backgroundImage value
+        if ('backgroundImage' in aTileNode &&
+            aTileNode.backgroundImage != aSite.backgroundImage) {
           aTileNode.backgroundImage = aSite.backgroundImage;
         } else {
+          // just update the attribute for when the node gets the binding applied
           aTileNode.setAttribute("customImage", aSite.backgroundImage);
         }
       }
     });
 
     aSite.applyToTileNode(aTileNode);
     if (aTileNode.refresh) {
       aTileNode.refresh();
--- a/browser/metro/modules/View.jsm
+++ b/browser/metro/modules/View.jsm
@@ -82,18 +82,18 @@ View.prototype = {
       aItem.style.color = foreground; //color text
       aItem.setAttribute("customColor", background);
       let matteColor =  0xffffff; // white
       let alpha = 0.04; // the tint weight
       let [,r,g,b] = background.match(/rgb\((\d+),(\d+),(\d+)/);
       // get the rgb value that represents this color at given opacity over a white matte
       let tintColor = ColorUtils.addRgbColors(matteColor, ColorUtils.createDecimalColorWord(r,g,b,alpha));
       aItem.setAttribute("tintColor", ColorUtils.convertDecimalToRgbColor(tintColor));
-
-      if (aItem.refresh) {
-        aItem.refresh();
+      // when bound, use the setter to propogate the color change through the tile
+      if ('color' in aItem) {
+        aItem.color = background;
       }
     };
     let failureAction = function() {};
     ColorUtils.getForegroundAndBackgroundIconColors(xpFaviconURI, successAction, failureAction);
   }
 
 };
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -0,0 +1,287 @@
+#include "nsGlobalWindow.h"
+#include "nsDOMWindowUtils.h"
+#include "nsIDOMClientRect.h"
+#include "nsIDocShell.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "gfxImageSurface.h"
+#include "gfxContext.h"
+#include "ImageContainer.h"
+#include "Layers.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDOMDocument.h"
+#include "nsITabSource.h"
+#include "MediaEngineTabVideoSource.h"
+#include "VideoUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIPrefService.h"
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS1(MediaEngineTabVideoSource, MediaEngineVideoSource)
+
+MediaEngineTabVideoSource::MediaEngineTabVideoSource()
+  : mName(NS_LITERAL_STRING("share tab")), mUuid(NS_LITERAL_STRING("uuid")),
+  mMonitor("MediaEngineTabVideoSource")
+{
+}
+
+nsresult
+MediaEngineTabVideoSource::StartRunnable::Run()
+{
+  mVideoSource->Draw();
+  nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow);
+  if (privateDOMWindow) {
+    privateDOMWindow->GetChromeEventHandler()->AddEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false);
+  } else {
+    mVideoSource->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    mVideoSource->mTimer->InitWithCallback(mVideoSource, mVideoSource->mTimePerFrame, nsITimer:: TYPE_REPEATING_SLACK);
+  }
+  return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::StopRunnable::Run()
+{
+  nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow);
+  if (privateDOMWindow && mVideoSource && privateDOMWindow->GetChromeEventHandler()) {
+    privateDOMWindow->GetChromeEventHandler()->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false);
+  }
+
+  if (mVideoSource->mTimer) {
+    mVideoSource->mTimer->Cancel();
+    mVideoSource->mTimer = NULL;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaEngineTabVideoSource::HandleEvent(nsIDOMEvent *event) {
+  Draw();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaEngineTabVideoSource::Notify(nsITimer*) {
+  Draw();
+  return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::InitRunnable::Run()
+{
+  nsresult rv;
+  nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
+  if (!branch)
+    return NS_OK;
+  branch->GetIntPref("media.tabstreaming.width", &mVideoSource->mBufW);
+  branch->GetIntPref("media.tabstreaming.height", &mVideoSource->mBufH);
+  branch->GetIntPref("media.tabstreaming.time_per_frame", &mVideoSource->mTimePerFrame);
+  mVideoSource->mData = (unsigned char*)malloc(mVideoSource->mBufW * mVideoSource->mBufH * 4);
+
+  nsCOMPtr<nsITabSource> tabSource = do_GetService(NS_TABSOURCESERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDOMWindow> win;
+  rv = tabSource->GetTabToStream(getter_AddRefs(win));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!win)
+    return NS_OK;
+
+  mVideoSource->mWindow = win;
+  nsCOMPtr<nsIRunnable> start(new StartRunnable(mVideoSource));
+  start->Run();
+  return NS_OK;
+}
+
+void
+MediaEngineTabVideoSource::GetName(nsAString_internal& aName)
+{
+  aName.Assign(mName);
+
+}
+
+void
+MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid)
+{
+  aUuid.Assign(mUuid);
+}
+
+nsresult
+MediaEngineTabVideoSource::Allocate(const mozilla::MediaEnginePrefs&)
+{
+  return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::Deallocate()
+{
+  return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::Start(mozilla::SourceMediaStream* aStream, mozilla::TrackID aID)
+{
+  nsCOMPtr<nsIRunnable> runnable;
+  if (!mWindow)
+    runnable = new InitRunnable(this);
+  else
+    runnable = new StartRunnable(this);
+  NS_DispatchToMainThread(runnable);
+  aStream->AddTrack(aID, USECS_PER_S, 0, new VideoSegment());
+  aStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
+
+  return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::Snapshot(uint32_t, nsIDOMFile**)
+{
+  return NS_OK;
+}
+
+void
+MediaEngineTabVideoSource::
+NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID, mozilla::StreamTime aDesiredTime, mozilla::TrackTicks& aLastEndTime)
+{
+  VideoSegment segment;
+  MonitorAutoLock mon(mMonitor);
+
+  // Note: we're not giving up mImage here
+  nsRefPtr<layers::CairoImage> image = mImage;
+  TrackTicks target = TimeToTicksRoundUp(USECS_PER_S, aDesiredTime);
+  TrackTicks delta = target - aLastEndTime;
+  if (delta > 0) {
+    // NULL images are allowed
+    if (image) {
+      gfxIntSize size = image->GetSize();
+      segment.AppendFrame(image.forget(), delta, size);
+    } else {
+      segment.AppendFrame(nullptr, delta, gfxIntSize(0,0));
+    }
+    // This can fail if either a) we haven't added the track yet, or b)
+    // we've removed or finished the track.
+    if (aSource->AppendToTrack(aID, &(segment))) {
+      aLastEndTime = target;
+    }
+  }
+}
+
+void
+MediaEngineTabVideoSource::Draw() {
+
+  nsIntSize size(mBufW, mBufH);
+
+  nsresult rv;
+  float scale = 1.0;
+
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mWindow);
+
+  if (!win) {
+    return;
+  }
+
+  // take a screenshot, as wide as possible, proportional to the destination size
+  nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(win);
+  if (!utils) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMClientRect> rect;
+  rv = utils->GetRootBounds(getter_AddRefs(rect));
+  NS_ENSURE_SUCCESS_VOID(rv);
+  if (!rect) {
+    return;
+  }
+
+  float left, top, width, height;
+  rect->GetLeft(&left);
+  rect->GetTop(&top);
+  rect->GetWidth(&width);
+  rect->GetHeight(&height);
+
+  if (width == 0 || height == 0) {
+    return;
+  }
+
+  int32_t srcX = left;
+  int32_t srcY = top;
+  int32_t srcW;
+  int32_t srcH;
+
+  float aspectRatio = ((float) size.width) / size.height;
+  if (width / aspectRatio < height) {
+    srcW = width;
+    srcH = width / aspectRatio;
+  } else {
+    srcW = height * aspectRatio;
+    srcH = height;
+  }
+
+  nsRefPtr<nsPresContext> presContext;
+  nsIDocShell* docshell = win->GetDocShell();
+  if (docshell) {
+    docshell->GetPresContext(getter_AddRefs(presContext));
+  }
+  if (!presContext) {
+    return;
+  }
+  nscolor bgColor = NS_RGB(255, 255, 255);
+  nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
+  uint32_t renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING |
+                             nsIPresShell::RENDER_DOCUMENT_RELATIVE);
+  nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale),
+           nsPresContext::CSSPixelsToAppUnits(srcY / scale),
+           nsPresContext::CSSPixelsToAppUnits(srcW / scale),
+           nsPresContext::CSSPixelsToAppUnits(srcH / scale));
+
+  uint32_t stride = size.width * 4;
+
+  nsRefPtr<layers::ImageContainer> container = layers::LayerManager::CreateImageContainer();
+  nsRefPtr<gfxASurface> surf;
+  surf = new gfxImageSurface(static_cast<unsigned char*>(mData), size,
+                             stride, gfxImageFormatRGB24);
+  if (surf->CairoStatus() != 0) {
+    return;
+  }
+  nsRefPtr<gfxContext> context = new gfxContext(surf);
+  gfxPoint pt(0, 0);
+  context->Translate(pt);
+  context->Scale(scale * size.width / srcW, scale * size.height / srcH);
+  rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
+
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  layers::CairoImage::Data cairoData;
+  cairoData.mSurface = surf;
+  cairoData.mSize = size;
+
+  ImageFormat cairoFormat = CAIRO_SURFACE;
+  nsRefPtr<layers::CairoImage> image = new layers::CairoImage();
+
+  image->SetData(cairoData);
+
+  MonitorAutoLock mon(mMonitor);
+  mImage = image;
+}
+
+nsresult
+MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
+{
+  NS_DispatchToMainThread(new StopRunnable(this));
+  return NS_OK;
+}
+
+nsresult
+MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t)
+{
+  return NS_OK;
+}
+
+bool
+MediaEngineTabVideoSource::IsFake()
+{
+  return false;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/MediaEngineTabVideoSource.h
@@ -0,0 +1,65 @@
+/* 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 "nsIDOMEventListener.h"
+#include "MediaEngine.h"
+#include "ImageContainer.h"
+#include "nsITimer.h"
+#include "mozilla/Monitor.h"
+
+namespace mozilla {
+
+class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventListener, nsITimerCallback {
+  public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSIDOMEVENTLISTENER
+    NS_DECL_NSITIMERCALLBACK
+    MediaEngineTabVideoSource();
+    ~MediaEngineTabVideoSource() { free(mData); }
+    virtual void GetName(nsAString_internal&);
+    virtual void GetUUID(nsAString_internal&);
+    virtual nsresult Allocate(const mozilla::MediaEnginePrefs&);
+    virtual nsresult Deallocate();
+    virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID);
+    virtual nsresult Snapshot(uint32_t, nsIDOMFile**);
+    virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, mozilla::TrackTicks&);
+    virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
+    virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t);
+    virtual bool IsFake();
+    void Draw();
+
+    class StartRunnable : public nsRunnable {
+    public:
+      StartRunnable(MediaEngineTabVideoSource *videoSource) : mVideoSource(videoSource) {}
+      NS_IMETHOD Run();
+      nsRefPtr<MediaEngineTabVideoSource> mVideoSource;
+    };
+
+    class StopRunnable : public nsRunnable {
+    public:
+    StopRunnable(MediaEngineTabVideoSource *videoSource) : mVideoSource(videoSource) {}
+      NS_IMETHOD Run();
+      nsRefPtr<MediaEngineTabVideoSource> mVideoSource;
+    };
+
+    class InitRunnable : public nsRunnable {
+    public:
+    InitRunnable(MediaEngineTabVideoSource *videoSource) : mVideoSource(videoSource) {}
+      NS_IMETHOD Run();
+      nsRefPtr<MediaEngineTabVideoSource> mVideoSource;
+    };
+
+private:
+    int mBufW;
+    int mBufH;
+    int mTimePerFrame;
+    unsigned char *mData;
+    nsCOMPtr<nsIDOMWindow> mWindow;
+    nsRefPtr<layers::CairoImage> mImage;
+    nsCOMPtr<nsITimer> mTimer;
+    nsAutoString mName, mUuid;
+    Monitor mMonitor;
+  };
+
+}
--- a/content/media/webrtc/MediaEngineWebRTC.cpp
+++ b/content/media/webrtc/MediaEngineWebRTC.cpp
@@ -24,24 +24,45 @@ GetUserMediaLog()
   if (!sLog)
     sLog = PR_NewLogModule("GetUserMedia");
   return sLog;
 }
 #endif
 
 #include "MediaEngineWebRTC.h"
 #include "ImageContainer.h"
+#include "nsIComponentRegistrar.h"
+#include "MediaEngineTabVideoSource.h"
+#include "nsITabSource.h"
+
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
+#ifndef MOZ_B2G_CAMERA
+MediaEngineWebRTC::MediaEngineWebRTC()
+  : mMutex("mozilla::MediaEngineWebRTC")
+  , mVideoEngine(nullptr)
+  , mVoiceEngine(nullptr)
+  , mVideoEngineInit(false)
+  , mAudioEngineInit(false)
+  , mHasTabVideoSource(false)
+{
+  nsCOMPtr<nsIComponentRegistrar> compMgr;
+  NS_GetComponentRegistrar(getter_AddRefs(compMgr));
+  if (compMgr) {
+    compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
+  }
+}
+#endif
+
 
 void
 MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
 #ifdef MOZ_B2G_CAMERA
   MutexAutoLock lock(mMutex);
   if (!mCameraManager) {
     return;
@@ -95,16 +116,19 @@ MediaEngineWebRTC::EnumerateVideoDevices
   JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM();
 
   if (webrtc::VideoEngine::SetAndroidObjects(jvm, (void*)context) != 0) {
     LOG(("VieCapture:SetAndroidObjects Failed"));
     return;
   }
 #endif
 
+  if (mHasTabVideoSource)
+    aVSources->AppendElement(new MediaEngineTabVideoSource());
+
   if (!mVideoEngine) {
     if (!(mVideoEngine = webrtc::VideoEngine::Create())) {
       return;
     }
   }
 
   PRLogModuleInfo *logs = GetWebRTCLogInfo();
   if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) {
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -348,28 +348,22 @@ public:
   MediaEngineWebRTC(nsDOMCameraManager* aCameraManager, uint64_t aWindowId)
     : mMutex("mozilla::MediaEngineWebRTC")
     , mVideoEngine(nullptr)
     , mVoiceEngine(nullptr)
     , mVideoEngineInit(false)
     , mAudioEngineInit(false)
     , mCameraManager(aCameraManager)
     , mWindowId(aWindowId)
+    , mHasTabVideoSource(false)
   {
     AsyncLatencyLogger::Get(true)->AddRef();
   }
 #else
-  MediaEngineWebRTC()
-    : mMutex("mozilla::MediaEngineWebRTC")
-    , mVideoEngine(nullptr)
-    , mVoiceEngine(nullptr)
-    , mVideoEngineInit(false)
-    , mAudioEngineInit(false)
-  {
-  }
+  MediaEngineWebRTC();
 #endif
   ~MediaEngineWebRTC() {
     Shutdown();
 #ifdef MOZ_B2G_CAMERA
     AsyncLatencyLogger::Get()->Release();
 #endif
   }
 
@@ -385,16 +379,17 @@ private:
   // protected with mMutex:
 
   webrtc::VideoEngine* mVideoEngine;
   webrtc::VoiceEngine* mVoiceEngine;
 
   // Need this to avoid unneccesary WebRTC calls while enumerating.
   bool mVideoEngineInit;
   bool mAudioEngineInit;
+  bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
   nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
 
 #ifdef MOZ_B2G_CAMERA
   // MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
--- a/content/media/webrtc/moz.build
+++ b/content/media/webrtc/moz.build
@@ -1,28 +1,34 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # 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_MODULE = 'content_webrtc'
+
 MODULE = 'content'
 
 EXPORTS += [
     'MediaEngine.h',
     'MediaEngineDefault.h',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     EXPORTS += ['MediaEngineWebRTC.h']
     CPP_SOURCES += [
+        'MediaEngineTabVideoSource.cpp',
         'MediaEngineWebRTC.cpp',
         'MediaEngineWebRTCVideo.cpp',
         'MediaEngineWebRTCAudio.cpp',
     ]
+XPIDL_SOURCES += [
+        'nsITabSource.idl'
+]
 
 CPP_SOURCES += [
     'MediaEngineDefault.cpp',
 ]
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'gkconwebrtc_s'
new file mode 100644
--- /dev/null
+++ b/content/media/webrtc/nsITabSource.idl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIDOMWindow.idl"
+
+[scriptable,uuid(ff9c0e45-4646-45ec-b2f0-3b16d9e41875)]
+interface nsITabSource : nsISupports
+{
+  nsIDOMWindow getTabToStream();
+};
+
+%{C++
+#define NS_TABSOURCESERVICE_CONTRACTID "@mozilla.org/tab-source-service;1"
+%}
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -556,17 +556,18 @@ BluetoothHfpManager::HandleVolumeChanged
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged()
 {
   nsCOMPtr<nsIMobileConnectionProvider> connection =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIDOMMozMobileConnectionInfo> voiceInfo;
-  connection->GetVoiceConnectionInfo(getter_AddRefs(voiceInfo));
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
+  connection->GetVoiceConnectionInfo(0, getter_AddRefs(voiceInfo));
   NS_ENSURE_TRUE_VOID(voiceInfo);
 
   nsString type;
   voiceInfo->GetType(type);
   mPhoneType = GetPhoneType(type);
 
   bool roaming;
   voiceInfo->GetRoaming(&roaming);
@@ -592,17 +593,18 @@ BluetoothHfpManager::HandleVoiceConnecti
 
   /**
    * Possible return values for mode are:
    * - null (unknown): set mNetworkSelectionMode to 0 (auto)
    * - automatic: set mNetworkSelectionMode to 0 (auto)
    * - manual: set mNetworkSelectionMode to 1 (manual)
    */
   nsString mode;
-  connection->GetNetworkSelectionMode(mode);
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
+  connection->GetNetworkSelectionMode(0, mode);
   if (mode.EqualsLiteral("manual")) {
     mNetworkSelectionMode = 1;
   } else {
     mNetworkSelectionMode = 0;
   }
 
   nsCOMPtr<nsIDOMMozMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -287,30 +287,32 @@ BluetoothRilListener::StopIccListening()
 
 bool
 BluetoothRilListener::StartMobileConnectionListening()
 {
   nsCOMPtr<nsIMobileConnectionProvider> provider =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE(provider, false);
 
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
   nsresult rv = provider->
-                  RegisterMobileConnectionMsg(mMobileConnectionListener);
+                  RegisterMobileConnectionMsg(0, mMobileConnectionListener);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 BluetoothRilListener::StopMobileConnectionListening()
 {
   nsCOMPtr<nsIMobileConnectionProvider> provider =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE(provider, false);
 
+  // TODO: Bug 921991 - B2G BT: support multiple sim cards
   nsresult rv = provider->
-                  UnregisterMobileConnectionMsg(mMobileConnectionListener);
+                  UnregisterMobileConnectionMsg(0, mMobileConnectionListener);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 BluetoothRilListener::StartTelephonyListening()
 {
   nsCOMPtr<nsITelephonyProvider> provider =
     do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1935,17 +1935,22 @@ ContentParent::RecvBroadcastVolume(const
 #endif
 }
 
 bool
 ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus)
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
-        obs->NotifyObservers(nullptr, "recording-device-events", aRecordingStatus.get());
+        // recording-device-ipc-events needs to gather more information from content process
+        nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+        props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
+        obs->NotifyObservers((nsIPropertyBag2*) props,
+                             "recording-device-ipc-events",
+                             aRecordingStatus.get());
     } else {
         NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
     }
     return true;
 }
 
 bool
 ContentParent::SendNuwaFork()
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -51,16 +51,17 @@ this.SystemMessagePermissionsTable = {
   },
   "bluetooth-opp-receiving-file-confirmation": {
     "bluetooth": []
   },
   "bluetooth-opp-transfer-start": {
     "bluetooth": []
   },
   "connection": { },
+  "dummy-system-message": { }, // for system message testing framework
   "headset-button": { },
   "icc-stkcommand": {
     "settings": ["read", "write"]
   },
   "media-button": { },
   "notification": {
     "desktop-notification": []
   },
--- a/dom/network/interfaces/nsIMobileConnectionProvider.idl
+++ b/dom/network/interfaces/nsIMobileConnectionProvider.idl
@@ -28,60 +28,84 @@ interface nsIMobileConnectionListener : 
                                     in unsigned long timeoutMs);
   void notifyOtaStatusChanged(in DOMString status);
 };
 
 /**
  * XPCOM component (in the content process) that provides the mobile
  * network information.
  */
-[scriptable, uuid(c66652e0-0628-11e3-8ffd-0800200c9a66)]
+[scriptable, uuid(84278a49-0f05-4585-b3f4-c74882ae5719)]
 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);
+  void registerMobileConnectionMsg(in unsigned long clientId,
+                                   in nsIMobileConnectionListener listener);
+  void unregisterMobileConnectionMsg(in unsigned long clientId,
+                                     in nsIMobileConnectionListener listener);
+
+  nsIDOMMozMobileConnectionInfo getVoiceConnectionInfo(in unsigned long clientId);
+  nsIDOMMozMobileConnectionInfo getDataConnectionInfo(in unsigned long clientId);
+  DOMString getIccId(in unsigned long clientId);
+  DOMString getNetworkSelectionMode(in unsigned long clientId);
 
-  readonly attribute nsIDOMMozMobileConnectionInfo voiceConnectionInfo;
-  readonly attribute nsIDOMMozMobileConnectionInfo dataConnectionInfo;
-  readonly attribute DOMString networkSelectionMode;
+  nsIDOMDOMRequest getNetworks(in unsigned long clientId,
+                               in nsIDOMWindow window);
+  nsIDOMDOMRequest selectNetwork(in unsigned long clientId,
+                                 in nsIDOMWindow window,
+                                 in nsIDOMMozMobileNetworkInfo network);
+  nsIDOMDOMRequest selectNetworkAutomatically(in unsigned long clientId,
+                                              in nsIDOMWindow window);
 
-  nsIDOMDOMRequest getNetworks(in nsIDOMWindow window);
-  nsIDOMDOMRequest selectNetwork(in nsIDOMWindow window, in nsIDOMMozMobileNetworkInfo network);
-  nsIDOMDOMRequest selectNetworkAutomatically(in nsIDOMWindow window);
-
-  nsIDOMDOMRequest setRoamingPreference(in nsIDOMWindow window,
+  nsIDOMDOMRequest setRoamingPreference(in unsigned long clientId,
+                                        in nsIDOMWindow window,
                                         in DOMString mode);
-  nsIDOMDOMRequest getRoamingPreference(in nsIDOMWindow window);
+  nsIDOMDOMRequest getRoamingPreference(in unsigned long clientId,
+                                        in nsIDOMWindow window);
 
-  nsIDOMDOMRequest setVoicePrivacyMode(in nsIDOMWindow window,
+  nsIDOMDOMRequest setVoicePrivacyMode(in unsigned long clientId,
+                                       in nsIDOMWindow window,
                                        in bool enabled);
-  nsIDOMDOMRequest getVoicePrivacyMode(in nsIDOMWindow window);
+  nsIDOMDOMRequest getVoicePrivacyMode(in unsigned long clientId,
+                                       in nsIDOMWindow window);
 
-  nsIDOMDOMRequest sendMMI(in nsIDOMWindow window, in DOMString mmi);
-  nsIDOMDOMRequest cancelMMI(in nsIDOMWindow window);
+  nsIDOMDOMRequest sendMMI(in unsigned long clientId,
+                           in nsIDOMWindow window,
+                           in DOMString mmi);
+  nsIDOMDOMRequest cancelMMI(in unsigned long clientId,
+                             in nsIDOMWindow window);
 
-  nsIDOMDOMRequest getCallForwardingOption(in nsIDOMWindow   window,
+  nsIDOMDOMRequest getCallForwardingOption(in unsigned long clientId,
+                                           in nsIDOMWindow window,
                                            in unsigned short reason);
-  nsIDOMDOMRequest setCallForwardingOption(in nsIDOMWindow          window,
+  nsIDOMDOMRequest setCallForwardingOption(in unsigned long clientId,
+                                           in nsIDOMWindow window,
                                            in nsIDOMMozMobileCFInfo CFInfo);
 
-  nsIDOMDOMRequest getCallBarringOption(in nsIDOMWindow window,
-                                        in jsval        option);
-  nsIDOMDOMRequest setCallBarringOption(in nsIDOMWindow window,
-                                        in jsval        option);
-  nsIDOMDOMRequest changeCallBarringPassword(in nsIDOMWindow window,
+  nsIDOMDOMRequest getCallBarringOption(in unsigned long clientId,
+                                        in nsIDOMWindow window,
+                                        in jsval option);
+  nsIDOMDOMRequest setCallBarringOption(in unsigned long clientId,
+                                        in nsIDOMWindow window,
+                                        in jsval option);
+  nsIDOMDOMRequest changeCallBarringPassword(in unsigned long clientId,
+                                             in nsIDOMWindow window,
                                              in jsval info);
 
-  nsIDOMDOMRequest setCallWaitingOption(in nsIDOMWindow   window,
+  nsIDOMDOMRequest setCallWaitingOption(in unsigned long clientId,
+                                        in nsIDOMWindow window,
                                         in bool enabled);
-  nsIDOMDOMRequest getCallWaitingOption(in nsIDOMWindow   window);
+  nsIDOMDOMRequest getCallWaitingOption(in unsigned long clientId,
+                                        in nsIDOMWindow window);
 
-  nsIDOMDOMRequest setCallingLineIdRestriction(in nsIDOMWindow   window,
+  nsIDOMDOMRequest setCallingLineIdRestriction(in unsigned long clientId,
+                                               in nsIDOMWindow window,
                                                in unsigned short clirMode);
-  nsIDOMDOMRequest getCallingLineIdRestriction(in nsIDOMWindow   window);
+  nsIDOMDOMRequest getCallingLineIdRestriction(in unsigned long clientId,
+                                               in nsIDOMWindow window);
 
-  nsIDOMDOMRequest exitEmergencyCbMode(in nsIDOMWindow window);
+  nsIDOMDOMRequest exitEmergencyCbMode(in unsigned long clientId,
+                                       in nsIDOMWindow window);
 };
--- a/dom/network/src/MobileConnection.cpp
+++ b/dom/network/src/MobileConnection.cpp
@@ -78,16 +78,19 @@ NS_IMPL_EVENT_HANDLER(MobileConnection, 
 NS_IMPL_EVENT_HANDLER(MobileConnection, emergencycbmodechange)
 NS_IMPL_EVENT_HANDLER(MobileConnection, otastatuschange)
 
 MobileConnection::MobileConnection()
 {
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   mWindow = nullptr;
 
+  // TODO: Bug 814629 - WebMobileConnection API: support multiple sim cards
+  mClientId = 0;
+
   // Not being able to acquire the provider isn't fatal since we check
   // for it explicitly below.
   if (!mProvider) {
     NS_WARNING("Could not acquire nsIMobileConnectionProvider!");
     return;
   }
 }
 
@@ -96,30 +99,30 @@ MobileConnection::Init(nsPIDOMWindow* aW
 {
   BindToOwner(aWindow);
 
   mWindow = do_GetWeakReference(aWindow);
   mListener = new Listener(this);
 
   if (!CheckPermission("mobilenetwork") &&
       CheckPermission("mobileconnection")) {
-    DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mListener);
+    DebugOnly<nsresult> rv = mProvider->RegisterMobileConnectionMsg(mClientId, mListener);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                      "Failed registering mobile connection messages with provider");
 
     printf_stderr("MobileConnection initialized");
   }
 }
 
 void
 MobileConnection::Shutdown()
 {
   if (mProvider && mListener) {
     mListener->Disconnect();
-    mProvider->UnregisterMobileConnectionMsg(mListener);
+    mProvider->UnregisterMobileConnectionMsg(mClientId, mListener);
     mProvider = nullptr;
     mListener = nullptr;
   }
 }
 
 // nsIDOMMozMobileConnection
 
 NS_IMETHODIMP
@@ -168,347 +171,347 @@ MobileConnection::CheckPermission(const 
 NS_IMETHODIMP
 MobileConnection::GetVoice(nsIDOMMozMobileConnectionInfo** voice)
 {
   *voice = nullptr;
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
     return NS_OK;
   }
-  return mProvider->GetVoiceConnectionInfo(voice);
+  return mProvider->GetVoiceConnectionInfo(mClientId, voice);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetData(nsIDOMMozMobileConnectionInfo** data)
 {
   *data = nullptr;
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
     return NS_OK;
   }
-  return mProvider->GetDataConnectionInfo(data);
+  return mProvider->GetDataConnectionInfo(mClientId, data);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetNetworkSelectionMode(nsAString& networkSelectionMode)
 {
   networkSelectionMode.SetIsVoid(true);
 
   if (!mProvider || !CheckPermission("mobileconnection")) {
      return NS_OK;
   }
-  return mProvider->GetNetworkSelectionMode(networkSelectionMode);
+  return mProvider->GetNetworkSelectionMode(mClientId, networkSelectionMode);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetNetworks(nsIDOMDOMRequest** request)
 {
   *request = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetNetworks(GetOwner(), request);
+  return mProvider->GetNetworks(mClientId, GetOwner(), request);
 }
 
 NS_IMETHODIMP
 MobileConnection::SelectNetwork(nsIDOMMozMobileNetworkInfo* network, nsIDOMDOMRequest** request)
 {
   *request = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SelectNetwork(GetOwner(), network, request);
+  return mProvider->SelectNetwork(mClientId, GetOwner(), network, request);
 }
 
 NS_IMETHODIMP
 MobileConnection::SelectNetworkAutomatically(nsIDOMDOMRequest** request)
 {
   *request = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SelectNetworkAutomatically(GetOwner(), request);
+  return mProvider->SelectNetworkAutomatically(mClientId, GetOwner(), request);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetRoamingPreference(const nsAString& aMode, nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetRoamingPreference(GetOwner(), aMode, aDomRequest);
+  return mProvider->SetRoamingPreference(mClientId, GetOwner(), aMode, aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetRoamingPreference(nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetRoamingPreference(GetOwner(), aDomRequest);
+  return mProvider->GetRoamingPreference(mClientId, GetOwner(), aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetVoicePrivacyMode(bool aEnabled, nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetVoicePrivacyMode(GetOwner(), aEnabled, aDomRequest);
+  return mProvider->SetVoicePrivacyMode(mClientId, GetOwner(), aEnabled, aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetVoicePrivacyMode(nsIDOMDOMRequest** aDomRequest)
 {
   *aDomRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetVoicePrivacyMode(GetOwner(), aDomRequest);
+  return mProvider->GetVoicePrivacyMode(mClientId, GetOwner(), aDomRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SendMMI(const nsAString& aMMIString,
                           nsIDOMDOMRequest** aRequest)
 {
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SendMMI(GetOwner(), aMMIString, aRequest);
+  return mProvider->SendMMI(mClientId, GetOwner(), aMMIString, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::CancelMMI(nsIDOMDOMRequest** aRequest)
 {
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->CancelMMI(GetOwner(), aRequest);
+  return mProvider->CancelMMI(mClientId, GetOwner(),aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallForwardingOption(uint16_t aReason,
                                           nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallForwardingOption(GetOwner(), aReason, aRequest);
+  return mProvider->GetCallForwardingOption(mClientId, GetOwner(), aReason, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallForwardingOption(nsIDOMMozMobileCFInfo* aCFInfo,
                                           nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallForwardingOption(GetOwner(), aCFInfo, aRequest);
+  return mProvider->SetCallForwardingOption(mClientId, GetOwner(), aCFInfo, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallBarringOption(const JS::Value& aOption,
                                        nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallBarringOption(GetOwner(), aOption, aRequest);
+  return mProvider->GetCallBarringOption(mClientId, GetOwner(), aOption, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallBarringOption(const JS::Value& aOption,
                                        nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallBarringOption(GetOwner(), aOption, aRequest);
+  return mProvider->SetCallBarringOption(mClientId, GetOwner(), aOption, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::ChangeCallBarringPassword(const JS::Value& aInfo,
                                             nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->ChangeCallBarringPassword(GetOwner(), aInfo, aRequest);
+  return mProvider->ChangeCallBarringPassword(mClientId, GetOwner(), aInfo, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallWaitingOption(nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallWaitingOption(GetOwner(), aRequest);
+  return mProvider->GetCallWaitingOption(mClientId, GetOwner(), aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallWaitingOption(bool aEnabled,
                                        nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallWaitingOption(GetOwner(), aEnabled, aRequest);
+  return mProvider->SetCallWaitingOption(mClientId, GetOwner(), aEnabled, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::GetCallingLineIdRestriction(nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->GetCallingLineIdRestriction(GetOwner(), aRequest);
+  return mProvider->GetCallingLineIdRestriction(mClientId, GetOwner(), aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::SetCallingLineIdRestriction(unsigned short aClirMode,
                                               nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->SetCallingLineIdRestriction(GetOwner(), aClirMode, aRequest);
+  return mProvider->SetCallingLineIdRestriction(mClientId, GetOwner(), aClirMode, aRequest);
 }
 
 NS_IMETHODIMP
 MobileConnection::ExitEmergencyCbMode(nsIDOMDOMRequest** aRequest)
 {
   *aRequest = nullptr;
 
   if (!CheckPermission("mobileconnection")) {
     return NS_OK;
   }
 
   if (!mProvider) {
     return NS_ERROR_FAILURE;
   }
 
-  return mProvider->ExitEmergencyCbMode(GetOwner(), aRequest);
+  return mProvider->ExitEmergencyCbMode(mClientId, GetOwner(), aRequest);
 }
 
 // nsIMobileConnectionListener
 
 NS_IMETHODIMP
 MobileConnection::NotifyVoiceChanged()
 {
   if (!CheckPermission("mobileconnection")) {
--- a/dom/network/src/MobileConnection.h
+++ b/dom/network/src/MobileConnection.h
@@ -43,16 +43,18 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MobileConnection,
                                            nsDOMEventTargetHelper)
 
 private:
   nsCOMPtr<nsIMobileConnectionProvider> mProvider;
   nsRefPtr<Listener> mListener;
   nsWeakPtr mWindow;
 
+  uint32_t mClientId;
+
   bool CheckPermission(const char* type);
 };
 
 } // namespace network
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_network_MobileConnection_h
--- a/dom/phonenumberutils/PhoneNumberUtils.jsm
+++ b/dom/phonenumberutils/PhoneNumberUtils.jsm
@@ -39,17 +39,18 @@ this.PhoneNumberUtils = {
   _mcc: '724',
 
   getCountryName: function getCountryName() {
     let mcc;
     let countryName;
 
 #ifdef MOZ_B2G_RIL
     // Get network mcc
-    let voice = mobileConnection.voiceConnectionInfo;
+    // TODO: Bug 926740 - PhoneNumberUtils for multisim
+    let voice = mobileConnection.getVoiceConnectionInfo(0);
     if (voice && voice.network && voice.network.mcc) {
       mcc = voice.network.mcc;
     }
 
     // Get SIM mcc
     let iccInfo = icc.iccInfo;
     if (!mcc && iccInfo && iccInfo.mcc) {
       mcc = iccInfo.mcc;
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -83,23 +83,23 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:CardLockResult",
   "RIL:CardLockRetryCount",
   "RIL:USSDReceived",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:StkCommand",
   "RIL:StkSessionEnd",
   "RIL:DataError",
-  "RIL:SetCallForwardingOption",
-  "RIL:GetCallForwardingOption",
-  "RIL:SetCallBarringOption",
-  "RIL:GetCallBarringOption",
+  "RIL:SetCallForwardingOptions",
+  "RIL:GetCallForwardingOptions",
+  "RIL:SetCallBarringOptions",
+  "RIL:GetCallBarringOptions",
   "RIL:ChangeCallBarringPassword",
-  "RIL:SetCallWaitingOption",
-  "RIL:GetCallWaitingOption",
+  "RIL:SetCallWaitingOptions",
+  "RIL:GetCallWaitingOptions",
   "RIL:SetCallingLineIdRestriction",
   "RIL:GetCallingLineIdRestriction",
   "RIL:CellBroadcastReceived",
   "RIL:CfStateChanged",
   "RIL:IccOpenChannel",
   "RIL:IccCloseChannel",
   "RIL:IccExchangeAPDU",
   "RIL:ReadIccContacts",
@@ -379,23 +379,23 @@ CellBroadcastEtwsInfo.prototype = {
 
   // nsIDOMMozCellBroadcastEtwsInfo
 
   warningType: null,
   emergencyUserAlert: null,
   popup: null
 };
 
-function CallBarringOption(option) {
-  this.program = option.program;
-  this.enabled = option.enabled;
-  this.password = option.password;
-  this.serviceClass = option.serviceClass;
+function CallBarringOptions(options) {
+  this.program = options.program;
+  this.enabled = options.enabled;
+  this.password = options.password;
+  this.serviceClass = options.serviceClass;
 }
-CallBarringOption.prototype = {
+CallBarringOptions.prototype = {
   __exposedProps__ : {program: 'r',
                       enabled: 'r',
                       password: 'r',
                       serviceClass: 'r'}
 };
 
 function DOMMMIResult(result) {
   this.serviceCode = result.serviceCode;
@@ -444,27 +444,40 @@ IccCardLockError.prototype = {
   __init: function(lockType, errorMsg, retryCount) {
     this.__DOM_IMPL__.init(errorMsg);
     this.lockType = lockType;
     this.retryCount = retryCount;
   },
 };
 
 function RILContentHelper() {
-  this.rilContext = {
-    cardState:            RIL.GECKO_CARDSTATE_UNKNOWN,
-    networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
-    iccInfo:              null,
-    voiceConnectionInfo:  new MobileConnectionInfo(),
-    dataConnectionInfo:   new MobileConnectionInfo()
-  };
+
+  this.numClients = gNumRadioInterfaces;
+  debug("Number of clients: " + this.numClients);
+
+  this.rilContexts = [];
+  for (let clientId = 0; clientId < this.numClients; clientId++) {
+    this.rilContexts[clientId] = {
+      cardState:            RIL.GECKO_CARDSTATE_UNKNOWN,
+      networkSelectionMode: RIL.GECKO_NETWORK_SELECTION_UNKNOWN,
+      iccInfo:              null,
+      voiceConnectionInfo:  new MobileConnectionInfo(),
+      dataConnectionInfo:   new MobileConnectionInfo()
+    };
+  }
   this.voicemailInfo = new VoicemailInfo();
 
   this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
+  this._selectingNetworks = [];
+  this._mobileConnectionListeners = [];
+  this._cellBroadcastListeners = [];
+  this._voicemailListeners = [];
+  this._iccListeners = [];
+
   Services.obs.addObserver(this, "xpcom-shutdown", false);
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionProvider,
                                          Ci.nsICellBroadcastProvider,
@@ -520,123 +533,138 @@ RILContentHelper.prototype = {
     this.updateInfo(srcNetwork, network);
   },
 
   /**
    * We need to consider below cases when update iccInfo:
    * 1. Should clear iccInfo to null if there is no card detected.
    * 2. Need to create corresponding object based on iccType.
    */
-  updateIccInfo: function updateIccInfo(newInfo) {
+  updateIccInfo: function updateIccInfo(clientId, newInfo) {
+    let rilContext = this.rilContexts[clientId];
+
     // Card is not detected, clear iccInfo to null.
     if (!newInfo || !newInfo.iccType) {
-      this.rilContext.iccInfo = null;
+      rilContext.iccInfo = null;
       return;
     }
 
     // If iccInfo is null, new corresponding object based on iccType.
-    if (!this.rilContext.iccInfo) {
+    if (!rilContext.iccInfo) {
       if (newInfo.iccType === "ruim" || newInfo.iccType === "csim") {
-        this.rilContext.iccInfo = new CdmaIccInfo();
+        rilContext.iccInfo = new CdmaIccInfo();
       } else {
-        this.rilContext.iccInfo = new GsmIccInfo();
+        rilContext.iccInfo = new GsmIccInfo();
       }
     }
 
-    this.updateInfo(newInfo, this.rilContext.iccInfo);
+    this.updateInfo(newInfo, rilContext.iccInfo);
   },
 
   _windowsMap: null,
 
-  rilContext: null,
+  rilContexts: null,
 
-  getRilContext: function getRilContext() {
-    // Update ril context by sending IPC message to chrome only when the first
+  getRilContext: function getRilContext(clientId) {
+    // Update ril contexts by sending IPC message to chrome only when the first
     // time we require it. The information will be updated by following info
     // changed messages.
-    this.getRilContext = function getRilContext() {
-      return this.rilContext;
+    this.getRilContext = function getRilContext(clientId) {
+      return this.rilContexts[clientId];
     };
 
-    let rilContext =
-      cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: 0})[0];
-    if (!rilContext) {
-      debug("Received null rilContext from chrome process.");
-      return;
+    for (let cId = 0; cId < this.numClients; cId++) {
+      let rilContext =
+        cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: cId})[0];
+      if (!rilContext) {
+        debug("Received null rilContext from chrome process.");
+        continue;
+      }
+      this.rilContexts[cId].cardState = rilContext.cardState;
+      this.rilContexts[cId].networkSelectionMode = rilContext.networkSelectionMode;
+      this.updateIccInfo(cId, rilContext.iccInfo);
+      this.updateConnectionInfo(rilContext.voice, this.rilContexts[cId].voiceConnectionInfo);
+      this.updateConnectionInfo(rilContext.data, this.rilContexts[cId].dataConnectionInfo);
     }
-    this.rilContext.cardState = rilContext.cardState;
-    this.rilContext.networkSelectionMode = rilContext.networkSelectionMode;
-    this.updateIccInfo(rilContext.iccInfo);
-    this.updateConnectionInfo(rilContext.voice, this.rilContext.voiceConnectionInfo);
-    this.updateConnectionInfo(rilContext.data, this.rilContext.dataConnectionInfo);
+
+    return this.rilContexts[clientId];
+  },
+
+  /**
+   * nsIIccProvider
+   */
 
-    return this.rilContext;
+  get iccInfo() {
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    let context = this.getRilContext(0);
+    return context && context.iccInfo;
+  },
+
+  get cardState() {
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    let context = this.getRilContext(0);
+    return context && context.cardState;
   },
 
   /**
    * nsIMobileConnectionProvider
    */
 
-  get iccInfo() {
-    let context = this.getRilContext();
-    return context && context.iccInfo;
-  },
-
-  get voiceConnectionInfo() {
-    let context = this.getRilContext();
+  getVoiceConnectionInfo: function getVoiceConnectionInfo(clientId) {
+    let context = this.getRilContext(clientId);
     return context && context.voiceConnectionInfo;
   },
 
-  get dataConnectionInfo() {
-    let context = this.getRilContext();
+  getDataConnectionInfo: function getDataConnectionInfo(clientId) {
+    let context = this.getRilContext(clientId);
     return context && context.dataConnectionInfo;
   },
 
-  get cardState() {
-    let context = this.getRilContext();
-    return context && context.cardState;
+  getIccId: function getIccId(clientId) {
+    let context = this.getRilContext(clientId);
+    return context && context.iccInfo.iccid;
   },
 
-  get networkSelectionMode() {
-    let context = this.getRilContext();
+  getNetworkSelectionMode: function getNetworkSelectionMode(clientId) {
+    let context = this.getRilContext(clientId);
     return context && context.networkSelectionMode;
   },
 
   /**
-   * The network that is currently trying to be selected (or "automatic").
-   * This helps ensure that only one network is selected at a time.
+   * The networks that are currently trying to be selected (or "automatic").
+   * This helps ensure that only one network per client is selected at a time.
    */
-  _selectingNetwork: null,
+  _selectingNetworks: null,
 
-  getNetworks: function getNetworks(window) {
+  getNetworks: function getNetworks(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetAvailableNetworks", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
-  selectNetwork: function selectNetwork(window, network) {
+  selectNetwork: function selectNetwork(clientId, window, network) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
-    if (this._selectingNetwork) {
-      throw new Error("Already selecting a network: " + this._selectingNetwork);
+    if (this._selectingNetworks[clientId]) {
+      throw new Error("Already selecting a network: " + this._selectingNetworks[clientId]);
     }
 
     if (!network) {
       throw new Error("Invalid network provided: " + network);
     }
 
     if (isNaN(parseInt(network.mnc, 10))) {
       throw new Error("Invalid network MNC: " + network.mnc);
@@ -644,143 +672,143 @@ RILContentHelper.prototype = {
 
     if (isNaN(parseInt(network.mcc, 10))) {
       throw new Error("Invalid network MCC: " + network.mcc);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL &&
-        this.rilContext.voiceConnectionInfo.network === network) {
+    if (this.rilContexts[clientId].networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL &&
+        this.rilContexts[clientId].voiceConnectionInfo.network === network) {
 
       // Already manually selected this network, so schedule
       // onsuccess to be fired on the next tick
       this.dispatchFireRequestSuccess(requestId, null);
       return request;
     }
 
-    this._selectingNetwork = network;
+    this._selectingNetworks[clientId] = network;
 
     cpmm.sendAsyncMessage("RIL:SelectNetwork", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         mnc: network.mnc,
         mcc: network.mcc
       }
     });
 
     return request;
   },
 
-  selectNetworkAutomatically: function selectNetworkAutomatically(window) {
+  selectNetworkAutomatically: function selectNetworkAutomatically(clientId, window) {
 
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
-    if (this._selectingNetwork) {
-      throw new Error("Already selecting a network: " + this._selectingNetwork);
+    if (this._selectingNetworks[clientId]) {
+      throw new Error("Already selecting a network: " + this._selectingNetworks[clientId]);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
+    if (this.rilContexts[clientId].networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
       // Already using automatic selection mode, so schedule
       // onsuccess to be be fired on the next tick
       this.dispatchFireRequestSuccess(requestId, null);
       return request;
     }
 
-    this._selectingNetwork = "automatic";
+    this._selectingNetworks[clientId] = "automatic";
     cpmm.sendAsyncMessage("RIL:SelectNetworkAuto", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
-  setRoamingPreference: function setRoamingPreference(window, mode) {
+  setRoamingPreference: function setRoamingPreference(clientId, window, mode) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (!mode) {
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
     cpmm.sendAsyncMessage("RIL:SetRoamingPreference", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         mode: mode
       }
     });
     return request;
   },
 
-  getRoamingPreference: function getRoamingPreference(window) {
+  getRoamingPreference: function getRoamingPreference(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetRoamingPreference", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
-  setVoicePrivacyMode: function setVoicePrivacyMode(window, enabled) {
+  setVoicePrivacyMode: function setVoicePrivacyMode(clientId, window, enabled) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:SetVoicePrivacyMode", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         enabled: enabled
       }
     });
     return request;
   },
 
-  getVoicePrivacyMode: function getVoicePrivacyMode(window) {
+  getVoicePrivacyMode: function getVoicePrivacyMode(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
 
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetVoicePrivacyMode", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
   getCardLockState: function getCardLockState(window, lockType) {
@@ -846,48 +874,48 @@ RILContentHelper.prototype = {
       data: {
         lockType: lockType,
         requestId: requestId
       }
     });
     return request;
   },
 
-  sendMMI: function sendMMI(window, mmi) {
+  sendMMI: function sendMMI(clientId, window, mmi) {
     debug("Sending MMI " + mmi);
     if (!window) {
       throw Components.Exception("Can't get window object",
                                  Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
     // We need to save the global window to get the proper MMIError
     // constructor once we get the reply from the parent process.
     this._windowsMap[requestId] = window;
 
     cpmm.sendAsyncMessage("RIL:SendMMI", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         mmi: mmi,
         requestId: requestId
       }
     });
     return request;
   },
 
-  cancelMMI: function cancelMMI(window) {
+  cancelMMI: function cancelMMI(clientId, window) {
     debug("Cancel MMI");
     if (!window) {
       throw Components.Exception("Can't get window object",
                                  Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
     cpmm.sendAsyncMessage("RIL:CancelMMI", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
     return request;
   },
 
   sendStkResponse: function sendStkResponse(window, command, response) {
@@ -1071,128 +1099,128 @@ RILContentHelper.prototype = {
         contact: iccContact,
         pin2: pin2
       }
     });
 
     return request;
   },
 
-  getCallForwardingOption: function getCallForwardingOption(window, reason) {
+  getCallForwardingOption: function getCallForwardingOption(clientId, window, reason) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (!this._isValidCFReason(reason)){
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:GetCallForwardingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:GetCallForwardingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         reason: reason
       }
     });
 
     return request;
   },
 
-  setCallForwardingOption: function setCallForwardingOption(window, cfInfo) {
+  setCallForwardingOption: function setCallForwardingOption(clientId, window, cfInfo) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (!cfInfo ||
         !this._isValidCFReason(cfInfo.reason) ||
         !this._isValidCFAction(cfInfo.action)){
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:SetCallForwardingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:SetCallForwardingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         active: cfInfo.active,
         action: cfInfo.action,
         reason: cfInfo.reason,
         number: cfInfo.number,
         timeSeconds: cfInfo.timeSeconds
       }
     });
 
     return request;
   },
 
-  getCallBarringOption: function getCallBarringOption(window, option) {
+  getCallBarringOption: function getCallBarringOption(clientId, window, option) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (DEBUG) debug("getCallBarringOption: " + JSON.stringify(option));
-    if (!this._isValidCallBarringOption(option)) {
+    if (!this._isValidCallBarringOptions(option)) {
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:GetCallBarringOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:GetCallBarringOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         program: option.program,
         password: option.password,
         serviceClass: option.serviceClass
       }
     });
     return request;
   },
 
-  setCallBarringOption: function setCallBarringOption(window, option) {
+  setCallBarringOption: function setCallBarringOption(clientId, window, option) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     if (DEBUG) debug("setCallBarringOption: " + JSON.stringify(option));
-    if (!this._isValidCallBarringOption(option, true)) {
+    if (!this._isValidCallBarringOptions(option, true)) {
       this.dispatchFireRequestError(requestId,
                                     RIL.GECKO_ERROR_INVALID_PARAMETER);
       return request;
     }
 
-    cpmm.sendAsyncMessage("RIL:SetCallBarringOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:SetCallBarringOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         program: option.program,
         enabled: option.enabled,
         password: option.password,
         serviceClass: option.serviceClass
       }
     });
     return request;
   },
 
-  changeCallBarringPassword: function changeCallBarringPassword(window, info) {
+  changeCallBarringPassword: function changeCallBarringPassword(clientId, window, info) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     // Checking valid PIN for supplementary services. See TS.22.004 clause 5.2.
@@ -1200,109 +1228,109 @@ RILContentHelper.prototype = {
         info.newPin == null || !info.newPin.match(/^\d{4}$/)) {
       this.dispatchFireRequestError(requestId, "InvalidPassword");
       return request;
     }
 
     if (DEBUG) debug("changeCallBarringPassword: " + JSON.stringify(info));
     info.requestId = requestId;
     cpmm.sendAsyncMessage("RIL:ChangeCallBarringPassword", {
-      clientId: 0,
+      clientId: clientId,
       data: info
     });
 
     return request;
   },
 
-  getCallWaitingOption: function getCallWaitingOption(window) {
+  getCallWaitingOption: function getCallWaitingOption(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    cpmm.sendAsyncMessage("RIL:GetCallWaitingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:GetCallWaitingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
 
     return request;
   },
 
-  setCallWaitingOption: function setCallWaitingOption(window, enabled) {
+  setCallWaitingOption: function setCallWaitingOption(clientId, window, enabled) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
-    cpmm.sendAsyncMessage("RIL:SetCallWaitingOption", {
-      clientId: 0,
+    cpmm.sendAsyncMessage("RIL:SetCallWaitingOptions", {
+      clientId: clientId,
       data: {
         requestId: requestId,
         enabled: enabled
       }
     });
 
     return request;
   },
 
-  getCallingLineIdRestriction: function getCallingLineIdRestriction(window) {
+  getCallingLineIdRestriction: function getCallingLineIdRestriction(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:GetCallingLineIdRestriction", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId
       }
     });
 
     return request;
   },
 
   setCallingLineIdRestriction:
-    function setCallingLineIdRestriction(window, clirMode) {
+    function setCallingLineIdRestriction(clientId, window, clirMode) {
 
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:SetCallingLineIdRestriction", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
         clirMode: clirMode
       }
     });
 
     return request;
   },
 
-  exitEmergencyCbMode: function exitEmergencyCbMode(window) {
+  exitEmergencyCbMode: function exitEmergencyCbMode(clientId, window) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
 
     cpmm.sendAsyncMessage("RIL:ExitEmergencyCbMode", {
-      clientId: 0,
+      clientId: clientId,
       data: {
         requestId: requestId,
       }
     });
 
     return request;
   },
 
@@ -1329,81 +1357,93 @@ RILContentHelper.prototype = {
   },
   get voicemailNumber() {
     return this.getVoicemailInfo().number;
   },
   get voicemailDisplayName() {
     return this.getVoicemailInfo().displayName;
   },
 
-  registerListener: function registerListener(listenerType, listener) {
-    let listeners = this[listenerType];
+  registerListener: function registerListener(listenerType, clientId, listener) {
+    if (!this[listenerType]) {
+      return;
+    }
+    let listeners = this[listenerType][clientId];
     if (!listeners) {
-      listeners = this[listenerType] = [];
+      listeners = this[listenerType][clientId] = [];
     }
 
     if (listeners.indexOf(listener) != -1) {
       throw new Error("Already registered this listener!");
     }
 
     listeners.push(listener);
     if (DEBUG) debug("Registered " + listenerType + " listener: " + listener);
   },
 
-  unregisterListener: function unregisterListener(listenerType, listener) {
-    let listeners = this[listenerType];
+  unregisterListener: function unregisterListener(listenerType, clientId, listener) {
+    if (!this[listenerType]) {
+      return;
+    }
+    let listeners = this[listenerType][clientId];
     if (!listeners) {
       return;
     }
 
     let index = listeners.indexOf(listener);
     if (index != -1) {
       listeners.splice(index, 1);
       if (DEBUG) debug("Unregistered listener: " + listener);
     }
   },
 
-  registerMobileConnectionMsg: function registerMobileConnectionMsg(listener) {
+  registerMobileConnectionMsg: function registerMobileConnectionMsg(clientId, listener) {
     debug("Registering for mobile connection related messages");
-    this.registerListener("_mobileConnectionListeners", listener);
+    this.registerListener("_mobileConnectionListeners", clientId, listener);
     cpmm.sendAsyncMessage("RIL:RegisterMobileConnectionMsg");
   },
 
-  unregisterMobileConnectionMsg: function unregisteMobileConnectionMsg(listener) {
-    this.unregisterListener("_mobileConnectionListeners", listener);
+  unregisterMobileConnectionMsg: function unregisteMobileConnectionMsg(clientId, listener) {
+    this.unregisterListener("_mobileConnectionListeners", clientId, listener);
   },
 
   registerVoicemailMsg: function registerVoicemailMsg(listener) {
     debug("Registering for voicemail-related messages");
-    this.registerListener("_voicemailListeners", listener);
+    //TODO: Bug 814634 - WebVoicemail API: support multiple sim cards.
+    this.registerListener("_voicemailListeners", 0, listener);
     cpmm.sendAsyncMessage("RIL:RegisterVoicemailMsg");
   },
 
   unregisterVoicemailMsg: function unregisteVoicemailMsg(listener) {
-    this.unregisterListener("_voicemailListeners", listener);
+    //TODO: Bug 814634 - WebVoicemail API: support multiple sim cards.
+    this.unregisterListener("_voicemailListeners", 0, listener);
   },
 
   registerCellBroadcastMsg: function registerCellBroadcastMsg(listener) {
     debug("Registering for Cell Broadcast related messages");
-    this.registerListener("_cellBroadcastListeners", listener);
+    //TODO: Bug 921326 - Cellbroadcast API: support multiple sim cards
+    this.registerListener("_cellBroadcastListeners", 0, listener);
     cpmm.sendAsyncMessage("RIL:RegisterCellBroadcastMsg");
   },
 
   unregisterCellBroadcastMsg: function unregisterCellBroadcastMsg(listener) {
-    this.unregisterListener("_cellBroadcastListeners", listener);
+    //TODO: Bug 921326 - Cellbroadcast API: support multiple sim cards
+    this.unregisterListener("_cellBroadcastListeners", 0, listener);
   },
 
   registerIccMsg: function registerIccMsg(listener) {
     debug("Registering for ICC related messages");
-    this.registerListener("_iccListeners", listener);
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    this.registerListener("_iccListeners", 0, listener);
     cpmm.sendAsyncMessage("RIL:RegisterIccMsg");
   },
 
   unregisterIccMsg: function unregisterIccMsg(listener) {
-    this.unregisterListener("_iccListeners", listener);
+    //TODO: Bug 814637 - WebIccManager API: support multiple sim cards.
+    this.unregisterListener("_iccListeners", 0, listener);
   },
 
   // nsIObserver
 
   observe: function observe(subject, topic, data) {
     if (topic == "xpcom-shutdown") {
       this.destroyDOMRequestHelper();
       Services.obs.removeObserver(this, "xpcom-shutdown");
@@ -1473,64 +1513,72 @@ RILContentHelper.prototype = {
     Services.DOMRequest.fireDetailedError(request, detailedError);
   },
 
   receiveMessage: function receiveMessage(msg) {
     let request;
     debug("Received message '" + msg.name + "': " + JSON.stringify(msg.json));
 
     let data = msg.json.data;
+    let clientId = msg.json.clientId;
     switch (msg.name) {
       case "RIL:CardStateChanged":
-        if (this.rilContext.cardState != data.cardState) {
-          this.rilContext.cardState = data.cardState;
-          this._deliverEvent("_iccListeners",
+        if (this.rilContexts[clientId].cardState != data.cardState) {
+          this.rilContexts[clientId].cardState = data.cardState;
+          this._deliverEvent(clientId,
+                             "_iccListeners",
                              "notifyCardStateChanged",
                              null);
         }
         break;
       case "RIL:IccInfoChanged":
-        this.updateIccInfo(data);
-        this._deliverEvent("_iccListeners", "notifyIccInfoChanged", null);
+        this.updateIccInfo(clientId, data);
+        this._deliverEvent(clientId,
+                           "_iccListeners",
+                           "notifyIccInfoChanged",
+                           null);
         break;
       case "RIL:VoiceInfoChanged":
         this.updateConnectionInfo(data,
-                                  this.rilContext.voiceConnectionInfo);
-        this._deliverEvent("_mobileConnectionListeners",
+                                  this.rilContexts[clientId].voiceConnectionInfo);
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyVoiceChanged",
                            null);
         break;
       case "RIL:DataInfoChanged":
         this.updateConnectionInfo(data,
-                                  this.rilContext.dataConnectionInfo);
-        this._deliverEvent("_mobileConnectionListeners",
+                                  this.rilContexts[clientId].dataConnectionInfo);
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyDataChanged",
                            null);
         break;
       case "RIL:OtaStatusChanged":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyOtaStatusChanged",
                            [data]);
         break;
       case "RIL:GetAvailableNetworks":
         this.handleGetAvailableNetworks(data);
         break;
       case "RIL:NetworkSelectionModeChanged":
-        this.rilContext.networkSelectionMode = data.mode;
+        this.rilContexts[clientId].networkSelectionMode = data.mode;
         break;
       case "RIL:SelectNetwork":
-        this.handleSelectNetwork(data,
+        this.handleSelectNetwork(clientId, data,
                                  RIL.GECKO_NETWORK_SELECTION_MANUAL);
         break;
       case "RIL:SelectNetworkAuto":
-        this.handleSelectNetwork(data,
+        this.handleSelectNetwork(clientId, data,
                                  RIL.GECKO_NETWORK_SELECTION_AUTOMATIC);
         break;
       case "RIL:VoicemailNotification":
-        this.handleVoicemailNotification(data);
+        this.handleVoicemailNotification(clientId, data);
         break;
       case "RIL:VoicemailInfoChanged":
         this.updateInfo(data, this.voicemailInfo);
         break;
       case "RIL:CardLockResult": {
         let requestId = data.requestId;
         let requestWindow = this._windowsMap[requestId];
         delete this._windowsMap[requestId];
@@ -1555,30 +1603,31 @@ RILContentHelper.prototype = {
         if (data.success) {
           let result = new MobileIccCardLockRetryCount(data);
           this.fireRequestSuccess(data.requestId, result);
         } else {
           this.fireRequestError(data.requestId, data.errorMsg);
         }
         break;
       case "RIL:USSDReceived":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyUssdReceived",
                            [data.message, data.sessionEnded]);
         break;
       case "RIL:SendMMI":
       case "RIL:CancelMMI":
         this.handleSendCancelMMI(data);
         break;
       case "RIL:StkCommand":
-        this._deliverEvent("_iccListeners", "notifyStkCommand",
+        this._deliverEvent(clientId, "_iccListeners", "notifyStkCommand",
                            [JSON.stringify(data)]);
         break;
       case "RIL:StkSessionEnd":
-        this._deliverEvent("_iccListeners", "notifyStkSessionEnd", null);
+        this._deliverEvent(clientId, "_iccListeners", "notifyStkSessionEnd", null);
         break;
       case "RIL:IccOpenChannel":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
                                  data.channel);
         break;
       case "RIL:IccCloseChannel":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
@@ -1587,74 +1636,77 @@ RILContentHelper.prototype = {
         break;
       case "RIL:ReadIccContacts":
         this.handleReadIccContacts(data);
         break;
       case "RIL:UpdateIccContact":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:DataError":
-        this.updateConnectionInfo(data, this.rilContext.dataConnectionInfo);
-        this._deliverEvent("_mobileConnectionListeners", "notifyDataError",
+        this.updateConnectionInfo(data, this.rilContexts[clientId].dataConnectionInfo);
+        this._deliverEvent(clientId, "_mobileConnectionListeners", "notifyDataError",
                            [data.errorMsg]);
         break;
-      case "RIL:GetCallForwardingOption":
-        this.handleGetCallForwardingOption(data);
+      case "RIL:GetCallForwardingOptions":
+        this.handleGetCallForwardingOptions(data);
         break;
-      case "RIL:SetCallForwardingOption":
+      case "RIL:SetCallForwardingOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
-      case "RIL:GetCallBarringOption":
-        this.handleGetCallBarringOption(data);
+      case "RIL:GetCallBarringOptions":
+        this.handleGetCallBarringOptions(data);
         break;
-      case "RIL:SetCallBarringOption":
+      case "RIL:SetCallBarringOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:ChangeCallBarringPassword":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
-      case "RIL:GetCallWaitingOption":
+      case "RIL:GetCallWaitingOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
                                  data.enabled);
         break;
-      case "RIL:SetCallWaitingOption":
+      case "RIL:SetCallWaitingOptions":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:CfStateChanged":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyCFStateChange",
                            [data.success, data.action,
                             data.reason, data.number,
                             data.timeSeconds, data.serviceClass]);
         break;
       case "RIL:GetCallingLineIdRestriction":
         this.handleGetCallingLineIdRestriction(data);
         break;
       case "RIL:SetCallingLineIdRestriction":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:CellBroadcastReceived": {
         let message = new CellBroadcastMessage(data);
-        this._deliverEvent("_cellBroadcastListeners",
+        this._deliverEvent(clientId,
+                           "_cellBroadcastListeners",
                            "notifyMessageReceived",
                            [message]);
         break;
       }
       case "RIL:SetRoamingPreference":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:GetRoamingPreference":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
                                  data.mode);
         break;
       case "RIL:ExitEmergencyCbMode":
         this.handleExitEmergencyCbMode(data);
         break;
       case "RIL:EmergencyCbModeChanged":
-        this._deliverEvent("_mobileConnectionListeners",
+        this._deliverEvent(clientId,
+                           "_mobileConnectionListeners",
                            "notifyEmergencyCbModeChanged",
                            [data.active, data.timeoutMs]);
         break;
       case "RIL:SetVoicePrivacyMode":
         this.handleSimpleRequest(data.requestId, data.errorMsg, null);
         break;
       case "RIL:GetVoicePrivacyMode":
         this.handleSimpleRequest(data.requestId, data.errorMsg,
@@ -1685,19 +1737,19 @@ RILContentHelper.prototype = {
       let info = new MobileNetworkInfo();
       this.updateInfo(network, info);
       networks[i] = info;
     }
 
     this.fireRequestSuccess(message.requestId, networks);
   },
 
-  handleSelectNetwork: function handleSelectNetwork(message, mode) {
-    this._selectingNetwork = null;
-    this.rilContext.networkSelectionMode = mode;
+  handleSelectNetwork: function handleSelectNetwork(clientId, message, mode) {
+    this._selectingNetworks[clientId] = null;
+    this.rilContexts[clientId].networkSelectionMode = mode;
 
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
     } else {
       this.fireRequestSuccess(message.requestId, null);
     }
   },
 
@@ -1736,17 +1788,18 @@ RILContentHelper.prototype = {
       contact.id = message.iccid + c.recordId;
       return contact;
     });
 
     this.fireRequestSuccess(message.requestId,
                             ObjectWrapper.wrap(result, window));
   },
 
-  handleVoicemailNotification: function handleVoicemailNotification(message) {
+  handleVoicemailNotification: function handleVoicemailNotification(clientId, message) {
+    // TODO: Bug 818352 - B2G Multi-SIM: voicemail - add subscription id in nsIRILContentHelper
     let changed = false;
     if (!this.voicemailStatus) {
       this.voicemailStatus = new VoicemailStatus();
     }
 
     if (this.voicemailStatus.hasMessages != message.active) {
       changed = true;
       this.voicemailStatus.hasMessages = message.active;
@@ -1766,47 +1819,48 @@ RILContentHelper.prototype = {
     }
 
     if (this.voicemailStatus.returnMessage != message.returnMessage) {
       changed = true;
       this.voicemailStatus.returnMessage = message.returnMessage;
     }
 
     if (changed) {
-      this._deliverEvent("_voicemailListeners",
+      this._deliverEvent(clientId,
+                         "_voicemailListeners",
                          "notifyStatusChanged",
                          [this.voicemailStatus]);
     }
   },
 
   _cfRulesToMobileCfInfo: function _cfRulesToMobileCfInfo(rules) {
     for (let i = 0; i < rules.length; i++) {
       let rule = rules[i];
       let info = new MobileCFInfo();
       this.updateInfo(rule, info);
       rules[i] = info;
     }
   },
 
-  handleGetCallForwardingOption: function handleGetCallForwardingOption(message) {
+  handleGetCallForwardingOptions: function handleGetCallForwardingOptions(message) {
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
       return;
     }
 
     this._cfRulesToMobileCfInfo(message.rules);
     this.fireRequestSuccess(message.requestId, message.rules);
   },
 
-  handleGetCallBarringOption: function handleGetCallBarringOption(message) {
+  handleGetCallBarringOptions: function handleGetCallBarringOptions(message) {
     if (!message.success) {
       this.fireRequestError(message.requestId, message.errorMsg);
     } else {
-      let option = new CallBarringOption(message);
-      this.fireRequestSuccess(message.requestId, option);
+      let options = new CallBarringOptions(message);
+      this.fireRequestSuccess(message.requestId, options);
     }
   },
 
   handleGetCallingLineIdRestriction:
     function handleGetCallingLineIdRestriction(message) {
     if (message.errorMsg) {
       this.fireRequestError(message.requestId, message.errorMsg);
       return;
@@ -1872,18 +1926,21 @@ RILContentHelper.prototype = {
       let mmiError = new requestWindow.DOMMMIError(result.serviceCode,
                                                    message.errorMsg,
                                                    null,
                                                    result.additionalInformation);
       Services.DOMRequest.fireDetailedError(request, mmiError);
     }
   },
 
-  _deliverEvent: function _deliverEvent(listenerType, name, args) {
-    let thisListeners = this[listenerType];
+  _deliverEvent: function _deliverEvent(clientId, listenerType, name, args) {
+    if (!this[listenerType]) {
+      return;
+    }
+    let thisListeners = this[listenerType][clientId];
     if (!thisListeners) {
       return;
     }
 
     let listeners = thisListeners.slice();
     for (let listener of listeners) {
       if (thisListeners.indexOf(listener) == -1) {
         continue;
@@ -1944,28 +2001,28 @@ RILContentHelper.prototype = {
       case Ci.nsIDOMMozMobileConnection.CALL_BARRING_PROGRAM_INCOMING_ROAMING:
         return true;
       default:
         return false;
     }
   },
 
   /**
-   * Helper for guarding us against invalid option for call barring.
+   * Helper for guarding us against invalid options for call barring.
    */
-  _isValidCallBarringOption:
-      function _isValidCallBarringOption(option, usedForSetting) {
-    if (!option ||
-        option.serviceClass == null ||
-        !this._isValidCallBarringProgram(option.program)) {
+  _isValidCallBarringOptions:
+      function _isValidCallBarringOptions(options, usedForSetting) {
+    if (!options ||
+        options.serviceClass == null ||
+        !this._isValidCallBarringProgram(options.program)) {
       return false;
     }
 
-    // For setting callbarring option, |enabled| and |password| are required.
-    if (usedForSetting && (option.enabled == null || option.password == null)) {
+    // For setting callbarring options, |enabled| and |password| are required.
+    if (usedForSetting && (options.enabled == null || options.password == null)) {
       return false;
     }
 
     return true;
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILContentHelper,
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -85,23 +85,23 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES
   "RIL:GetNumRadioInterfaces",
   "RIL:GetRilContext",
   "RIL:GetAvailableNetworks",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:SendMMI",
   "RIL:CancelMMI",
   "RIL:RegisterMobileConnectionMsg",
-  "RIL:SetCallForwardingOption",
-  "RIL:GetCallForwardingOption",
-  "RIL:SetCallBarringOption",
-  "RIL:GetCallBarringOption",
+  "RIL:SetCallForwardingOptions",
+  "RIL:GetCallForwardingOptions",
+  "RIL:SetCallBarringOptions",
+  "RIL:GetCallBarringOptions",
   "RIL:ChangeCallBarringPassword",
-  "RIL:SetCallWaitingOption",
-  "RIL:GetCallWaitingOption",
+  "RIL:SetCallWaitingOptions",
+  "RIL:GetCallWaitingOptions",
   "RIL:SetCallingLineIdRestriction",
   "RIL:GetCallingLineIdRestriction",
   "RIL:SetRoamingPreference",
   "RIL:GetRoamingPreference",
   "RIL:ExitEmergencyCbMode",
   "RIL:SetVoicePrivacyMode",
   "RIL:GetVoicePrivacyMode"
 ];
@@ -930,35 +930,35 @@ RadioInterface.prototype = {
         this.workerMessenger.sendWithIPCMessage(msg, "iccExchangeAPDU");
         break;
       case "RIL:ReadIccContacts":
         this.workerMessenger.sendWithIPCMessage(msg, "readICCContacts");
         break;
       case "RIL:UpdateIccContact":
         this.workerMessenger.sendWithIPCMessage(msg, "updateICCContact");
         break;
-      case "RIL:SetCallForwardingOption":
-        this.setCallForwardingOption(msg.target, msg.json.data);
+      case "RIL:SetCallForwardingOptions":
+        this.setCallForwardingOptions(msg.target, msg.json.data);
         break;
-      case "RIL:GetCallForwardingOption":
+      case "RIL:GetCallForwardingOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "queryCallForwardStatus");
         break;
-      case "RIL:SetCallBarringOption":
+      case "RIL:SetCallBarringOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "setCallBarring");
         break;
-      case "RIL:GetCallBarringOption":
+      case "RIL:GetCallBarringOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "queryCallBarringStatus");
         break;
       case "RIL:ChangeCallBarringPassword":
         this.workerMessenger.sendWithIPCMessage(msg, "changeCallBarringPassword");
         break;
-      case "RIL:SetCallWaitingOption":
+      case "RIL:SetCallWaitingOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "setCallWaiting");
         break;
-      case "RIL:GetCallWaitingOption":
+      case "RIL:GetCallWaitingOptions":
         this.workerMessenger.sendWithIPCMessage(msg, "queryCallWaiting");
         break;
       case "RIL:SetCallingLineIdRestriction":
         this.setCallingLineIdRestriction(msg.target, msg.json.data);
         break;
       case "RIL:GetCallingLineIdRestriction":
         this.workerMessenger.sendWithIPCMessage(msg, "getCLIR");
         break;
@@ -2401,22 +2401,22 @@ RadioInterface.prototype = {
       target.sendAsyncMessage("RIL:SendMMI", {
         clientId: this.clientId,
         data: response
       });
       return false;
     }).bind(this));
   },
 
-  setCallForwardingOption: function setCallForwardingOption(target, message) {
-    if (DEBUG) this.debug("setCallForwardingOption: " + JSON.stringify(message));
+  setCallForwardingOptions: function setCallForwardingOptions(target, message) {
+    if (DEBUG) this.debug("setCallForwardingOptions: " + JSON.stringify(message));
     message.serviceClass = RIL.ICC_SERVICE_CLASS_VOICE;
     this.workerMessenger.send("setCallForward", message, (function(response) {
       this._sendCfStateChanged(response);
-      target.sendAsyncMessage("RIL:SetCallForwardingOption", {
+      target.sendAsyncMessage("RIL:SetCallForwardingOptions", {
         clientId: this.clientId,
         data: response
       });
       return false;
     }).bind(this));
   },
 
   setCallingLineIdRestriction: function setCallingLineIdRestriction(target,
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -21,16 +21,17 @@ const WIFIWORKER_CID        = Components
 
 const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
 
 const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
 const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+const MAX_RETRIES_ON_DHCP_FAILURE = 2;
 
 // Settings DB path for wifi
 const SETTINGS_WIFI_ENABLED            = "wifi.enabled";
 const SETTINGS_WIFI_DEBUG_ENABLED      = "wifi.debugging.enabled";
 // Settings DB path for Wifi tethering.
 const SETTINGS_WIFI_TETHERING_ENABLED  = "tethering.wifi.enabled";
 const SETTINGS_WIFI_SSID               = "tethering.wifi.ssid";
 const SETTINGS_WIFI_SECURITY_TYPE      = "tethering.wifi.security.type";
@@ -553,16 +554,31 @@ var WifiManager = (function() {
           (key in staticIpConfig) &&
           staticIpConfig[key].enabled) {
           debug("Run static ip");
           runStaticIp(manager.ifname, key);
           return;
       }
       netUtil.runDhcp(manager.ifname, function(data) {
         dhcpInfo = data.info;
+        if (!dhcpInfo) {
+          if (++manager.dhcpFailuresCount >= MAX_RETRIES_ON_DHCP_FAILURE) {
+            manager.dhcpFailuresCount = 0;
+            notify("disconnected", {ssid: manager.connectionInfo.ssid});
+            return;
+          }
+          // NB: We have to call disconnect first. Otherwise, we only reauth with
+          // the existing AP and don't retrigger DHCP.
+          manager.disconnect(function() {
+            manager.reassociate(function(){});
+          });
+          return;
+        }
+
+        manager.dhcpFailuresCount = 0;
         notify("networkconnected", data);
       });
     });
   }
 
   var supplicantStatesMap = (sdkVersion >= 15) ?
     ["DISCONNECTED", "INTERFACE_DISABLED", "INACTIVE", "SCANNING",
      "AUTHENTICATING", "ASSOCIATING", "ASSOCIATED", "FOUR_WAY_HANDSHAKE",
@@ -784,16 +800,17 @@ var WifiManager = (function() {
   // Initial state.
   manager.state = "UNINITIALIZED";
   manager.tetheringState = "UNINITIALIZED";
   manager.enabled = false;
   manager.supplicantStarted = false;
   manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
   manager.authenticationFailuresCount = 0;
   manager.loopDetectionCount = 0;
+  manager.dhcpFailuresCount = 0;
 
   var waitForDriverReadyTimer = null;
   function cancelWaitForDriverReadyTimer() {
     if (waitForDriverReadyTimer) {
       waitForDriverReadyTimer.cancel();
       waitForDriverReadyTimer = null;
     }
   };
@@ -1818,44 +1835,41 @@ function WifiWorker() {
         // about getting stuck while scanning.
         if (!WifiManager.backgroundScanEnabled && WifiManager.enabled)
           startScanStuckTimer();
         break;
     }
   };
 
   WifiManager.onnetworkconnected = function() {
-    if (this.info) {
-      WifiNetworkInterface.state =
-        Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
-      WifiNetworkInterface.ip = this.info.ipaddr_str;
-      WifiNetworkInterface.netmask = this.info.mask_str;
-      WifiNetworkInterface.broadcast = this.info.broadcast_str;
-      WifiNetworkInterface.gateway = this.info.gateway_str;
-      WifiNetworkInterface.dns1 = this.info.dns1_str;
-      WifiNetworkInterface.dns2 = this.info.dns2_str;
-      Services.obs.notifyObservers(WifiNetworkInterface,
-                                   kNetworkInterfaceStateChangedTopic,
-                                   null);
-
-      self.ipAddress = this.info.ipaddr_str;
-
-      // We start the connection information timer when we associate, but
-      // don't have our IP address until here. Make sure that we fire a new
-      // connectionInformation event with the IP address the next time the
-      // timer fires.
-      self._lastConnectionInfo = null;
-      self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
-    } else {
-      // NB: We have to call disconnect first. Otherwise, we only reauth with
-      // the existing AP and don't retrigger DHCP.
-      WifiManager.disconnect(function() {
-        WifiManager.reassociate(function(){});
-      });
+    if (!this.info) {
+      debug("Network information is invalid.");
+      return;
     }
+
+    WifiNetworkInterface.state =
+      Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
+    WifiNetworkInterface.ip = this.info.ipaddr_str;
+    WifiNetworkInterface.netmask = this.info.mask_str;
+    WifiNetworkInterface.broadcast = this.info.broadcast_str;
+    WifiNetworkInterface.gateway = this.info.gateway_str;
+    WifiNetworkInterface.dns1 = this.info.dns1_str;
+    WifiNetworkInterface.dns2 = this.info.dns2_str;
+    Services.obs.notifyObservers(WifiNetworkInterface,
+                                 kNetworkInterfaceStateChangedTopic,
+                                 null);
+
+    self.ipAddress = this.info.ipaddr_str;
+
+    // We start the connection information timer when we associate, but
+    // don't have our IP address until here. Make sure that we fire a new
+    // connectionInformation event with the IP address the next time the
+    // timer fires.
+    self._lastConnectionInfo = null;
+    self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
   };
 
   WifiManager.onscanresultsavailable = function() {
     if (self._scanStuckTimer) {
       // We got scan results! We must not be stuck for now, try again.
       self._scanStuckTimer.cancel();
       self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
                                             Ci.nsITimer.TYPE_ONE_SHOT);
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
@@ -9116,476 +9116,496 @@ 12152c17961
 12432c18241
 < assignees
 ---
 > assignee/MS
 12685c18494
 < auteur
 ---
 > auteur/MS
-12754a18564
+12713a18523
+> autocomplete/S
+12754a18565
 > avant-garde
-12827d18636
+12827d18637
 < awol
-12829a18639
+12829a18640
 > axe/M
-13864a19675
+13864a19676
 > biodiesel/M
-14210d20020
+14210d20021
 < blueing's
-14434a20245
+14434a20246
 > bookselling
-14841c20652
+14841c20653
 < broadcast/AMGS
 ---
 > broadcast/AMGSD
-15039,15040c20850
+15039,15040c20851
 < bullshitter's
 < bullshitter/S!
 ---
 > bullshitter/SM!
-15230d21039
+15230d21040
 < byelaw/SM
-15383d21191
+15383d21192
 < callisthenics/M
-15460a21269,21271
+15460a21270,21272
 > cancelled/U
 > canceller/M
 > cancelling
-15559a21371
+15559a21372
 > capita
-15629,15630d21440
+15629,15630d21441
 < carburetter/SM
 < carburettor/SM
-15701a21512
+15701a21513
 > carnitas
-15788d21598
+15788d21599
 < cashpoint/S
-15797d21606
+15797d21607
 < cassino/M
-15832a21642
+15832a21643
 > catalyses
-15940d21749
+15940d21750
 < caviare/M
-16372c22181
+16372c22182
 < chickenshit/S!
 ---
 > chickenshit/SM!
-16404c22213
+16404c22214
 < children
 ---
 > children/M
-16488d22296
+16488d22297
 < chlorophyl/M
-16629,16630c22437
+16629,16630c22438
 < cider's
 < cider/S
 ---
 > cider/MS
-17072d22878
+17072d22879
 < cocain/M
-17102,17103c22908
+17102,17103c22909
 < cocksucker's
 < cocksucker/S!
 ---
 > cocksucker/SM!
-17755c23560
+17755c23561
 < confer/S
 ---
 > confer/SB
-17800a23606
+17800a23607
 > conformant
-18151d23956
+18151d23957
 < convenor/S
-18206c24011
+18206c24012
 < cookie/M
 ---
 > cookie/SM
-18467a24273
+18467a24274
 > could've
-19035a24842
+19035a24843
 > cul-de-sac
-19246c25053
+19246c25054
 < cysteine
 ---
 > cysteine/M
-20196,20197c26003,26004
+20196,20197c26004,26005
 < dialog/SM
 < dialogue/SM
 ---
 > dialog/SMGD
 > dialogue/SMRGD
-20481a26289
+20481a26290
 > disclose/DSG
-20830c26638
+20830c26639
 < dogie/M
 ---
 > dogie/SM
-20895a26704
+20895a26705
 > donator/MS
-21820a27630
+21820a27631
 > elicitor/MS
-22071a27882
+22071a27883
 > encyclopaedia
-22556a28368
+22556a28369
 > estoppel
-22638c28450
+22638c28451
 < euthanize
 ---
 > euthanize/DSG
-22719a28532
+22719a28533
 > exabyte/MS
-22947a28761
+22947a28762
 > experimentalism
-23207,23208d29020
+23207,23208d29021
 < faecal
 < faeces/M
-23215c29027
+23215c29028
 < faggoting's
 ---
 > faggot/SMG
-23701a29514
+23701a29515
 > filesystem/MS
-24155c29968
+24155c29969
 < fluidized
 ---
 > fluidize/DSG
-24216a30030
+24216a30031
 > foci
-24736d30549
+24736d30550
 < frier/M
-24855,24856c30668,30669
+24855,24856c30669,30670
 < fucker/M!
 < fuckhead/S!
 ---
 > fucker/SM!
 > fuckhead/SM!
-24953d30765
+24953d30766
 < furore/MS
-25125c30937
+25125c30938
 < gaolbird/S
 ---
 > gaolbirds
-25180d30991
+25180d30992
 < gasolene/M
-25190a31002
+25190a31003
 > gastroenterologist/M
-25262c31074
+25262c31075
 < geezer/M
 ---
 > geezer/MS
-25327c31139
+25327c31140
 < genomic
 ---
 > genomic/S
-25462a31275
+25462a31276
 > gigabit/MS
-25464a31278,31280
+25464a31279,31281
 > gigajoule/MS
 > gigapixel/MS
 > gigawatt/MS
-25560d31375
+25560d31376
 < glamourize/DSG
-25674c31489
+25674c31490
 < glycerine's
 ---
 > glycerine/M
-25905c31720
+25905c31721
 < gram/MS
 ---
 > gram/KMS
-25909d31723
+25909d31724
 < gramme/SM
-26063c31877,31878
+26063c31878,31879
 < greybeard
 ---
 > grey/MDRTGSP
 > greybeard/SM
-26066c31881
+26066c31882
 < greyness
 ---
 > greyness/M
-26246,26247d32060
+26246,26247d32061
 < guerilla's
 < guerillas
-26432,26436d32244
+26432,26436d32245
 < haemoglobin's
 < haemophilia/M
 < haemorrhage/DSMG
 < haemorrhoid/S
 < haemorrhoids/M
-27167c32975
+27167c32976
 < hexane
 ---
 > hexane/SM
-27273a33082
+27273a33083
 > hippopotami
-27875d33683
+27875d33684
 < hyaena/SM
-28017c33825
+28017c33826
 < iPod/M
 ---
 > iPod/MS
-28105a33914
+28105a33915
 > idolator/SM
-28513c34322
+28513c34323
 < inbound
 ---
 > inbound/s
-28650a34460
+28650a34461
 > indices
-28812d34621
+28812d34622
 < inflexion/SM
-29216a35026
+29216a35027
 > intern/GDL
-29272a35083,35086
+29266a35078
+> interruptible/U
+29272a35085,35088
 > intersex
 > intersexual/MS
 > intersexualism
 > intersexuality
-29724c35538
+29724c35540
 < jewellery's
 ---
 > jewellery/M
-29870a35685
+29870a35687
 > judgement/MS
-30066c35881
+30066c35883
 < kiddie/M
 ---
 > kiddie/SM
-30262,30263c36077
+30262,30263c36079
 < kraut's
 < kraut/S!
 ---
 > kraut/MS!
-30665a36480
+30665a36482
 > lector/MS
-31031c36846
+31031c36848
 < linguini's
 ---
 > linguini/M
-31151,31152c36966
+31151,31152c36968
 < liver's
 < liver/S
 ---
 > liver/MS
-32230c38044
+32230c38046
 < meanie/M
 ---
 > meanie/MS
-32317,32318c38131
+32317,32318c38133
 < megadeath/M
 < megadeaths
 ---
 > megadeath/SM
-32320c38133
+32320c38135
 < megajoules
 ---
 > megajoule/SM
-32329c38142
+32329c38144
 < megapixel/S
 ---
 > megapixel/MS
-32708a38522
+32708a38524
 > might've
-32777d38590
+32717a38534
+> migrator/SM
+32777d38593
 < millionnaire/M
-32934a38748
+32934a38751
 > miscommunication/S
-32991a38806
+32991a38809
 > misjudgement/MS
-33784a39600
+33784a39603
 > must've
-33963c39779
+33963c39782
 < native/MS
 ---
 > native/MSY
-34169,34171c39985,39986
+34169,34171c39988,39989
 < neurone/S
 < neurophysiology
 < neuroscience
 ---
 > neurophysiology/M
 > neuroscience/MS
-34275c40090
+34275c40093
 < nightie/M
 ---
 > nightie/SM
-35104a40920
+35104a40923
 > octopi
-35219d41034
+35219d41037
 < oleomargarin/M
-35226a41042
+35226a41045
 > oligo
-35913c41729
+35913c41732
 < oversize/D
 ---
 > oversize
-36056,36059d41871
+36056,36059d41874
 < paederast/S
 < paediatrician's
 < paediatricians
 < paediatrics/M
-36291a42104
+36291a42107
 > paralyses
-36403d42215
+36403d42218
 < parrakeet/MS
-36449d42260
+36449d42263
 < partizan/SM
-37093a42905
+37093a42908
 > petabyte/MS
-37102c42914
+37102c42917
 < petitioner/M
 ---
 > petitioner/MS
-37264a43077
+37264a43080
 > phosphorylate/DSGN
-37316d43128
+37316d43131
 < phrenetic
-37796a43609
+37796a43612
 > plugin/MS
-37987c43800
+37987c43803
 < polypeptide/S
 ---
 > polypeptide/MS
-38291d44103
+38291d44106
 < practise's
-38451a44264
+38451a44267
 > prejudgement/MS
-38891a44705,44706
+38805a44622
+> profiler/SM
+38835a44653
+> programmatically
+38891a44710,44711
 > pronate/DSGN
 > pronator/MS
-38951c44766
+38951c44771
 < proprietorship/M
 ---
 > proprietorship/MS
-39039a44855
+39039a44860
 > provender/M
-39564a45381
+39564a45386
 > quinoa
-40036a45854
+40036a45859
 > recency
-40140a45959
+40140a45964
 > recurse/DGSV
-40141a45961
+40141a45966
 > recuse/DGS
-40208a46029
+40208a46034
 > refactor/SMDG
-40244d46064
+40244d46069
 < reflexion/SM
-40829c46649
+40659d46483
+< resizing
+40829c46653
 < reverie/M
 ---
 > reverie/MS
-41415a47236
+41415a47240
 > sabre/MS
-41914c47735
+41914c47739
 < schnaps's
 ---
 > schnaps/M
-41949c47770
+41949c47774
 < schrod's
 ---
 > schrod/SM
-41998a47820
+41998a47824
 > scot-free
-42883,42885c48705
+42883,42885c48709
 < shit's
 < shit/S!
 < shite/S!
 ---
 > shit/MS!
-42887,42888c48707,48708
+42887,42888c48711,48712
 < shithead/S!
 < shitload/!
 ---
 > shithead/MS!
 > shitload/MS!
-42891c48711
+42891c48715
 < shitty/RT!
 ---
 > shitty/TR!
-42976a48797
+42976a48801
 > should've
-43008c48829
+43008c48833
 < showtime
 ---
 > showtime/MS
-43724,43726c49545
+43328c49153
+< size/MGBDRS
+---
+> size/AMGBDRS
+43724,43726c49549
 < smoulder's
 < smouldered
 < smoulders
 ---
 > smoulder/GSMD
-44062c49881
+44062c49885
 < sonofabitch
 ---
 > sonofabitch/!
-44346a50166
+44346a50170
 > spelled
-44348a50169
+44348a50173
 > spelt
-44371a50193
+44371a50197
 > spick/S!
-44383c50205
+44383c50209
 < spik/S
 ---
 > spik/S!
-46106a51929
+46106a51933
 > syllabi
-46160c51983
+46160c51987
 < synch/GMD
 ---
 > synch/GMDS
-46167d51989
+46167d51993
 < synchs
-46203,46204c52025,52026
+46203,46204c52029,52030
 < sysadmin/S
 < sysop/S
 ---
 > sysadmin/MS
 > sysop/MS
-46752a52575
+46752a52579
 > terabit/MS
-46753a52577,52578
+46753a52581,52582
 > terahertz/M
 > terapixel/MS
-46817a52643
+46817a52647
 > testcase/MS
-46831a52658
+46831a52662
 > testsuite/MS
-46925a52753
+46925a52757
 > theremin/MS
-47755a53584
+47455c53287
+< toolbar
+---
+> toolbar/MS
+47755a53588
 > transfect/DSMG
-47774a53604,53605
+47774a53608,53609
 > transgenderism
 > transgene/MS
-47951c53782
+47951c53786
 < triage/M
 ---
 > triage/MG
-48869a54701
+48869a54705
 > unlikeable
-49211c55043
+49211c55047
 < vagina/M
 ---
 > vagina/MS
-49368,49369c55200
+49368,49369c55204
 < velour's
 < velours's
 ---
 > velour/MS
-49478a55310
+49478a55314
 > vertices
-50148a55981
+50148a55985
 > weaponize/DSG
-50260,50261d56092
+50260,50261d56096
 < werwolf/M
 < werwolves
-50728c56559
+50728c56563
 < women
 ---
 > women/M
-50794c56625
+50794c56629
 < wop/S!
 ---
 > wop/MS!
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-57447
+57451
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -18766,16 +18766,17 @@ autism/M
 autistic
 auto/MS
 autobahn/SM
 autobiographer/SM
 autobiographic
 autobiographical/Y
 autobiography/SM
 autoclave/MS
+autocomplete/S
 autocracy/SM
 autocrat/SM
 autocratic
 autocratically
 autocross
 autocue
 autodidact/MS
 autograph/MDG
@@ -35362,16 +35363,17 @@ interring/E
 interrogate/GNVDSX
 interrogation/M
 interrogative/MYS
 interrogator/SM
 interrogatory/SM
 interrupt/ZGMDRS
 interrupter/M
 interruptibility
+interruptible/U
 interruption/MS
 interscholastic
 intersect/GDS
 intersection/SM
 intersession/MS
 intersex
 intersexual/MS
 intersexualism
@@ -38829,16 +38831,17 @@ mightily
 mightiness/M
 mightn't
 mighty/TRP
 mignonette/SM
 migraine/MS
 migrant/MS
 migrate/AGDS
 migration/SM
+migrator/SM
 migratory
 mikado/MS
 mike/MGDS
 mil/SZMR
 milady/SM
 milch
 mild/MRYTP
 mildew/SMDG
@@ -44936,16 +44939,17 @@ professionalism/M
 professionalization
 professionalize/DSG
 professor/SM
 professorial/Y
 professorship/MS
 proffer/GMDS
 proficiency/SM
 proficient/MYS
+profiler/SM
 profit/BGD
 profitability/M
 profitable/U
 profitably/U
 profiteer/MDSG
 profiteering/M
 profiterole/MS
 profitless
@@ -44966,16 +44970,17 @@ prognosis/M
 prognostic/MS
 prognosticate/XDSGN
 prognostication/M
 prognosticator/MS
 program/ZGMDRS
 programer/M
 programmable/SM
 programmatic
+programmatically
 programmed/CA
 programmer/MS
 programming/MS
 progress/MDSGV
 progression/SM
 progressive/SMYP
 progressiveness/M
 prohibit/DGVS
@@ -46798,17 +46803,16 @@ resist/SMDRZGV
 resistance/MS
 resistant/U
 resistible
 resistive/Y
 resistivity
 resistless
 resistor/MS
 resitting
-resizing
 resold
 resole/DSG
 resolute/XPYTNR
 resoluteness/M
 resolvability
 resolvable/U
 resolve/M
 resolved/U
@@ -49483,17 +49487,17 @@ sixshooter/M
 sixteen/SMH
 sixteenth/M
 sixteenths
 sixth/MY
 sixths
 sixtieth/M
 sixtieths
 sixty/SMH
-size/MGBDRS
+size/AMGBDRS
 sizing/M
 sizzle/DRSMZG
 ska/M
 skate/MZGDRS
 skateboard/ZGSMDR
 skateboarder/M
 skateboarding/M
 skater/M
@@ -53631,17 +53635,17 @@ tonsillectomy/SM
 tonsillitis/M
 tonsorial
 tonsure/DSMG
 tony/RT
 too
 took/A
 tool's
 tool/ADGS
-toolbar
+toolbar/MS
 toolbox/MS
 toolkit
 toolmaker/MS
 toolmaking
 toot/MDRZGS
 tooter/M
 tooth/MD
 toothache/MS
--- a/mobile/android/base/tests/PixelTest.java.in
+++ b/mobile/android/base/tests/PixelTest.java.in
@@ -52,16 +52,17 @@ abstract class PixelTest extends BaseTes
         Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
         if (isPrivate) {
             selectMenuItem(StringHelper.NEW_PRIVATE_TAB_LABEL);
         } else {
             selectMenuItem(StringHelper.NEW_TAB_LABEL);
         }
         tabEventExpecter.blockForEvent();
         contentEventExpecter.blockForEvent();
+        waitForText(StringHelper.TITLE_PLACE_HOLDER);
         loadAndPaint(url);
         tabEventExpecter.unregisterListener();
         contentEventExpecter.unregisterListener();
         mAsserter.ok(waitForText(title), "Checking that the page has loaded", "The page has loaded");
     }
 
     protected final PaintedSurface waitForPaint(Actions.RepeatedEventExpecter expecter) {
         expecter.blockUntilClear(PAINT_CLEAR_DELAY);
--- a/mobile/android/base/tests/StringHelper.java.in
+++ b/mobile/android/base/tests/StringHelper.java.in
@@ -40,16 +40,18 @@ class StringHelper {
         "Bookmark Link"
     };
 
     public static final String[] BOOKMARKS_OPTIONS_CONTEXTMENU_ITEMS = new String[] {
         "Edit",
         "Add to Home Screen"
     };
 
+    public static final String TITLE_PLACE_HOLDER = "Enter Search or Address";
+
     // Robocop page urls
     // Note: please use getAbsoluteUrl(String url) on each robocop url to get the correct url
     public static final String ROBOCOP_BIG_LINK_URL = "/robocop/robocop_big_link.html";
     public static final String ROBOCOP_BIG_MAILTO_URL = "/robocop/robocop_big_mailto.html";
     public static final String ROBOCOP_BLANK_PAGE_01_URL = "/robocop/robocop_blank_01.html";
     public static final String ROBOCOP_BLANK_PAGE_02_URL = "/robocop/robocop_blank_02.html";
     public static final String ROBOCOP_BLANK_PAGE_03_URL = "/robocop/robocop_blank_03.html";
     public static final String ROBOCOP_BOXES_URL = "/robocop/robocop_boxes.html";
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3385,18 +3385,26 @@ Tab.prototype = {
           let listener = function() {
             this.browser.removeEventListener("click", ErrorPageEventHandler, true);
             this.browser.removeEventListener("pagehide", listener, true);
           }.bind(this);
 
           this.browser.addEventListener("pagehide", listener, true);
         }
 
-        if (docURI.startsWith("about:reader"))
-          new AboutReader(this.browser.contentDocument, this.browser.contentWindow);
+        if (docURI.startsWith("about:reader")) {
+          // During browser restart / recovery, duplicate "DOMContentLoaded" messages are received here
+          // For the visible tab ... where more than one tab is being reloaded, the inital "DOMContentLoaded"
+          // Message can be received before the document body is available ... so we avoid instantiating an
+          // AboutReader object, expecting that an eventual valid message will follow.
+          let contentDocument = this.browser.contentDocument;
+          if (contentDocument.body) {
+            new AboutReader(contentDocument, this.browser.contentWindow);
+          }
+        }
 
         break;
       }
 
       case "DOMFormHasPassword": {
         LoginManagerContent.onFormPassword(aEvent);
         break;
       }
@@ -4292,20 +4300,21 @@ var BrowserEventHandler = {
               [x, y] = this._moveClickPoint(element, x, y);
               element = ElementTouchHelper.anyElementFromPoint(x, y);
             }
 
             this._sendMouseEvent("mousemove", element, x, y);
             this._sendMouseEvent("mousedown", element, x, y);
             this._sendMouseEvent("mouseup",   element, x, y);
 
-            // See if its a input element
-            if ((element instanceof HTMLInputElement && element.mozIsTextField(false)) ||
-                (element instanceof HTMLTextAreaElement))
-               SelectionHandler.attachCaret(element);
+            // See if its an input element, and it isn't disabled
+            if (!element.disabled &&
+                ((element instanceof HTMLInputElement && element.mozIsTextField(false)) ||
+                (element instanceof HTMLTextAreaElement)))
+              SelectionHandler.attachCaret(element);
 
             // scrollToFocusedInput does its own checks to find out if an element should be zoomed into
             BrowserApp.scrollToFocusedInput(BrowserApp.selectedBrowser);
           } catch(e) {
             Cu.reportError(e);
           }
         }
         this._cancelTapHighlight();
@@ -5721,18 +5730,21 @@ var ViewportHandler = {
         let tabs = BrowserApp.tabs;
         for (let i = 0; i < tabs.length; i++)
           tabs[i].updateViewportSize(oldScreenWidth);
         break;
     }
   },
 
   updateMetadata: function updateMetadata(tab, aInitialLoad) {
-    let metadata = this.getViewportMetadata(tab.browser.contentWindow);
-    tab.updateViewportMetadata(metadata, aInitialLoad);
+    let contentWindow = tab.browser.contentWindow;
+    if (contentWindow.document.documentElement) {
+      let metadata = this.getViewportMetadata(contentWindow);
+      tab.updateViewportMetadata(metadata, aInitialLoad);
+    }
   },
 
   /**
    * Returns the ViewportMetadata object.
    */
   getViewportMetadata: function getViewportMetadata(aWindow) {
     let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -247,16 +247,21 @@ pref("media.peerconnection.agc_enabled",
 pref("media.peerconnection.agc", 1);
 pref("media.peerconnection.noise_enabled", false);
 pref("media.peerconnection.noise", 1);
 #else
 #ifdef ANDROID
 pref("media.navigator.enabled", true);
 #endif
 #endif
+
+pref("media.tabstreaming.width", 320);
+pref("media.tabstreaming.height", 240);
+pref("media.tabstreaming.time_per_frame", 40);
+
 // TextTrack support
 pref("media.webvtt.enabled", false);
 
 // Whether to enable MediaSource support
 pref("media.mediasource.enabled", false);
 
 #ifdef MOZ_WEBSPEECH
 pref("media.webspeech.recognition.enable", false);
--- a/toolkit/devtools/server/actors/root.js
+++ b/toolkit/devtools/server/actors/root.js
@@ -186,16 +186,27 @@ RootActor.prototype = {
   get isRootActor() true,
 
   /**
    * The (chrome) window, for use by child actors
    */
   get window() Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType),
 
   /**
+   * Getter for the best nsIWebProgress for to watching this window.
+   */
+  get webProgress() {
+    return this.window
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIDocShell)
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIWebProgress);
+  },
+
+  /**
    * Disconnects the actor from the browser window.
    */
   disconnect: function() {
     /* Tell the live lists we aren't watching any more. */
     if (this._parameters.tabList) {
       this._parameters.tabList.onListChanged = null;
     }
     if (this._parameters.addonList) {