Merge m-c to inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 11 Mar 2015 19:06:00 -0700
changeset 263473 b6329532e4e9507e3b5f5439bb279e3a6f561110
parent 263472 99814e9730de09eaeb0a3dc2d7ce5deeefaae5db (current diff)
parent 263387 5334d2bead3e3a50a08212228a6ee2ae9c02f8c7 (diff)
child 263503 58c9d079f31811f3f325d4f439084a9ceb36764b
child 263601 b60ccf33795d6e61e42d77c271e8f3a5a29e6700
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.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 a=merge
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -28,16 +28,19 @@ SignInToWebsiteController.init();
 Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
 Cu.import('resource://gre/modules/DownloadsAPI.jsm');
 Cu.import('resource://gre/modules/MobileIdentityManager.jsm');
 Cu.import('resource://gre/modules/PresentationDeviceInfoManager.jsm');
 
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "Screenshot",
+                                  "resource://gre/modules/Screenshot.jsm");
+
 Cu.import('resource://gre/modules/Webapps.jsm');
 DOMApplicationRegistry.allAppsLaunchable = true;
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'env',
                                    '@mozilla.org/process/environment;1',
                                    'nsIEnvironment');
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'ss',
@@ -822,40 +825,19 @@ let KeyboardHelper = {
 // and detail.file set to the an image/png blob
 window.addEventListener('ContentStart', function ss_onContentStart() {
   let content = shell.contentBrowser.contentWindow;
   content.addEventListener('mozContentEvent', function ss_onMozContentEvent(e) {
     if (e.detail.type !== 'take-screenshot')
       return;
 
     try {
-      var canvas = document.createElementNS('http://www.w3.org/1999/xhtml',
-                                            'canvas');
-      var docRect = document.body.getBoundingClientRect();
-      var width = docRect.width;
-      var height = docRect.height;
-
-      // Convert width and height from CSS pixels (potentially fractional)
-      // to device pixels (integer).
-      var scale = window.devicePixelRatio;
-      canvas.setAttribute('width', Math.round(width * scale));
-      canvas.setAttribute('height', Math.round(height * scale));
-
-      var context = canvas.getContext('2d');
-      var flags =
-        context.DRAWWINDOW_DRAW_CARET |
-        context.DRAWWINDOW_DRAW_VIEW |
-        context.DRAWWINDOW_USE_WIDGET_LAYERS;
-      context.scale(scale, scale);
-      context.drawWindow(window, 0, 0, width, height,
-                         'rgb(255,255,255)', flags);
-
       shell.sendChromeEvent({
         type: 'take-screenshot-success',
-        file: canvas.mozGetAsFile('screenshot', 'image/png')
+        file: Screenshot.get()
       });
     } catch (e) {
       dump('exception while creating screenshot: ' + e + '\n');
       shell.sendChromeEvent({
         type: 'take-screenshot-error',
         error: String(e)
       });
     }
--- a/b2g/components/LogCapture.jsm
+++ b/b2g/components/LogCapture.jsm
@@ -8,16 +8,17 @@
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Screenshot", "resource://gre/modules/Screenshot.jsm");
 
 this.EXPORTED_SYMBOLS = ["LogCapture"];
 
 const SYSTEM_PROPERTY_KEY_MAX = 32;
 const SYSTEM_PROPERTY_VALUE_MAX = 92;
 
 function debug(msg) {
   dump("LogCapture.jsm: " + msg + "\n");
@@ -181,12 +182,32 @@ let LogCapture = {
                     .getService(Ci.nsIMemoryInfoDumper);
 
     let file = "/data/local/tmp/logshake-about_memory-" + this.getpid() + ".json.gz";
     dumper.dumpMemoryReportsToNamedFile(file, function() {
       deferred.resolve(file);
     }, null, false);
 
     return deferred.promise;
+  },
+
+  /**
+   * Dumping screenshot, returning a Promise. Will be resolved with the content
+   * as an ArrayBuffer.
+   */
+  getScreenshot: function() {
+    this.ensureLoaded();
+    let deferred = Promise.defer();
+
+    let fr = Cc["@mozilla.org/files/filereader;1"]
+                .createInstance(Ci.nsIDOMFileReader);
+
+    fr.onload = function(evt) {
+      deferred.resolve(new Uint8Array(evt.target.result));
+    };
+
+    fr.readAsArrayBuffer(Screenshot.get());
+
+    return deferred.promise;
   }
 };
 
 this.LogCapture = LogCapture;
--- a/b2g/components/LogShake.jsm
+++ b/b2g/components/LogShake.jsm
@@ -235,16 +235,24 @@ let LogShake = {
           Cu.reportError("Unable to handle about:memory dump: " + ex);
         }
         logArrays[file] = LogParser.prettyPrintArray(logArray);
       });
     } catch (ex) {
       Cu.reportError("Unable to get about:memory dump: " + ex);
     }
 
+    try {
+      LogCapture.getScreenshot().then(screenshot => {
+        logArrays["logshake-screenshot.png"] = screenshot;
+      });
+    } catch (ex) {
+      Cu.reportError("Unable to get screenshot dump: " + ex);
+    }
+
     for (let loc in this.LOGS_WITH_PARSERS) {
       let logArray;
       try {
         logArray = LogCapture.readLogFile(loc);
         if (!logArray) {
           debug("LogCapture.readLogFile() returned nothing for: " + loc);
           continue;
         }
new file mode 100644
--- /dev/null
+++ b/b2g/components/Screenshot.jsm
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", "resource://gre/modules/SystemAppProxy.jsm");
+
+this.EXPORTED_SYMBOLS = ['Screenshot'];
+
+let Screenshot = {
+  get: function screenshot_get() {
+    let systemAppFrame = SystemAppProxy.getFrame();
+    let window = systemAppFrame.contentWindow;
+    let document = window.document;
+
+    var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
+    var docRect = document.body.getBoundingClientRect();
+    var width = docRect.width;
+    var height = docRect.height;
+
+    // Convert width and height from CSS pixels (potentially fractional)
+    // to device pixels (integer).
+    var scale = window.devicePixelRatio;
+    canvas.setAttribute('width', Math.round(width * scale));
+    canvas.setAttribute('height', Math.round(height * scale));
+
+    var context = canvas.getContext('2d');
+    var flags =
+      context.DRAWWINDOW_DRAW_CARET |
+      context.DRAWWINDOW_DRAW_VIEW |
+      context.DRAWWINDOW_USE_WIDGET_LAYERS;
+    context.scale(scale, scale);
+    context.drawWindow(window, 0, 0, width, height, 'rgb(255,255,255)', flags);
+
+    return canvas.mozGetAsFile('screenshot', 'image/png');
+  }
+};
+this.Screenshot = Screenshot;
--- a/b2g/components/SystemAppProxy.jsm
+++ b/b2g/components/SystemAppProxy.jsm
@@ -24,16 +24,21 @@ let SystemAppProxy = {
 
     // Register all DOM event listeners added before we got a ref to the app iframe
     this._pendingListeners
         .forEach((args) =>
                  this.addEventListener.apply(this, args));
     this._pendingListeners = [];
   },
 
+  // Get the system app frame
+  getFrame: function () {
+    return this._frame;
+  },
+
   // To call when it is ready to receive events
   setIsReady: function () {
     if (this._isReady) {
       Cu.reportError('SystemApp has already been declared as being ready.');
     }
     this._isReady = true;
 
     // Dispatch all events being queued while the system app was still loading
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -55,16 +55,17 @@ EXTRA_JS_MODULES += [
     'DebuggerActors.js',
     'ErrorPage.jsm',
     'Frames.jsm',
     'FxAccountsMgmtService.jsm',
     'LogCapture.jsm',
     'LogParser.jsm',
     'LogShake.jsm',
     'OrientationChangeHandler.jsm',
+    'Screenshot.jsm',
     'SignInToWebsite.jsm',
     'SystemAppProxy.jsm',
     'TelURIParser.jsm',
     'WebappsUpdater.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     EXTRA_JS_MODULES += [
--- a/b2g/components/test/mochitest/mochitest.ini
+++ b/b2g/components/test/mochitest/mochitest.ini
@@ -1,16 +1,18 @@
 [DEFAULT]
 skip-if = toolkit != "gonk"
 support-files =
   permission_handler_chrome.js
   SandboxPromptTest.html
   filepicker_path_handler_chrome.js
+  screenshot_helper.js
   systemapp_helper.js
   presentation_prompt_handler_chrome.js
 
 [test_filepicker_path.html]
 [test_permission_deny.html]
 [test_permission_gum_remember.html]
 skip-if = true # Bug 1019572 - frequent timeouts
 [test_sandbox_permission.html]
+[test_screenshot.html]
 [test_systemapp.html]
 [test_presentation_device_prompt.html]
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/mochitest/screenshot_helper.js
@@ -0,0 +1,38 @@
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
+
+// Load a duplicated copy of the jsm to prevent messing with the currently running one
+let scope = {};
+Services.scriptloader.loadSubScript("resource://gre/modules/Screenshot.jsm", scope);
+const { Screenshot } = scope;
+
+let index = -1;
+function next() {
+  index++;
+  if (index >= steps.length) {
+    assert.ok(false, "Shouldn't get here!");
+    return;
+  }
+  try {
+    steps[index]();
+  } catch(ex) {
+    assert.ok(false, "Caught exception: " + ex);
+  }
+}
+
+let steps = [
+  function getScreenshot() {
+    let screenshot = Screenshot.get();
+    assert.ok(screenshot instanceof Ci.nsIDOMFile,
+              "Screenshot.get() returns a File");
+    next();
+  },
+
+  function endOfTest() {
+    sendAsyncMessage("finish");
+  }
+];
+
+next();
--- a/b2g/components/test/mochitest/systemapp_helper.js
+++ b/b2g/components/test/mochitest/systemapp_helper.js
@@ -108,16 +108,19 @@ let steps = [
       assert.ok(true, "Frame document loaded");
 
       // Declare that the iframe is now loaded.
       // That should dispatch early events
       isLoaded = true;
       SystemAppProxy.setIsReady();
       assert.ok(true, "Frame declared as loaded");
 
+      let gotFrame = SystemAppProxy.getFrame();
+      assert.equal(gotFrame, frame, "getFrame returns the frame we passed");
+
       // Once pending events are received,
       // we will run checkEventDispatching from `listener` function
     });
 
     frame.setAttribute("src", "data:text/html,system app");
   },
 
   function checkEventDispatching() {
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/mochitest/test_screenshot.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1136784
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Screenshot Test</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136784">Screenshot.jsm</a>
+<script type="application/javascript">
+
+"use strict";
+
+var gUrl = SimpleTest.getTestFileURL("screenshot_helper.js");
+var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+SimpleTest.waitForExplicitFinish();
+gScript.addMessageListener("finish", function () {
+  SimpleTest.ok(true, "chrome test script finished");
+  gScript.destroy();
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/b2g/components/test/unit/test_logcapture_gonk.js
+++ b/b2g/components/test/unit/test_logcapture_gonk.js
@@ -40,17 +40,16 @@ add_test(function test_readProperties() 
 
 add_test(function test_readAppIni() {
   let appIni = LogCapture.readLogFile("/system/b2g/application.ini");
   verifyLog(appIni);
 
   run_next_test();
 });
 
-
 add_test(function test_get_about_memory() {
   let memLog = LogCapture.readAboutMemory();
 
   ok(memLog, "Should have returned a valid Promise object");
 
   memLog.then(file => {
     ok(file, "Should have returned a filename");
     run_next_test();
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,22 +14,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="97c3d9b8b87774ca7a08c89145e95b55652459ef"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,19 +12,19 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,22 +14,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="97c3d9b8b87774ca7a08c89145e95b55652459ef"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,19 +12,19 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "2b87ee8e7e2ec30a9851b6b59a899006a98767ab", 
+        "git_revision": "0c4e8b0b330757e261b031b7e7f326ef419c9808", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "a3f060d46f5e179164fe9435f30878ebdf58eb49", 
+    "revision": "f8a8e3a65150eaf7a870a4e75ad6bbea833390ee", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,19 +12,19 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,21 +10,21 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="2b87ee8e7e2ec30a9851b6b59a899006a98767ab"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="0c4e8b0b330757e261b031b7e7f326ef419c9808"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c82a532ee1f14b9733214022b1e2d55a0b030be8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
-  <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
+  <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="ddf33f81e9a60f8110fcfd6b51b5dff2db676183"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
--- a/dom/bluetooth/BluetoothInterface.cpp
+++ b/dom/bluetooth/BluetoothInterface.cpp
@@ -90,46 +90,66 @@ BluetoothNotificationHandler::~Bluetooth
 
 // Interface
 //
 
 BluetoothInterface*
 BluetoothInterface::GetInstance()
 {
 #if ANDROID_VERSION >= 17
-  /* We pick a default backend from the available ones. The branches
-   * are ordered by preference.
+  /* We pick a default backend from the available ones. The options are
+   * ordered by preference. If a backend is supported but not available
+   * on the current system, we pick the next one. The selected default
+   * can be overriden manually by storing the respective string in the
+   * system property 'ro.moz.bluetooth.backend'.
    */
+
+  static const char* const sDefaultBackend[] = {
+#ifdef MOZ_B2G_BT_DAEMON
+    "bluetoothd",
+#endif
 #ifdef MOZ_B2G_BT_BLUEDROID
-  static const char sDefaultBackend[] = "bluedroid";
-#else
-#ifdef MOZ_B2G_BT_DAEMON
-  static const char sDefaultBackend[] = "bluetoothd";
-#else
-  static const char* const sDefaultBackend = nullptr;
+    "bluedroid",
 #endif
-#endif
+    nullptr // no default backend; must be final element in array
+  };
+
+  const char* defaultBackend;
+
+  for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDefaultBackend); ++i) {
+
+    /* select current backend */
+    defaultBackend = sDefaultBackend[i];
+
+    if (defaultBackend) {
+      if (!strcmp(defaultBackend, "bluetoothd") &&
+          access("/init.bluetooth.rc", F_OK) == -1) {
+        continue; /* bluetoothd not available */
+      }
+    }
+    break;
+  }
+
+  char value[PROPERTY_VALUE_MAX];
+  int len;
+
+  len = property_get("ro.moz.bluetooth.backend", value, defaultBackend);
+  if (len < 0) {
+    BT_WARNING("No Bluetooth backend available.");
+    return nullptr;
+  }
+
+  const nsDependentCString backend(value, len);
 
   /* Here's where we decide which implementation to use. Currently
    * there is only Bluedroid and the Bluetooth daemon, but others are
    * possible. Having multiple interfaces built-in and selecting the
    * correct one at runtime is also an option.
    */
 
-  char value[PROPERTY_VALUE_MAX];
-  int len;
-
-  len = property_get("ro.moz.bluetooth.backend", value, sDefaultBackend);
-  if (len < 0) {
-    BT_WARNING("No Bluetooth backend available.");
-    return nullptr;
-  }
-
-  const nsDependentCString backend(value, len);
-
 #ifdef MOZ_B2G_BT_BLUEDROID
   if (backend.LowerCaseEqualsLiteral("bluedroid")) {
     return BluetoothHALInterface::GetInstance();
   } else
 #endif
 #ifdef MOZ_B2G_BT_DAEMON
   if (backend.LowerCaseEqualsLiteral("bluetoothd")) {
     return BluetoothDaemonInterface::GetInstance();
--- a/dom/camera/CameraPreferences.cpp
+++ b/dom/camera/CameraPreferences.cpp
@@ -26,16 +26,18 @@ StaticAutoPtr<nsCString> CameraPreferenc
 
 nsresult CameraPreferences::sPrefCameraControlMethodErrorOverride = NS_OK;
 nsresult CameraPreferences::sPrefCameraControlAsyncErrorOverride = NS_OK;
 
 uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0;
 
 bool CameraPreferences::sPrefCameraParametersIsLowMemory = false;
 
+bool CameraPreferences::sPrefCameraParametersPermission = false;
+
 #ifdef MOZ_WIDGET_GONK
 StaticRefPtr<CameraPreferences> CameraPreferences::sObserver;
 
 NS_IMPL_ISUPPORTS(CameraPreferences, nsIObserver);
 #endif
 
 /* static */
 nsresult
@@ -108,16 +110,21 @@ CameraPreferences::Pref CameraPreference
     kPrefValueIsCString,
     { &sPrefTestEnabled }
   },
   {
     "camera.control.test.hardware",
     kPrefValueIsCString,
     { &sPrefHardwareTest }
   },
+  {
+    "camera.control.test.permission",
+    kPrefValueIsBoolean,
+    { &sPrefCameraParametersPermission }
+  },
 #ifdef MOZ_B2G
   {
     "camera.control.test.hardware.gonk.parameters",
     kPrefValueIsCString,
     { &sPrefGonkParameters }
   },
 #endif
   {
--- a/dom/camera/CameraPreferences.h
+++ b/dom/camera/CameraPreferences.h
@@ -75,16 +75,18 @@ protected:
 
   static nsresult sPrefCameraControlMethodErrorOverride;
   static nsresult sPrefCameraControlAsyncErrorOverride;
 
   static uint32_t sPrefCameraControlLowMemoryThresholdMB;
 
   static bool sPrefCameraParametersIsLowMemory;
 
+  static bool sPrefCameraParametersPermission;
+
 #ifdef MOZ_WIDGET_GONK
   static StaticRefPtr<CameraPreferences> sObserver;
 
   nsresult PreinitCameraHardware();
 
 protected:
   // Objects may be instantiated for use as observers.
   CameraPreferences() { }
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -11,16 +11,17 @@
 #include "nsContentPermissionHelper.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "DOMCameraControl.h"
 #include "nsDOMClassInfo.h"
 #include "CameraCommon.h"
+#include "CameraPreferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMCameraManager, mWindow)
 
@@ -293,17 +294,21 @@ nsDOMCameraManager::GetCamera(const nsAS
     return nullptr;
   }
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   // If we are a CERTIFIED app, we can short-circuit the permission check,
   // which gets us a performance win.
   uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   principal->GetAppStatus(&status);
-  if (status == nsIPrincipal::APP_STATUS_CERTIFIED && CheckPermission(mWindow)) {
+  // Unprivileged mochitests always fail the dispatched permission check,
+  // even if permission to the camera has been granted.
+  bool immediateCheck = false;
+  CameraPreferences::GetPref("camera.control.test.permission", immediateCheck);
+  if ((status == nsIPrincipal::APP_STATUS_CERTIFIED || immediateCheck) && CheckPermission(mWindow)) {
     PermissionAllowed(cameraId, aInitialConfig, promise);
     return promise.forget();
   }
 
   nsCOMPtr<nsIRunnable> permissionRequest =
     new CameraPermissionRequest(principal, mWindow, this, cameraId,
                                 aInitialConfig, promise);
 
new file mode 100644
--- /dev/null
+++ b/dom/camera/FallbackCameraPlatform.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FallbackCameraPlatform.h"
+
+using namespace android;
+
+MediaProfiles* MediaProfiles::sMediaProfiles = nullptr;
+
+const char CameraParameters::KEY_PREVIEW_SIZE[] = "preview-size";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES[] = "preview-size-values";
+const char CameraParameters::KEY_PREVIEW_FORMAT[] = "preview-format";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS[] = "preview-format-values";
+const char CameraParameters::KEY_PREVIEW_FRAME_RATE[] = "preview-frame-rate";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES[] = "preview-frame-rate-values";
+const char CameraParameters::KEY_PREVIEW_FPS_RANGE[] = "preview-fps-range";
+const char CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE[] = "preview-fps-range-values";
+const char CameraParameters::KEY_PICTURE_SIZE[] = "picture-size";
+const char CameraParameters::KEY_SUPPORTED_PICTURE_SIZES[] = "picture-size-values";
+const char CameraParameters::KEY_PICTURE_FORMAT[] = "picture-format";
+const char CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS[] = "picture-format-values";
+const char CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH[] = "jpeg-thumbnail-width";
+const char CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT[] = "jpeg-thumbnail-height";
+const char CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES[] = "jpeg-thumbnail-size-values";
+const char CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY[] = "jpeg-thumbnail-quality";
+const char CameraParameters::KEY_JPEG_QUALITY[] = "jpeg-quality";
+const char CameraParameters::KEY_ROTATION[] = "rotation";
+const char CameraParameters::KEY_GPS_LATITUDE[] = "gps-latitude";
+const char CameraParameters::KEY_GPS_LONGITUDE[] = "gps-longitude";
+const char CameraParameters::KEY_GPS_ALTITUDE[] = "gps-altitude";
+const char CameraParameters::KEY_GPS_TIMESTAMP[] = "gps-timestamp";
+const char CameraParameters::KEY_GPS_PROCESSING_METHOD[] = "gps-processing-method";
+const char CameraParameters::KEY_WHITE_BALANCE[] = "whitebalance";
+const char CameraParameters::KEY_SUPPORTED_WHITE_BALANCE[] = "whitebalance-values";
+const char CameraParameters::KEY_EFFECT[] = "effect";
+const char CameraParameters::KEY_SUPPORTED_EFFECTS[] = "effect-values";
+const char CameraParameters::KEY_ANTIBANDING[] = "antibanding";
+const char CameraParameters::KEY_SUPPORTED_ANTIBANDING[] = "antibanding-values";
+const char CameraParameters::KEY_SCENE_MODE[] = "scene-mode";
+const char CameraParameters::KEY_SUPPORTED_SCENE_MODES[] = "scene-mode-values";
+const char CameraParameters::KEY_FLASH_MODE[] = "flash-mode";
+const char CameraParameters::KEY_SUPPORTED_FLASH_MODES[] = "flash-mode-values";
+const char CameraParameters::KEY_FOCUS_MODE[] = "focus-mode";
+const char CameraParameters::KEY_SUPPORTED_FOCUS_MODES[] = "focus-mode-values";
+const char CameraParameters::KEY_MAX_NUM_FOCUS_AREAS[] = "max-num-focus-areas";
+const char CameraParameters::KEY_FOCUS_AREAS[] = "focus-areas";
+const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length";
+const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle";
+const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle";
+const char CameraParameters::KEY_EXPOSURE_COMPENSATION[] = "exposure-compensation";
+const char CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION[] = "max-exposure-compensation";
+const char CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION[] = "min-exposure-compensation";
+const char CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP[] = "exposure-compensation-step";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK[] = "auto-exposure-lock";
+const char CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[] = "auto-exposure-lock-supported";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK[] = "auto-whitebalance-lock";
+const char CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[] = "auto-whitebalance-lock-supported";
+const char CameraParameters::KEY_MAX_NUM_METERING_AREAS[] = "max-num-metering-areas";
+const char CameraParameters::KEY_METERING_AREAS[] = "metering-areas";
+const char CameraParameters::KEY_ZOOM[] = "zoom";
+const char CameraParameters::KEY_MAX_ZOOM[] = "max-zoom";
+const char CameraParameters::KEY_ZOOM_RATIOS[] = "zoom-ratios";
+const char CameraParameters::KEY_ZOOM_SUPPORTED[] = "zoom-supported";
+const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported";
+const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
+const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
+const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size";
+const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values";
+const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video";
+const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
+const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
+const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
+const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
+const char CameraParameters::KEY_VIDEO_STABILIZATION[] = "video-stabilization";
+const char CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED[] = "video-stabilization-supported";
+const char CameraParameters::KEY_LIGHTFX[] = "light-fx";
+
new file mode 100644
--- /dev/null
+++ b/dom/camera/FallbackCameraPlatform.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DOM_CAMERA_FALLBACKCAMERAPLATFORM_H
+#define DOM_CAMERA_FALLBACKCAMERAPLATFORM_H
+
+#include <inttypes.h>
+#include <string.h>
+
+typedef struct {
+  int32_t id;
+  int32_t score;
+  int32_t rect[4];
+  int32_t left_eye[2];
+  int32_t right_eye[2];
+  int32_t mouth[2];
+} camera_face_t;
+
+typedef struct {
+  uint32_t number_of_faces;
+  camera_face_t* faces;
+} camera_frame_metadata_t;
+
+namespace android {
+  enum camcorder_quality {
+    CAMCORDER_QUALITY_LOW,
+    CAMCORDER_QUALITY_HIGH,
+    CAMCORDER_QUALITY_QCIF,
+    CAMCORDER_QUALITY_CIF,
+    CAMCORDER_QUALITY_480P,
+    CAMCORDER_QUALITY_720P,
+    CAMCORDER_QUALITY_1080P,
+    CAMCORDER_QUALITY_QVGA,
+    CAMCORDER_QUALITY_VGA,
+    CAMCORDER_QUALITY_LIST_START = CAMCORDER_QUALITY_LOW,
+    CAMCORDER_QUALITY_LIST_END = CAMCORDER_QUALITY_VGA
+  };
+
+  enum output_format {
+    OUTPUT_FORMAT_THREE_GPP,
+    OUTPUT_FORMAT_MPEG_4
+  };
+
+  enum video_encoder {
+    VIDEO_ENCODER_H263,
+    VIDEO_ENCODER_H264,
+    VIDEO_ENCODER_MPEG_4_SP
+  };
+
+  enum audio_encoder {
+    AUDIO_ENCODER_AMR_WB,
+    AUDIO_ENCODER_AMR_NB,
+    AUDIO_ENCODER_AAC
+  };
+
+  template <class T>
+  class sp MOZ_FINAL
+  {
+  public:
+    sp()
+      : mPtr(nullptr)
+    { }
+
+    sp(T *aPtr)
+      : mPtr(aPtr)
+    { }
+
+    virtual ~sp()         { }
+    T* get() const        { return mPtr; }
+    void clear()          { mPtr = nullptr; }
+    T* operator->() const { return get(); }
+
+  private:
+    nsRefPtr<T> mPtr;
+  };
+
+  typedef uint64_t nsecs_t;
+
+  enum error_t {
+    OK = 0,
+    UNKNOWN_ERROR,
+    INVALID_OPERATION
+  };
+
+  enum camera_msg_t {
+    CAMERA_MSG_SHUTTER,
+    CAMERA_MSG_COMPRESSED_IMAGE
+  };
+
+  class String8 MOZ_FINAL
+  {
+  public:
+    String8()                  { }
+    String8(const char* aData) { mData.AssignASCII(aData); }
+    virtual ~String8()         { }
+    const char* string() const { return mData.Data(); }
+
+  private:
+    nsCString mData;
+  };
+
+  enum camera_facing_t {
+    CAMERA_FACING_BACK,
+    CAMERA_FACING_FRONT
+  };
+
+  struct CameraInfo {
+    camera_facing_t facing;
+  };
+
+  class Camera MOZ_FINAL : public nsISupports
+  {
+  public:
+    NS_DECL_ISUPPORTS;
+
+    void disconnect()                         { }
+    String8 getParameters()                   { return String8(); }
+    int setParameters(const String8& aParams) { return UNKNOWN_ERROR; }
+    int storeMetaDataInBuffers(bool aEnabled) { return UNKNOWN_ERROR; }
+    int autoFocus()                           { return UNKNOWN_ERROR; }
+    int cancelAutoFocus()                     { return UNKNOWN_ERROR; }
+    int takePicture(uint32_t flags)           { return UNKNOWN_ERROR; }
+    int startPreview()                        { return UNKNOWN_ERROR; }
+    int stopPreview()                         { return UNKNOWN_ERROR; }
+    int startRecording()                      { return UNKNOWN_ERROR; }
+    int stopRecording()                       { return UNKNOWN_ERROR; }
+    int startFaceDetection()                  { return UNKNOWN_ERROR; }
+    int stopFaceDetection()                   { return UNKNOWN_ERROR; }
+    static int32_t getNumberOfCameras()       { return 2; }
+
+    static int getCameraInfo(int32_t aDevice, CameraInfo* aInfo)
+    {
+      switch (aDevice) {
+        case 0:
+          aInfo->facing = CAMERA_FACING_BACK;
+          break;
+        case 1:
+          aInfo->facing = CAMERA_FACING_FRONT;
+          break;
+        default:
+          return UNKNOWN_ERROR;
+      }
+      return OK;
+    }
+
+  protected:
+    Camera()          { }
+    virtual ~Camera() { }
+
+  private:
+    Camera(const Camera&) = delete;
+    Camera& operator=(const Camera&) = delete;
+  };
+
+  class CameraParameters MOZ_FINAL
+  {
+  public:
+    static const char KEY_PREVIEW_SIZE[];
+    static const char KEY_SUPPORTED_PREVIEW_SIZES[];
+    static const char KEY_PREVIEW_FPS_RANGE[];
+    static const char KEY_SUPPORTED_PREVIEW_FPS_RANGE[];
+    static const char KEY_PREVIEW_FORMAT[];
+    static const char KEY_SUPPORTED_PREVIEW_FORMATS[];
+    static const char KEY_PREVIEW_FRAME_RATE[];
+    static const char KEY_SUPPORTED_PREVIEW_FRAME_RATES[];
+    static const char KEY_PICTURE_SIZE[];
+    static const char KEY_SUPPORTED_PICTURE_SIZES[];
+    static const char KEY_PICTURE_FORMAT[];
+    static const char KEY_SUPPORTED_PICTURE_FORMATS[];
+    static const char KEY_JPEG_THUMBNAIL_WIDTH[];
+    static const char KEY_JPEG_THUMBNAIL_HEIGHT[];
+    static const char KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES[];
+    static const char KEY_JPEG_THUMBNAIL_QUALITY[];
+    static const char KEY_JPEG_QUALITY[];
+    static const char KEY_ROTATION[];
+    static const char KEY_GPS_LATITUDE[];
+    static const char KEY_GPS_LONGITUDE[];
+    static const char KEY_GPS_ALTITUDE[];
+    static const char KEY_GPS_TIMESTAMP[];
+    static const char KEY_GPS_PROCESSING_METHOD[];
+    static const char KEY_WHITE_BALANCE[];
+    static const char KEY_SUPPORTED_WHITE_BALANCE[];
+    static const char KEY_EFFECT[];
+    static const char KEY_SUPPORTED_EFFECTS[];
+    static const char KEY_ANTIBANDING[];
+    static const char KEY_SUPPORTED_ANTIBANDING[];
+    static const char KEY_SCENE_MODE[];
+    static const char KEY_SUPPORTED_SCENE_MODES[];
+    static const char KEY_FLASH_MODE[];
+    static const char KEY_SUPPORTED_FLASH_MODES[];
+    static const char KEY_FOCUS_MODE[];
+    static const char KEY_SUPPORTED_FOCUS_MODES[];
+    static const char KEY_MAX_NUM_FOCUS_AREAS[];
+    static const char KEY_FOCUS_AREAS[];
+    static const char KEY_FOCAL_LENGTH[];
+    static const char KEY_HORIZONTAL_VIEW_ANGLE[];
+    static const char KEY_VERTICAL_VIEW_ANGLE[];
+    static const char KEY_EXPOSURE_COMPENSATION[];
+    static const char KEY_MAX_EXPOSURE_COMPENSATION[];
+    static const char KEY_MIN_EXPOSURE_COMPENSATION[];
+    static const char KEY_EXPOSURE_COMPENSATION_STEP[];
+    static const char KEY_AUTO_EXPOSURE_LOCK[];
+    static const char KEY_AUTO_EXPOSURE_LOCK_SUPPORTED[];
+    static const char KEY_AUTO_WHITEBALANCE_LOCK[];
+    static const char KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED[];
+    static const char KEY_MAX_NUM_METERING_AREAS[];
+    static const char KEY_METERING_AREAS[];
+    static const char KEY_ZOOM[];
+    static const char KEY_MAX_ZOOM[];
+    static const char KEY_ZOOM_RATIOS[];
+    static const char KEY_ZOOM_SUPPORTED[];
+    static const char KEY_SMOOTH_ZOOM_SUPPORTED[];
+    static const char KEY_FOCUS_DISTANCES[];
+    static const char KEY_VIDEO_SIZE[];
+    static const char KEY_SUPPORTED_VIDEO_SIZES[];
+    static const char KEY_MAX_NUM_DETECTED_FACES_HW[];
+    static const char KEY_MAX_NUM_DETECTED_FACES_SW[];
+    static const char KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[];
+    static const char KEY_VIDEO_FRAME_FORMAT[];
+    static const char KEY_RECORDING_HINT[];
+    static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[];
+    static const char KEY_VIDEO_STABILIZATION[];
+    static const char KEY_VIDEO_STABILIZATION_SUPPORTED[];
+    static const char KEY_LIGHTFX[];
+  };
+
+  class MediaProfiles MOZ_FINAL
+  {
+  public:
+    static MediaProfiles* getInstance() {
+      if (!sMediaProfiles) {
+        sMediaProfiles = new MediaProfiles();
+      }
+      return sMediaProfiles;
+    }
+
+    bool hasCamcorderProfile(int aCameraId, camcorder_quality aQuality) const {
+      switch (aQuality) {
+        case CAMCORDER_QUALITY_LOW:
+        case CAMCORDER_QUALITY_HIGH:
+        case CAMCORDER_QUALITY_QVGA:
+        case CAMCORDER_QUALITY_VGA:
+          return true;
+        default:
+          break;
+      }
+      return false;
+    }
+
+    int getCamcorderProfileParamByName(const char* aParameter, int aCameraId, camcorder_quality aQuality) const {
+      switch (aQuality) {
+        case CAMCORDER_QUALITY_LOW:
+        case CAMCORDER_QUALITY_QVGA:
+          if (strcmp(aParameter, "vid.width") == 0) {
+            return 320;
+          } else if (strcmp(aParameter, "vid.height") == 0) {
+            return 240;
+          }
+          return 0;
+        case CAMCORDER_QUALITY_HIGH:
+        case CAMCORDER_QUALITY_VGA:
+          if (strcmp(aParameter, "vid.width") == 0) {
+            return 640;
+          } else if (strcmp(aParameter, "vid.height") == 0) {
+            return 480;
+          }
+          return 0;
+        default:
+          break;
+      }
+      return -1;
+    }
+
+  protected:
+    MediaProfiles()          { }
+    virtual ~MediaProfiles() { }
+
+  private:
+    MediaProfiles(const MediaProfiles&) = delete;
+    MediaProfiles& operator=(const MediaProfiles&) = delete;
+
+    static MediaProfiles* sMediaProfiles;
+  };
+}
+
+#endif
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -17,35 +17,34 @@
 #include "GonkCameraControl.h"
 #include <time.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <libgen.h>
 #include "base/basictypes.h"
-#include "camera/CameraParameters.h"
+#include "Layers.h"
+#ifdef MOZ_WIDGET_GONK
+#include <media/mediaplayer.h>
+#include <media/MediaProfiles.h>
+#include "GrallocImages.h"
+#endif
 #include "nsCOMPtr.h"
 #include "nsMemory.h"
 #include "nsThread.h"
-#include <media/MediaProfiles.h>
 #include "mozilla/FileUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "nsAlgorithm.h"
-#include <media/mediaplayer.h>
 #include "nsPrintfCString.h"
-#include "nsIObserverService.h"
-#include "nsIVolume.h"
-#include "nsIVolumeService.h"
 #include "AutoRwLock.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkRecorderProfiles.h"
-#include "GrallocImages.h"
 #include "CameraCommon.h"
 #include "GonkCameraParameters.h"
 #include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 using namespace mozilla::ipc;
@@ -66,17 +65,19 @@ nsGonkCameraControl::nsGonkCameraControl
   , mLastThumbnailSize({0, 0})
   , mPreviewFps(30)
   , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
   , mFlashSupported(false)
   , mLuminanceSupported(false)
   , mAutoFlashModeOverridden(false)
   , mSeparateVideoAndPreviewSizesSupported(false)
   , mDeferConfigUpdate(0)
+#ifdef MOZ_WIDGET_GONK
   , mRecorder(nullptr)
+#endif
   , mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
   , mVideoFile(nullptr)
   , mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
 {
   // Constructor runs on the main thread...
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mImageContainer = LayerManager::CreateImageContainer();
 }
@@ -1160,17 +1161,19 @@ nsresult
 nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
                                         const StartRecordingOptions* aOptions)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
   NS_ENSURE_TRUE(!mCurrentConfiguration.mRecorderProfile.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
+#ifdef MOZ_WIDGET_GONK
   NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
+#endif
 
   /**
    * Get the base path from device storage and append the app-specified
    * filename to it.  The filename may include a relative subpath
    * (e.g.) "DCIM/IMG_0001.jpg".
    *
    * The camera app needs to provide the file extension '.3gp' for now.
    * See bug 795202.
@@ -1207,26 +1210,28 @@ nsGonkCameraControl::StartRecordingImpl(
     }
   } else {
     rv = SetupRecording(fd, 0, 0, 0);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+#ifdef MOZ_WIDGET_GONK
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     // important: we MUST destroy the recorder if start() fails!
     mRecorder = nullptr;
     // put the flash back to the 'auto' state
     if (mAutoFlashModeOverridden) {
       SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto"));
     }
     return NS_ERROR_FAILURE;
   }
+#endif
 
   OnRecorderStateChange(CameraControlListener::kRecorderStarted);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopRecordingImpl()
 {
@@ -1250,16 +1255,17 @@ nsGonkCameraControl::StopRecordingImpl()
     }
 
   private:
     nsRefPtr<DeviceStorageFile> mFile;
   };
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
+#ifdef MOZ_WIDGET_GONK
   // nothing to do if we have no mRecorder
   if (!mRecorder) {
     return NS_OK;
   }
 
   mRecorder->stop();
   mRecorder = nullptr;
   OnRecorderStateChange(CameraControlListener::kRecorderStopped);
@@ -1272,16 +1278,19 @@ nsGonkCameraControl::StopRecordingImpl()
       if (NS_FAILED(rv)) {
         DOM_CAMERA_LOGE("Failed to set flash mode (0x%x)\n", rv);
       }
     }
   }
 
   // notify DeviceStorage that the new video file is closed and ready
   return NS_DispatchToMainThread(new RecordingComplete(mVideoFile));
+#else
+  return NS_OK;
+#endif
 }
 
 nsresult
 nsGonkCameraControl::ResumeContinuousFocusImpl()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
   RETURN_IF_NO_CAMERA_HW();
 
@@ -1692,16 +1701,17 @@ nsGonkCameraControl::SetVideoConfigurati
       return rv;
     }
   }
 
   mPreviewFps = fps;
   return NS_OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 class GonkRecorderListener : public IMediaRecorderClient
 {
 public:
   GonkRecorderListener(nsGonkCameraControl* aCameraControl)
     : mCameraControl(aCameraControl)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n",
       __func__, __LINE__, this, mCameraControl.get());
@@ -1838,24 +1848,26 @@ nsGonkCameraControl::OnRecorderEvent(int
       DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2);
       OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum);
       return;
   }
 
   // All unhandled cases wind up here
   DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2);
 }
+#endif
 
 nsresult
 nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
                                     uint64_t aMaxFileSizeBytes,
                                     uint64_t aMaxVideoLengthMs)
 {
   RETURN_IF_NO_CAMERA_HW();
 
+#ifdef MOZ_WIDGET_GONK
   // choosing a size big enough to hold the params
   const size_t SIZE = 256;
   char buffer[SIZE];
 
   ReentrantMonitorAutoEnter mon(mRecorderMonitor);
 
   mRecorder = new GonkRecorder();
   CHECK_SETARG_RETURN(mRecorder->init(), NS_ERROR_FAILURE);
@@ -1906,16 +1918,17 @@ nsGonkCameraControl::SetupRecording(int 
                       NS_ERROR_INVALID_ARG);
 
   CHECK_SETARG_RETURN(mRecorder->setListener(new GonkRecorderListener(this)),
                       NS_ERROR_FAILURE);
 
   // recording API needs file descriptor of output file
   CHECK_SETARG_RETURN(mRecorder->setOutputFile(aFd, 0, 0), NS_ERROR_FAILURE);
   CHECK_SETARG_RETURN(mRecorder->prepare(), NS_ERROR_FAILURE);
+#endif
 
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopInternal()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
@@ -2041,28 +2054,30 @@ void
 nsGonkCameraControl::OnRateLimitPreview(bool aLimit)
 {
   CameraControlImpl::OnRateLimitPreview(aLimit);
 }
 
 void
 nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
 {
+#ifdef MOZ_WIDGET_GONK
   nsRefPtr<Image> frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
 
   GrallocImage* videoImage = static_cast<GrallocImage*>(frame.get());
 
   GrallocImage::GrallocData data;
   data.mGraphicBuffer = aBuffer;
   data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width,
                           mCurrentConfiguration.mPreviewSize.height);
   videoImage->SetData(data);
 
   OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width,
                     mCurrentConfiguration.mPreviewSize.height);
+#endif
 }
 
 void
 nsGonkCameraControl::OnSystemError(CameraControlListener::SystemContext aWhere,
                                    nsresult aError)
 {
   if (aWhere == CameraControlListener::kSystemService) {
     StopInternal();
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -14,28 +14,34 @@
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERACONTROL_H
 #define DOM_CAMERA_GONKCAMERACONTROL_H
 
 #include "base/basictypes.h"
 #include "nsRefPtrHashtable.h"
-#include <media/MediaProfiles.h>
 #include "mozilla/ReentrantMonitor.h"
 #include "DeviceStorage.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
-#include "GonkRecorder.h"
 #include "GonkCameraHwMgr.h"
 #include "GonkCameraParameters.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include <media/MediaProfiles.h>
+#include <camera/Camera.h>
+#include "GonkRecorder.h"
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
+
 namespace android {
   class GonkCameraHardware;
-  class MediaProfiles;
   class GonkRecorder;
   class GonkCameraSource;
 }
 
 namespace mozilla {
 
 namespace layers {
   class TextureClient;
@@ -51,17 +57,19 @@ public:
   nsGonkCameraControl(uint32_t aCameraId);
 
   void OnAutoFocusComplete(bool aSuccess);
   void OnFacesDetected(camera_frame_metadata_t* aMetaData);
   void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
   void OnTakePictureError();
   void OnRateLimitPreview(bool aLimit);
   void OnNewPreviewFrame(layers::TextureClient* aBuffer);
+#ifdef MOZ_WIDGET_GONK
   void OnRecorderEvent(int msg, int ext1, int ext2);
+#endif
   void OnSystemError(CameraControlListener::SystemContext aWhere, nsresult aError);
 
   // See ICameraControl.h for getter/setter return values.
   virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE;
   virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE;
   virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE;
   virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE;
   virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE;
@@ -172,17 +180,19 @@ protected:
   bool                      mLuminanceSupported;
   bool                      mAutoFlashModeOverridden;
   bool                      mSeparateVideoAndPreviewSizesSupported;
   Atomic<uint32_t>          mDeferConfigUpdate;
   GonkCameraParameters      mParams;
 
   nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
 
+#ifdef MOZ_WIDGET_GONK
   nsRefPtr<android::GonkRecorder> mRecorder;
+#endif
   // Touching mRecorder happens inside this monitor because the destructor
   // can run on any thread, and we need to be able to clean up properly if
   // GonkCameraControl goes away.
   ReentrantMonitor          mRecorderMonitor;
 
   // Supported recorder profiles
   nsRefPtrHashtable<nsStringHashKey, RecorderProfile> mRecorderProfiles;
 
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -12,52 +12,63 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkCameraHwMgr.h"
 #include "TestGonkCameraHardware.h"
 
+#ifdef MOZ_WIDGET_GONK
 #include <binder/IPCThreadState.h>
 #include <sys/system_properties.h>
+#include "GonkNativeWindow.h"
+#endif
 
 #include "base/basictypes.h"
 #include "nsDebug.h"
 #include "mozilla/layers/TextureClient.h"
 #include "CameraPreferences.h"
 #include "mozilla/RefPtr.h"
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
 #include "GonkBufferQueueProducer.h"
 #endif
 #include "GonkCameraControl.h"
-#include "GonkNativeWindow.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace android;
 
+#ifndef MOZ_WIDGET_GONK
+NS_IMPL_ISUPPORTS0(GonkCameraHardware);
+NS_IMPL_ISUPPORTS0(android::Camera);
+#endif
+
 GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera)
   : mCameraId(aCameraId)
   , mClosing(false)
   , mNumFrames(0)
+#ifdef MOZ_WIDGET_GONK
   , mCamera(aCamera)
+#endif
   , mTarget(aTarget)
+  , mRawSensorOrientation(0)
   , mSensorOrientation(0)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget);
 }
 
 void
 GonkCameraHardware::OnRateLimitPreview(bool aLimit)
 {
   ::OnRateLimitPreview(mTarget, aLimit);
 }
 
+#ifdef MOZ_WIDGET_GONK
 void
 GonkCameraHardware::OnNewFrame()
 {
   if (mClosing) {
     return;
   }
   RefPtr<TextureClient> buffer = mNativeWindow->getCurrentBuffer();
   if (!buffer) {
@@ -145,22 +156,24 @@ GonkCameraHardware::postDataTimestamp(ns
       DOM_CAMERA_LOGW("Listener unable to process. Drop a recording frame.");
       mCamera->releaseRecordingFrame(aDataPtr);
     }
   } else {
     DOM_CAMERA_LOGW("No listener was set. Drop a recording frame.");
     mCamera->releaseRecordingFrame(aDataPtr);
   }
 }
+#endif
 
 nsresult
 GonkCameraHardware::Init()
 {
   DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this);
 
+#ifdef MOZ_WIDGET_GONK
   CameraInfo info;
   int rv = Camera::getCameraInfo(mCameraId, &info);
   if (rv != 0) {
     DOM_CAMERA_LOGE("%s: failed to get CameraInfo mCameraId %d\n", __func__, mCameraId);
     return NS_ERROR_NOT_INITIALIZED;
    }
 
   mRawSensorOrientation = info.orientation;
@@ -179,18 +192,16 @@ GonkCameraHardware::Init()
     mSensorOrientation += offset;
     mSensorOrientation %= 360;
   }
   DOM_CAMERA_LOGI("Sensor orientation: base=%d, offset=%d, final=%d\n", info.orientation, offset, mSensorOrientation);
 
   // Disable shutter sound in android CameraService because gaia camera app will play it
   mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
 
-#if defined(MOZ_WIDGET_GONK)
-
 #if ANDROID_VERSION >= 21
   sp<IGraphicBufferProducer> producer;
   sp<IGonkGraphicBufferConsumer> consumer;
   GonkBufferQueue::createBufferQueue(&producer, &consumer);
   static_cast<GonkBufferQueueProducer*>(producer.get())->setSynchronousMode(false);
   mNativeWindow = new GonkNativeWindow(consumer, GonkCameraHardware::MIN_UNDEQUEUED_BUFFERS);
   mCamera->setPreviewTarget(producer);
 #elif ANDROID_VERSION >= 19
@@ -226,21 +237,23 @@ sp<GonkCameraHardware>
 GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId)
 {
   sp<Camera> camera;
 
   nsCString test;
   CameraPreferences::GetPref("camera.control.test.enabled", test);
 
   if (!test.EqualsASCII("hardware")) {
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
+#ifdef MOZ_WIDGET_GONK
+#if ANDROID_VERSION >= 18
     camera = Camera::connect(aCameraId, /* clientPackageName */String16("gonk.camera"), Camera::USE_CALLING_UID);
 #else
     camera = Camera::connect(aCameraId);
 #endif
+#endif
 
     if (camera.get() == nullptr) {
       return nullptr;
     }
   }
 
   sp<GonkCameraHardware> cameraHardware;
   if (test.EqualsASCII("hardware")) {
@@ -265,31 +278,35 @@ GonkCameraHardware::Close()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
 
   mClosing = true;
   if (mCamera.get()) {
     mCamera->stopPreview();
     mCamera->disconnect();
   }
+  mCamera.clear();
+#ifdef MOZ_WIDGET_GONK
   if (mNativeWindow.get()) {
     mNativeWindow->abandon();
   }
-  mCamera.clear();
   mNativeWindow.clear();
 
   // Ensure that ICamera's destructor is actually executed
   IPCThreadState::self()->flushCommands();
+#endif
 }
 
 GonkCameraHardware::~GonkCameraHardware()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
   mCamera.clear();
+#ifdef MOZ_WIDGET_GONK
   mNativeWindow.clear();
+#endif
 }
 
 int
 GonkCameraHardware::GetSensorOrientation(uint32_t aType)
 {
   DOM_CAMERA_LOGI("%s\n", __func__);
 
   switch (aType) {
@@ -365,36 +382,38 @@ GonkCameraHardware::CancelTakePicture()
 
 int
 GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
 {
   const String8 s = aParams.Flatten();
   return mCamera->setParameters(s);
 }
 
+nsresult
+GonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
+{
+  const String8 s = mCamera->getParameters();
+  return aParams.Unflatten(s);
+}
+
+#ifdef MOZ_WIDGET_GONK
 int
 GonkCameraHardware::PushParameters(const CameraParameters& aParams)
 {
   String8 s = aParams.flatten();
   return mCamera->setParameters(s);
 }
 
-nsresult
-GonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
-{
-  const String8 s = mCamera->getParameters();
-  return aParams.Unflatten(s);
-}
-
 void
 GonkCameraHardware::PullParameters(CameraParameters& aParams)
 {
   const String8 s = mCamera->getParameters();
   aParams.unflatten(s);
 }
+#endif
 
 int
 GonkCameraHardware::StartPreview()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   return mCamera->startPreview();
 }
 
@@ -421,26 +440,28 @@ GonkCameraHardware::StartRecording()
 int
 GonkCameraHardware::StopRecording()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mCamera->stopRecording();
   return OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 int
 GonkCameraHardware::SetListener(const sp<GonkCameraListener>& aListener)
 {
   mListener = aListener;
   return OK;
 }
 
 void
 GonkCameraHardware::ReleaseRecordingFrame(const sp<IMemory>& aFrame)
 {
   mCamera->releaseRecordingFrame(aFrame);
 }
+#endif
 
 int
 GonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled)
 {
   return mCamera->storeMetaDataInBuffers(aEnabled);
 }
--- a/dom/camera/GonkCameraHwMgr.h
+++ b/dom/camera/GonkCameraHwMgr.h
@@ -12,39 +12,51 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERAHWMGR_H
 #define DOM_CAMERA_GONKCAMERAHWMGR_H
 
+#include "GonkCameraControl.h"
+#include "CameraCommon.h"
+#include "GonkCameraParameters.h"
+#include "mozilla/ReentrantMonitor.h"
+
+#ifdef MOZ_WIDGET_GONK
 #include <binder/IMemory.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
 #include <utils/threads.h>
-
-#include "GonkCameraControl.h"
-#include "CameraCommon.h"
-
 #include "GonkCameraListener.h"
 #include "GonkNativeWindow.h"
-#include "GonkCameraParameters.h"
-#include "mozilla/ReentrantMonitor.h"
+#else
+#include "FallbackCameraPlatform.h"
+#endif
 
 namespace mozilla {
   class nsGonkCameraControl;
   class GonkCameraParameters;
 }
 
 namespace android {
 
-class GonkCameraHardware : public GonkNativeWindowNewFrameCallback
-                         , public CameraListener
+class GonkCameraHardware
+#ifdef MOZ_WIDGET_GONK
+  : public GonkNativeWindowNewFrameCallback
+  , public CameraListener
+#else
+  : public nsISupports
+#endif
 {
+#ifndef MOZ_WIDGET_GONK
+  NS_DECL_ISUPPORTS
+#endif
+
 protected:
   GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera);
   virtual ~GonkCameraHardware();
 
   // Initialize the AOSP camera interface.
   //
   // Return values:
   //  - NS_OK on success;
@@ -52,23 +64,25 @@ protected:
   virtual nsresult Init();
 
 public:
   static sp<GonkCameraHardware> Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId);
   virtual void Close();
 
   virtual void OnRateLimitPreview(bool aLimit);
 
+#ifdef MOZ_WIDGET_GONK
   // derived from GonkNativeWindowNewFrameCallback
   virtual void OnNewFrame() MOZ_OVERRIDE;
 
   // derived from CameraListener
   virtual void notify(int32_t aMsgType, int32_t ext1, int32_t ext2);
   virtual void postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, camera_frame_metadata_t* metadata);
   virtual void postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory>& aDataPtr);
+#endif
 
   /**
    * The physical orientation of the camera sensor: 0, 90, 180, or 270.
    *
    * For example, suppose a device has a naturally tall screen. The
    * back-facing camera sensor is mounted in landscape. You are looking at
    * the screen. If the top side of the camera sensor is aligned with the
    * right edge of the screen in natural orientation, the value should be
@@ -98,33 +112,37 @@ public:
   virtual int      CancelAutoFocus();
   virtual int      StartFaceDetection();
   virtual int      StopFaceDetection();
   virtual int      TakePicture();
   virtual void     CancelTakePicture();
   virtual int      StartPreview();
   virtual void     StopPreview();
   virtual int      PushParameters(const mozilla::GonkCameraParameters& aParams);
+  virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
+#ifdef MOZ_WIDGET_GONK
   virtual int      PushParameters(const CameraParameters& aParams);
-  virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
   virtual void     PullParameters(CameraParameters& aParams);
+  virtual int      SetListener(const sp<GonkCameraListener>& aListener);
+  virtual void     ReleaseRecordingFrame(const sp<IMemory>& aFrame);
+#endif
   virtual int      StartRecording();
   virtual int      StopRecording();
-  virtual int      SetListener(const sp<GonkCameraListener>& aListener);
-  virtual void     ReleaseRecordingFrame(const sp<IMemory>& aFrame);
   virtual int      StoreMetaDataInBuffers(bool aEnabled);
 
 protected:
   uint32_t                      mCameraId;
   bool                          mClosing;
   uint32_t                      mNumFrames;
   sp<Camera>                    mCamera;
   mozilla::nsGonkCameraControl* mTarget;
+#ifdef MOZ_WIDGET_GONK
   sp<GonkNativeWindow>          mNativeWindow;
   sp<GonkCameraListener>        mListener;
+#endif
   int                           mRawSensorOrientation;
   int                           mSensorOrientation;
 
 private:
   GonkCameraHardware(const GonkCameraHardware&) = delete;
   GonkCameraHardware& operator=(const GonkCameraHardware&) = delete;
 };
 
--- a/dom/camera/GonkCameraManager.cpp
+++ b/dom/camera/GonkCameraManager.cpp
@@ -10,24 +10,27 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "ICameraControl.h"
-
-#include <camera/Camera.h>
-
 #include "CameraCommon.h"
 #include "GonkCameraControl.h"
 #include "CameraPreferences.h"
 #include "TestGonkCameraControl.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include <camera/Camera.h>
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
 using namespace mozilla;
 
 // From ICameraControl, gonk-specific management functions
 nsresult
 ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
 {
   aDeviceCount = android::Camera::getNumberOfCameras();
   return NS_OK;
--- a/dom/camera/GonkCameraParameters.cpp
+++ b/dom/camera/GonkCameraParameters.cpp
@@ -10,22 +10,22 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkCameraParameters.h"
-#include "camera/CameraParameters.h"
 #include "CameraPreferences.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 #include "mozilla/Hal.h"
 #include "nsDataHashtable.h"
+#include "nsPrintfCString.h"
 
 using namespace mozilla;
 using namespace android;
 
 /* static */ bool
 GonkCameraParameters::IsLowMemoryPlatform()
 {
   bool testIsLowMem = false;
@@ -47,189 +47,246 @@ GonkCameraParameters::IsLowMemoryPlatfor
       return true;
     }
   }
 
   return false;
 }
 
 const char*
-GonkCameraParameters::Parameters::FindVendorSpecificKey(const char* aPotentialKeys[],
-                                                        size_t aPotentialKeyCount)
+GonkCameraParameters::FindVendorSpecificKey(const char* aPotentialKeys[],
+                                            size_t aPotentialKeyCount)
 {
   const char* val;
 
   for (size_t i = 0; i < aPotentialKeyCount; ++i) {
-    get(aPotentialKeys[i], val);
+    GetImpl(aPotentialKeys[i], val);
     if (val) {
       // We received a value (potentially an empty-string one),
       // which indicates that this key exists.
       return aPotentialKeys[i];
     }
   }
 
   return nullptr;
 }
 
+/* static */ PLDHashOperator
+GonkCameraParameters::EnumerateFlatten(const nsACString& aKey,
+                                       nsCString* aValue,
+                                       void* aUserArg)
+{
+  nsCString* data = static_cast<nsCString*>(aUserArg);
+  if (!data->IsEmpty()) {
+    data->Append(';');
+  }
+  data->Append(aKey);
+  data->Append('=');
+  data->Append(*aValue);
+  return PL_DHASH_NEXT;
+}
+
+String8
+GonkCameraParameters::Flatten() const
+{
+  MutexAutoLock lock(mLock);
+  nsCString data;
+  mParams.EnumerateRead(EnumerateFlatten, static_cast<void*>(&data));
+  return String8(data.Data());
+}
+
+nsresult
+GonkCameraParameters::Unflatten(const String8& aFlatParameters)
+{
+  MutexAutoLock lock(mLock);
+  mParams.Clear();
+
+  const char* data = aFlatParameters.string();
+  while (data && *data) {
+    const char* pos = strchr(data, '=');
+    if (!pos) {
+      break;
+    }
+
+    nsAutoCString key(data, pos - data);
+    data = pos + 1;
+
+    nsCString* value;
+    pos = strchr(data, ';');
+    if (pos) {
+      value = new nsCString(data, pos - data);
+      data = pos + 1;
+    } else {
+      value = new nsCString(data);
+      data = nullptr;
+    }
+
+    mParams.Put(key, value);
+  }
+
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  // We call Initialize() once when the parameter set is first loaded,
+  // to set up any constant values this class requires internally,
+  // e.g. the exposure compensation step and limits.
+  return Initialize();
+}
+
 const char*
-GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
+GonkCameraParameters::GetTextKey(uint32_t aKey)
 {
   switch (aKey) {
     case CAMERA_PARAM_PREVIEWSIZE:
-      return KEY_PREVIEW_SIZE;
+      return CameraParameters::KEY_PREVIEW_SIZE;
     case CAMERA_PARAM_PREVIEWFORMAT:
-      return KEY_PREVIEW_FORMAT;
+      return CameraParameters::KEY_PREVIEW_FORMAT;
     case CAMERA_PARAM_PREVIEWFRAMERATE:
-      return KEY_PREVIEW_FRAME_RATE;
+      return CameraParameters::KEY_PREVIEW_FRAME_RATE;
     case CAMERA_PARAM_EFFECT:
-      return KEY_EFFECT;
+      return CameraParameters::KEY_EFFECT;
     case CAMERA_PARAM_WHITEBALANCE:
-      return KEY_WHITE_BALANCE;
+      return CameraParameters::KEY_WHITE_BALANCE;
     case CAMERA_PARAM_SCENEMODE:
-      return KEY_SCENE_MODE;
+      return CameraParameters::KEY_SCENE_MODE;
     case CAMERA_PARAM_FLASHMODE:
-      return KEY_FLASH_MODE;
+      return CameraParameters::KEY_FLASH_MODE;
     case CAMERA_PARAM_FOCUSMODE:
-      return KEY_FOCUS_MODE;
+      return CameraParameters::KEY_FOCUS_MODE;
     case CAMERA_PARAM_ZOOM:
-      return KEY_ZOOM;
+      return CameraParameters::KEY_ZOOM;
     case CAMERA_PARAM_METERINGAREAS:
-      return KEY_METERING_AREAS;
+      return CameraParameters::KEY_METERING_AREAS;
     case CAMERA_PARAM_FOCUSAREAS:
-      return KEY_FOCUS_AREAS;
+      return CameraParameters::KEY_FOCUS_AREAS;
     case CAMERA_PARAM_FOCALLENGTH:
-      return KEY_FOCAL_LENGTH;
+      return CameraParameters::KEY_FOCAL_LENGTH;
     case CAMERA_PARAM_FOCUSDISTANCENEAR:
-      return KEY_FOCUS_DISTANCES;
+      return CameraParameters::KEY_FOCUS_DISTANCES;
     case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
-      return KEY_FOCUS_DISTANCES;
+      return CameraParameters::KEY_FOCUS_DISTANCES;
     case CAMERA_PARAM_FOCUSDISTANCEFAR:
-      return KEY_FOCUS_DISTANCES;
+      return CameraParameters::KEY_FOCUS_DISTANCES;
     case CAMERA_PARAM_EXPOSURECOMPENSATION:
-      return KEY_EXPOSURE_COMPENSATION;
+      return CameraParameters::KEY_EXPOSURE_COMPENSATION;
     case CAMERA_PARAM_THUMBNAILQUALITY:
-      return KEY_JPEG_THUMBNAIL_QUALITY;
+      return CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY;
     case CAMERA_PARAM_PICTURE_SIZE:
-      return KEY_PICTURE_SIZE;
+      return CameraParameters::KEY_PICTURE_SIZE;
     case CAMERA_PARAM_PICTURE_FILEFORMAT:
-      return KEY_PICTURE_FORMAT;
+      return CameraParameters::KEY_PICTURE_FORMAT;
     case CAMERA_PARAM_PICTURE_ROTATION:
-      return KEY_ROTATION;
+      return CameraParameters::KEY_ROTATION;
     case CAMERA_PARAM_PICTURE_DATETIME:
-      // Not every platform defines a KEY_EXIF_DATETIME;
+      // Not every platform defines a CameraParameters::EXIF_DATETIME;
       // for those that don't, we use the raw string key, and if the platform
       // doesn't support it, it will be ignored.
       //
       // See bug 832494.
       return "exif-datetime";
     case CAMERA_PARAM_VIDEOSIZE:
-      return KEY_VIDEO_SIZE;
+      return CameraParameters::KEY_VIDEO_SIZE;
     case CAMERA_PARAM_ISOMODE:
       if (!mVendorSpecificKeyIsoMode) {
         const char* isoModeKeys[] = {
           "iso",
           "sony-iso"
         };
         mVendorSpecificKeyIsoMode =
           FindVendorSpecificKey(isoModeKeys, MOZ_ARRAY_LENGTH(isoModeKeys));
       }
       return mVendorSpecificKeyIsoMode;
     case CAMERA_PARAM_LUMINANCE:
       return "luminance-condition";
     case CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE:
-      // Not every platform defines KEY_QC_HDR_NEED_1X;
+      // Not every platform defines CameraParameters::QC_HDR_NEED_1X;
       // for those that don't, we use the raw string key.
       return "hdr-need-1x";
     case CAMERA_PARAM_RECORDINGHINT:
-      return KEY_RECORDING_HINT;
+      return CameraParameters::KEY_RECORDING_HINT;
     case CAMERA_PARAM_PICTURE_QUALITY:
-      return KEY_JPEG_QUALITY;
+      return CameraParameters::KEY_JPEG_QUALITY;
     case CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO:
-      return KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO;
+      return CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO;
     case CAMERA_PARAM_METERINGMODE:
-      // Not every platform defines KEY_AUTO_EXPOSURE.
+      // Not every platform defines CameraParameters::AUTO_EXPOSURE.
       return "auto-exposure";
 
     case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
-      return KEY_SUPPORTED_PREVIEW_SIZES;
+      return CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES;
     case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
-      return KEY_SUPPORTED_PICTURE_SIZES;
+      return CameraParameters::KEY_SUPPORTED_PICTURE_SIZES;
     case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
-      return KEY_SUPPORTED_VIDEO_SIZES;
+      return CameraParameters::KEY_SUPPORTED_VIDEO_SIZES;
     case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
-      return KEY_SUPPORTED_PICTURE_FORMATS;
+      return CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS;
     case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
-      return KEY_SUPPORTED_WHITE_BALANCE;
+      return CameraParameters::KEY_SUPPORTED_WHITE_BALANCE;
     case CAMERA_PARAM_SUPPORTED_SCENEMODES:
-      return KEY_SUPPORTED_SCENE_MODES;
+      return CameraParameters::KEY_SUPPORTED_SCENE_MODES;
     case CAMERA_PARAM_SUPPORTED_EFFECTS:
-      return KEY_SUPPORTED_EFFECTS;
+      return CameraParameters::KEY_SUPPORTED_EFFECTS;
     case CAMERA_PARAM_SUPPORTED_FLASHMODES:
-      return KEY_SUPPORTED_FLASH_MODES;
+      return CameraParameters::KEY_SUPPORTED_FLASH_MODES;
     case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
-      return KEY_SUPPORTED_FOCUS_MODES;
+      return CameraParameters::KEY_SUPPORTED_FOCUS_MODES;
     case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
-      return KEY_MAX_NUM_FOCUS_AREAS;
+      return CameraParameters::KEY_MAX_NUM_FOCUS_AREAS;
     case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
-      return KEY_MAX_NUM_METERING_AREAS;
+      return CameraParameters::KEY_MAX_NUM_METERING_AREAS;
     case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
-      return KEY_MIN_EXPOSURE_COMPENSATION;
+      return CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION;
     case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
-      return KEY_MAX_EXPOSURE_COMPENSATION;
+      return CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION;
     case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
-      return KEY_EXPOSURE_COMPENSATION_STEP;
+      return CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP;
     case CAMERA_PARAM_SUPPORTED_ZOOM:
-      return KEY_ZOOM_SUPPORTED;
+      return CameraParameters::KEY_ZOOM_SUPPORTED;
     case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
-      return KEY_ZOOM_RATIOS;
+      return CameraParameters::KEY_ZOOM_RATIOS;
     case CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES:
-      return KEY_MAX_NUM_DETECTED_FACES_HW;
+      return CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW;
     case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
-      return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
+      return CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
     case CAMERA_PARAM_SUPPORTED_ISOMODES:
       if (!mVendorSpecificKeySupportedIsoModes) {
         const char* supportedIsoModesKeys[] = {
           "iso-values",
           "sony-iso-values"
         };
         mVendorSpecificKeySupportedIsoModes =
           FindVendorSpecificKey(supportedIsoModesKeys,
                                 MOZ_ARRAY_LENGTH(supportedIsoModesKeys));
       }
       return mVendorSpecificKeySupportedIsoModes;
     case CAMERA_PARAM_SUPPORTED_METERINGMODES:
-      // Not every platform defines KEY_SUPPORTED_AUTO_EXPOSURE.
+      // Not every platform defines CameraParameters::SUPPORTED_AUTO_EXPOSURE.
       return "auto-exposure-values";
     default:
       DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey);
       return nullptr;
   }
 }
 
 GonkCameraParameters::GonkCameraParameters()
-  : mLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraParameters.Lock"))
+  : mLock("mozilla::camera::GonkCameraParameters")
   , mDirty(false)
   , mInitialized(false)
   , mExposureCompensationStep(0.0)
+  , mVendorSpecificKeyIsoMode(nullptr)
+  , mVendorSpecificKeySupportedIsoModes(nullptr)
 {
   MOZ_COUNT_CTOR(GonkCameraParameters);
-  if (!mLock) {
-    MOZ_CRASH("Out of memory getting new PRRWLock");
-  }
 }
 
 GonkCameraParameters::~GonkCameraParameters()
 {
   MOZ_COUNT_DTOR(GonkCameraParameters);
   mIsoModeMap.Clear();
-  MOZ_ASSERT(mLock, "mLock missing in ~GonkCameraParameters()");
-  if (mLock) {
-    PR_DestroyRWLock(mLock);
-    mLock = nullptr;
-  }
 }
 
 nsresult
 GonkCameraParameters::MapIsoToGonk(const nsAString& aIso, nsACString& aIsoOut)
 {
   nsCString* s;
   if (mIsoModeMap.Get(aIso, &s)) {
     if (!s) {
@@ -273,27 +330,27 @@ GonkCameraParameters::MapIsoFromGonk(con
 
 // Any members that need to be initialized on the first parameter pull
 // need to get handled in here.
 nsresult
 GonkCameraParameters::Initialize()
 {
   nsresult rv;
 
-  rv = GetImpl(Parameters::KEY_EXPOSURE_COMPENSATION_STEP, mExposureCompensationStep);
+  rv = GetImpl(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP, mExposureCompensationStep);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize exposure compensation step size");
     mExposureCompensationStep = 0.0;
   }
-  rv = GetImpl(Parameters::KEY_MIN_EXPOSURE_COMPENSATION, mExposureCompensationMinIndex);
+  rv = GetImpl(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, mExposureCompensationMinIndex);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize minimum exposure compensation index");
     mExposureCompensationMinIndex = 0;
   }
-  rv = GetImpl(Parameters::KEY_MAX_EXPOSURE_COMPENSATION, mExposureCompensationMaxIndex);
+  rv = GetImpl(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, mExposureCompensationMaxIndex);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to initialize maximum exposure compensation index");
     mExposureCompensationMaxIndex = 0;
   }
 
   rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
   if (NS_FAILED(rv)) {
     // zoom is not supported
@@ -420,19 +477,19 @@ GonkCameraParameters::SetTranslated(uint
 
   nsresult rv;
 
   switch (aKey) {
     case CAMERA_PARAM_THUMBNAILSIZE:
       // This is a special case--for some reason the thumbnail size
       // is accessed as two separate values instead of a tuple.
       // XXXmikeh - make this restore the original values on error
-      rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(aSize.width));
+      rv = SetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(aSize.width));
       if (NS_SUCCEEDED(rv)) {
-        rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(aSize.height));
+        rv = SetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(aSize.height));
       }
       break;
 
     case CAMERA_PARAM_VIDEOSIZE:
       // "record-size" is probably deprecated in later ICS;
       // might need to set "video-size" instead of "record-size";
       // for the time being, set both. See bug 795332.
       rv = SetImpl("record-size", nsPrintfCString("%ux%u", aSize.width, aSize.height).get());
@@ -456,24 +513,24 @@ nsresult
 GonkCameraParameters::GetTranslated(uint32_t aKey, ICameraControl::Size& aSize)
 {
   nsresult rv;
 
   if (aKey == CAMERA_PARAM_THUMBNAILSIZE) {
     int width;
     int height;
 
-    rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, width);
+    rv = GetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, width);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (width < 0) {
       return NS_ERROR_NOT_AVAILABLE;
     }
-    rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, height);
+    rv = GetImpl(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, height);
     if (NS_FAILED(rv)) {
       return rv;
     }
     if (height < 0) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     aSize.width = static_cast<uint32_t>(width);
@@ -567,37 +624,37 @@ GonkCameraParameters::GetTranslated(uint
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition)
 {
   MOZ_ASSERT(aKey == CAMERA_PARAM_PICTURE_LOCATION);
 
   // Add any specified location information -- we don't care if these fail.
   if (!isnan(aPosition.latitude)) {
     DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aPosition.latitude);
-    SetImpl(Parameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get());
+    SetImpl(CameraParameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_LATITUDE);
+    ClearImpl(CameraParameters::KEY_GPS_LATITUDE);
   }
   if (!isnan(aPosition.longitude)) {
     DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aPosition.longitude);
-    SetImpl(Parameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get());
+    SetImpl(CameraParameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_LONGITUDE);
+    ClearImpl(CameraParameters::KEY_GPS_LONGITUDE);
   }
   if (!isnan(aPosition.altitude)) {
     DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aPosition.altitude);
-    SetImpl(Parameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get());
+    SetImpl(CameraParameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_ALTITUDE);
+    ClearImpl(CameraParameters::KEY_GPS_ALTITUDE);
   }
   if (!isnan(aPosition.timestamp)) {
     DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aPosition.timestamp);
-    SetImpl(Parameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get());
+    SetImpl(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get());
   } else {
-    ClearImpl(Parameters::KEY_GPS_TIMESTAMP);
+    ClearImpl(CameraParameters::KEY_GPS_TIMESTAMP);
   }
 
   return NS_OK;
 }
 
 // Handle int64_ts
 nsresult
 GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue)
--- a/dom/camera/GonkCameraParameters.h
+++ b/dom/camera/GonkCameraParameters.h
@@ -13,24 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef DOM_CAMERA_GONKCAMERAPARAMETERS_H
 #define DOM_CAMERA_GONKCAMERAPARAMETERS_H
 
 #include <math.h>
-#include "camera/CameraParameters.h"
 #include "nsTArray.h"
 #include "nsString.h"
-#include "AutoRwLock.h"
-#include "nsPrintfCString.h"
+#include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
 #include "ICameraControl.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include <camera/CameraParameters.h>
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
 namespace mozilla {
 
 class GonkCameraParameters
 {
 public:
   GonkCameraParameters();
   virtual ~GonkCameraParameters();
 
@@ -39,166 +43,198 @@ public:
   // for the life of their operation. Not doing so was the cause of
   // bug 928856, which was -painful- to track down.
   //
   // Return values:
   //  - see return values for GetTranslated() and SetTranslated() below.
   template<class T> nsresult
   Set(uint32_t aKey, const T& aValue)
   {
-    RwLockAutoEnterWrite lock(mLock);
+    MutexAutoLock lock(mLock);
     nsresult rv = SetTranslated(aKey, aValue);
     mDirty = mDirty || NS_SUCCEEDED(rv);
     return rv;
   }
 
   template<class T> nsresult
   Get(uint32_t aKey, T& aValue)
   {
-    RwLockAutoEnterRead lock(mLock);
+    MutexAutoLock lock(mLock);
     return GetTranslated(aKey, aValue);
   }
 
   bool
   TestAndClearDirtyFlag()
   {
     bool dirty;
 
-    RwLockAutoEnterWrite lock(mLock);
+    MutexAutoLock lock(mLock);
     dirty = mDirty;
     mDirty = false;
     return dirty;
   }
 
-  android::String8
-  Flatten() const
-  {
-    RwLockAutoEnterRead lock(mLock);
-    return mParams.flatten();
-  }
-
-  nsresult
-  Unflatten(const android::String8& aFlatParameters)
-  {
-    RwLockAutoEnterWrite lock(mLock);
-    mParams.unflatten(aFlatParameters);
-    if (mInitialized) {
-      return NS_OK;
-    }
-
-    // We call Initialize() once when the parameter set is first loaded,
-    // to set up any constant values this class requires internally,
-    // e.g. the exposure compensation step and limits.
-    return Initialize();
-  }
+  android::String8 Flatten() const;
+  nsresult Unflatten(const android::String8& aFlatParameters);
 
 protected:
-  PRRWLock* mLock;
+  mutable Mutex mLock;
   bool mDirty;
   bool mInitialized;
 
   // Required internal properties
   double mExposureCompensationStep;
   int32_t mExposureCompensationMinIndex;
   int32_t mExposureCompensationMaxIndex;
+  const char* mVendorSpecificKeyIsoMode;
+  const char* mVendorSpecificKeySupportedIsoModes;
   nsTArray<int> mZoomRatios;
   nsTArray<nsString> mIsoModes;
   nsTArray<nsString> mSceneModes;
   nsTArray<nsString> mMeteringModes;
   nsClassHashtable<nsStringHashKey, nsCString> mIsoModeMap;
+  nsClassHashtable<nsCStringHashKey, nsCString> mParams;
 
-  // This subclass of android::CameraParameters just gives
-  // all of the AOSP getters and setters the same signature.
-  class Parameters : public android::CameraParameters
+  static PLDHashOperator EnumerateFlatten(const nsACString& aKey, nsCString* aValue, void* aUserArg);
+
+  nsresult SetImpl(const char* aKey, const char* aValue)
+  {
+    nsCString key(aKey);
+    mParams.Put(key, new nsCString(aValue));
+    return NS_OK;
+  }
+
+  nsresult SetImpl(const char* aKey, int aValue)
+  {
+    nsCString key(aKey);
+    nsCString* value = new nsCString();
+    value->AppendInt(aValue);
+    mParams.Put(key, value);
+    return NS_OK;
+  }
+
+  nsresult SetImpl(const char* aKey, double aValue)
   {
-  public:
-    Parameters()
-      : mVendorSpecificKeyIsoMode(nullptr)
-      , mVendorSpecificKeySupportedIsoModes(nullptr)
-    { }
-    virtual ~Parameters() { }
+    nsCString key(aKey);
+    nsCString* value = new nsCString();
+    value->AppendFloat(aValue);
+    mParams.Put(key, value);
+    return NS_OK;
+  }
 
-    using android::CameraParameters::set;
-    using android::CameraParameters::get;
-    using android::CameraParameters::TRUE;
-    using android::CameraParameters::FALSE;
+  nsresult SetImpl(const char* aKey, float aValue)
+  {
+    nsCString key(aKey);
+    nsCString* value = new nsCString();
+    value->AppendFloat(aValue);
+    mParams.Put(key, value);
+    return NS_OK;
+  }
+
+  nsresult SetImpl(const char* aKey, bool aValue)
+  {
+    nsCString key(aKey);
+    mParams.Put(key, new nsCString(aValue ? "true" : "false"));
+    return NS_OK;
+  }
 
-    void set(const char* aKey, float aValue)      { setFloat(aKey, aValue); }
-    void set(const char* aKey, double aValue)     { setFloat(aKey, aValue); }
-    void set(const char* aKey, bool aValue)       { set(aKey, aValue ? TRUE : FALSE); }
-    void get(const char* aKey, float& aRet)       { aRet = getFloat(aKey); }
-    void get(const char* aKey, double& aRet)      { aRet = getFloat(aKey); }
-    void get(const char* aKey, const char*& aRet) { aRet = get(aKey); }
-    void get(const char* aKey, int& aRet)         { aRet = getInt(aKey); }
+  nsresult GetImpl(const char* aKey, const char*& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    if (!mParams.Get(key, &value)) {
+      aRet = nullptr;
+      return NS_ERROR_FAILURE;
+    }
+    aRet = value->Data();
+    return NS_OK;
+  }
 
-    void
-    get(const char* aKey, bool& aRet)
-    {
-      const char* value = get(aKey);
-      aRet = value ? strcmp(value, TRUE) == 0 : false;
+  nsresult GetImpl(const char* aKey, float& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    nsresult rv = NS_ERROR_FAILURE;
+    if (mParams.Get(key, &value)) {
+      aRet = value->ToFloat(&rv);
+    } else {
+      aRet = 0.0;
     }
+    return rv;
+  }
 
-    void remove(const char* aKey)                 { android::CameraParameters::remove(aKey); }
-
-    const char* GetTextKey(uint32_t aKey);
+  nsresult GetImpl(const char* aKey, double& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    nsresult rv = NS_ERROR_FAILURE;
+    if (mParams.Get(key, &value)) {
+      aRet = value->ToFloat(&rv);
+    } else {
+      aRet = 0.0;
+    }
+    return rv;
+  }
 
-  protected:
-    const char* FindVendorSpecificKey(const char* aPotentialKeys[], size_t aPotentialKeyCount);
+  nsresult GetImpl(const char* aKey, int& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    nsresult rv = NS_ERROR_FAILURE;
+    if (mParams.Get(key, &value)) {
+      aRet = value->ToInteger(&rv);
+    } else {
+      aRet = 0.0;
+    }
+    return rv;
+  }
 
-    const char* mVendorSpecificKeyIsoMode;
-    const char* mVendorSpecificKeySupportedIsoModes;
-  };
+  nsresult GetImpl(const char* aKey, bool& aRet)
+  {
+    nsCString key(aKey);
+    nsCString* value;
+    if (!mParams.Get(key, &value)) {
+      aRet = false;
+      return NS_ERROR_FAILURE;
+    }
+    aRet = value->EqualsLiteral("true");
+    return NS_OK;
+  }
 
-  Parameters mParams;
+  const char* GetTextKey(uint32_t aKey);
+  const char* FindVendorSpecificKey(const char* aPotentialKeys[], size_t aPotentialKeyCount);
 
   // The *Impl() templates handle converting the parameter keys from
   // their enum values to string types, if necessary. These are the
   // bottom layer accessors to mParams.
   //
   // Return values:
   //  - NS_OK on success;
   //  - NS_ERROR_NOT_IMPLEMENTED if the numeric 'aKey' value is invalid.
   template<typename T> nsresult
   SetImpl(uint32_t aKey, const T& aValue)
   {
-    const char* key = mParams.GetTextKey(aKey);
+    const char* key = GetTextKey(aKey);
     NS_ENSURE_TRUE(key, NS_ERROR_NOT_IMPLEMENTED);
-
-    mParams.set(key, aValue);
-    return NS_OK;
+    return SetImpl(key, aValue);
   }
 
   template<typename T> nsresult
   GetImpl(uint32_t aKey, T& aValue)
   {
-    const char* key = mParams.GetTextKey(aKey);
+    const char* key = GetTextKey(aKey);
     NS_ENSURE_TRUE(key, NS_ERROR_NOT_IMPLEMENTED);
-
-    mParams.get(key, aValue);
-    return NS_OK;
-  }
-
-  template<class T> nsresult
-  SetImpl(const char* aKey, const T& aValue)
-  {
-    mParams.set(aKey, aValue);
-    return NS_OK;
-  }
-
-  template<class T> nsresult
-  GetImpl(const char* aKey, T& aValue)
-  {
-    mParams.get(aKey, aValue);
-    return NS_OK;
+    return GetImpl(key, aValue);
   }
 
   nsresult
   ClearImpl(const char* aKey)
   {
-    mParams.remove(aKey);
+    nsCString key(aKey);
+    mParams.Remove(key);
     return NS_OK;
   }
 
   // The *Translated() functions allow us to handle special cases;
   // for example, where the thumbnail size setting is exposed as an
   // ICameraControl::Size object, but is handled by the AOSP layer
   // as two separate parameters.
   //
--- a/dom/camera/GonkRecorderProfiles.cpp
+++ b/dom/camera/GonkRecorderProfiles.cpp
@@ -10,22 +10,24 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkRecorderProfiles.h"
-#include <media/MediaProfiles.h>
 #include "nsMimeTypes.h"
-#include "GonkRecorder.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include "GonkRecorder.h"
+#endif
+
 using namespace mozilla;
 using namespace android;
 
 namespace mozilla {
 
 struct ProfileConfig {
   const char* name;
   int quality;
@@ -369,20 +371,21 @@ GonkRecorderProfile::GetAll(uint32_t aCa
 {
   ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
   if (!profiles) {
     return NS_ERROR_FAILURE;
   }
 
   aProfiles.Clear();
   profiles->EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
-  
+
   return NS_OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 nsresult
 GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
 {
   static const size_t SIZE = 256;
   char buffer[SIZE];
 
   // set all the params
   CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER));
@@ -420,8 +423,9 @@ GonkRecorderProfile::ConfigureRecorder(a
 
   GonkRecorderProfile* profile;
   if (!profiles->Get(aProfileName, &profile)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   return profile->ConfigureRecorder(aRecorder);
 }
+#endif
--- a/dom/camera/GonkRecorderProfiles.h
+++ b/dom/camera/GonkRecorderProfiles.h
@@ -1,16 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_GONK_RECORDER_PROFILES_H
 #define DOM_CAMERA_GONK_RECORDER_PROFILES_H
 
+#ifdef MOZ_WIDGET_GONK
 #include <media/MediaProfiles.h>
+#else
+#include "FallbackCameraPlatform.h"
+#endif
+
 #include "ICameraControl.h"
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #ifndef CHECK_SETARG_RETURN
 #define CHECK_SETARG_RETURN(x, rv)      \
   do {                                  \
     if (x) {                            \
@@ -104,37 +109,41 @@ typedef nsRefPtrHashtable<nsStringHashKe
 
 class GonkRecorderProfile
   : public GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>
 {
 public:
   static nsresult GetAll(uint32_t aCameraId,
                          nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles);
 
+#ifdef MOZ_WIDGET_GONK
   // Configures the specified recorder using the specified profile.
   //
   // Return values:
   //  - NS_OK on success;
   //  - NS_ERROR_INVALID_ARG if the profile isn't supported;
   //  - NS_ERROR_NOT_AVAILABLE if the recorder rejected the profile.
   static nsresult ConfigureRecorder(android::GonkRecorder& aRecorder,
                                     uint32_t aCameraId,
                                     const nsAString& aProfileName);
+#endif
 
 protected:
   GonkRecorderProfile(uint32_t aCameraId,
                       int aQuality);
 
   int GetProfileParameter(const char* aParameter);
 
   bool Translate(android::output_format aContainer, nsAString& aContainerName);
   bool GetMimeType(android::output_format aContainer, nsAString& aMimeType);
   bool IsValid() const { return mIsValid; };
 
+#ifdef MOZ_WIDGET_GONK
   nsresult ConfigureRecorder(android::GonkRecorder& aRecorder);
+#endif
   static already_AddRefed<GonkRecorderProfile> CreateProfile(uint32_t aCameraId,
                                                              int aQuality);
   static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId);
   static PLDHashOperator Enumerate(const nsAString& aProfileName,
                                    GonkRecorderProfile* aProfile,
                                    void* aUserArg);
 
   uint32_t mCameraId;
--- a/dom/camera/TestGonkCameraHardware.cpp
+++ b/dom/camera/TestGonkCameraHardware.cpp
@@ -26,16 +26,20 @@
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsICameraTestHardware.h"
 
 using namespace android;
 using namespace mozilla;
 using namespace mozilla::dom;
 
+#ifndef MOZ_WIDGET_GONK
+NS_IMPL_ISUPPORTS_INHERITED0(TestGonkCameraHardware, GonkCameraHardware);
+#endif
+
 static void
 CopyFaceFeature(int32_t (&aDst)[2], bool aExists, const DOMPoint* aSrc)
 {
   if (aExists && aSrc) {
     aDst[0] = static_cast<int32_t>(aSrc->X());
     aDst[1] = static_cast<int32_t>(aSrc->Y());
   } else {
     aDst[0] = -2000;
@@ -628,16 +632,17 @@ TestGonkCameraHardware::PullParameters(G
     return rv;
   }
 
   String8 s(NS_LossyConvertUTF16toASCII(as).get());
   aParams.Unflatten(s);
   return NS_OK;
 }
 
+#ifdef MOZ_WIDGET_GONK
 int
 TestGonkCameraHardware::PushParameters(const CameraParameters& aParams)
 {
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   String8 s = aParams.flatten();
   nsresult rv = WaitWhileRunningOnMainThread(new PushParametersDelegate(this, &s));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return UNKNOWN_ERROR;
@@ -653,16 +658,17 @@ TestGonkCameraHardware::PullParameters(C
   nsresult rv = WaitWhileRunningOnMainThread(new PullParametersDelegate(this, &as));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     as.Truncate();
   }
 
   String8 s(NS_LossyConvertUTF16toASCII(as).get());
   aParams.unflatten(s);
 }
+#endif
 
 int
 TestGonkCameraHardware::StartRecording()
 {
   class Delegate : public ControlMessage
   {
   public:
     Delegate(TestGonkCameraHardware* aTestHw)
--- a/dom/camera/TestGonkCameraHardware.h
+++ b/dom/camera/TestGonkCameraHardware.h
@@ -21,32 +21,38 @@
 #include "nsAutoPtr.h"
 #include "nsIDOMEventListener.h"
 #include "mozilla/CondVar.h"
 
 namespace mozilla {
 
 class TestGonkCameraHardware : public android::GonkCameraHardware
 {
+#ifndef MOZ_WIDGET_GONK
+  NS_DECL_ISUPPORTS_INHERITED
+#endif
+
 public:
   virtual nsresult Init() MOZ_OVERRIDE;
   virtual int AutoFocus() MOZ_OVERRIDE;
   virtual int StartFaceDetection() MOZ_OVERRIDE;
   virtual int StopFaceDetection() MOZ_OVERRIDE;
   virtual int TakePicture() MOZ_OVERRIDE;
   virtual void CancelTakePicture() MOZ_OVERRIDE;
   virtual int StartPreview() MOZ_OVERRIDE;
   virtual void StopPreview() MOZ_OVERRIDE;
   virtual int PushParameters(const mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE;
   virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE;
   virtual int StartRecording() MOZ_OVERRIDE;
   virtual int StopRecording() MOZ_OVERRIDE;
   virtual int StoreMetaDataInBuffers(bool aEnabled) MOZ_OVERRIDE;
+#ifdef MOZ_WIDGET_GONK
   virtual int PushParameters(const android::CameraParameters& aParams) MOZ_OVERRIDE;
   virtual void PullParameters(android::CameraParameters& aParams) MOZ_OVERRIDE;
+#endif
 
   TestGonkCameraHardware(mozilla::nsGonkCameraControl* aTarget,
                          uint32_t aCameraId,
                          const android::sp<android::Camera>& aCamera);
 
 protected:
   virtual ~TestGonkCameraHardware();
 
--- a/dom/camera/moz.build
+++ b/dom/camera/moz.build
@@ -1,15 +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/.
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+if CONFIG['MOZ_B2G_CAMERA']:
     MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'CameraCommon.h',
     'CameraPreferences.h',
     'DOMCameraManager.h',
 ]
 
@@ -31,23 +31,31 @@ if CONFIG['MOZ_B2G_CAMERA']:
 
     XPIDL_MODULE = 'dom_camera'
 
     UNIFIED_SOURCES += [
         'GonkCameraControl.cpp',
         'GonkCameraHwMgr.cpp',
         'GonkCameraManager.cpp',
         'GonkCameraParameters.cpp',
-        'GonkCameraSource.cpp',
-        'GonkRecorder.cpp',
         'GonkRecorderProfiles.cpp',
         'TestGonkCameraControl.cpp',
         'TestGonkCameraHardware.cpp',
     ]
 
+    if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+        UNIFIED_SOURCES += [
+            'GonkCameraSource.cpp',
+            'GonkRecorder.cpp',
+        ]
+    else:
+        UNIFIED_SOURCES += [
+            'FallbackCameraPlatform.cpp',
+        ]
+
     EXTRA_COMPONENTS += [
         'CameraTestHardware.js',
         'CameraTestHardware.manifest',
     ]
 else:
     UNIFIED_SOURCES += [
         'FallbackCameraControl.cpp',
         'FallbackCameraManager.cpp',
--- a/dom/camera/test/camera_common.js
+++ b/dom/camera/test/camera_common.js
@@ -63,22 +63,28 @@ function CameraTestSuite() {
   this.waitPreviewStarted = this._waitPreviewStarted.bind(this);
   this.waitParameterPush = this._waitParameterPush.bind(this);
   this.initJsHw = this._initJsHw.bind(this);
   this.getCamera = this._getCamera.bind(this);
   this.setLowMemoryPlatform = this._setLowMemoryPlatform.bind(this);
   this.logError = this._logError.bind(this);
   this.expectedError = this._expectedError.bind(this);
   this.expectedRejectGetCamera = this._expectedRejectGetCamera.bind(this);
+  this.expectedRejectConfigure = this._expectedRejectConfigure.bind(this);
   this.expectedRejectAutoFocus = this._expectedRejectAutoFocus.bind(this);
   this.expectedRejectTakePicture = this._expectedRejectTakePicture.bind(this);
+  this.expectedRejectStartRecording = this._expectedRejectStartRecording.bind(this);
+  this.expectedRejectStopRecording = this._expectedRejectStopRecording.bind(this);
   this.rejectGetCamera = this._rejectGetCamera.bind(this);
+  this.rejectConfigure = this._rejectConfigure.bind(this);
   this.rejectRelease = this._rejectRelease.bind(this);
   this.rejectAutoFocus = this._rejectAutoFocus.bind(this);
   this.rejectTakePicture = this._rejectTakePicture.bind(this);
+  this.rejectStartRecording = this._rejectStartRecording.bind(this);
+  this.rejectStopRecording = this._rejectStopRecording.bind(this);
   this.rejectPreviewStarted = this._rejectPreviewStarted.bind(this);
 
   var self = this;
   this._window.addEventListener('beforeunload', function() {
     if (isDefinedObj(self.viewfinder)) {
       self.viewfinder.mozSrcObject = null;
     }
 
@@ -90,30 +96,45 @@ function CameraTestSuite() {
     }
   });
 }
 
 CameraTestSuite.prototype = {
   camera: null,
   hw: null,
   _lowMemSet: false,
+  _reloading: false,
 
   /* Returns a promise which is resolved when the test suite is ready
      to be executing individual test cases. One may provide the expected
      hardware type here if desired; the default is to use the JS test
      hardware. Use '' for the native emulated camera hardware. */
   _setup: function(hwType) {
+    /* Depending on how we run the mochitest, we may not have the necessary
+       permissions yet. If we do need to request them, then we have to reload
+       the window to ensure the reconfiguration propogated properly. */
+    if (!SpecialPowers.hasPermission("camera", document)) {
+      info("requesting camera permission");
+      this._reloading = true;
+      SpecialPowers.addPermission("camera", true, document);
+      window.location.reload();
+      return Promise.reject();
+    }
+
+    info("has camera permission");
     if (!isDefined(hwType)) {
       hwType = 'hardware';
     }
 
     this._hwType = hwType;
     return new Promise(function(resolve, reject) {
-      SpecialPowers.pushPrefEnv({'set': [['camera.control.test.enabled', hwType]]}, function() {
-        resolve();
+      SpecialPowers.pushPrefEnv({'set': [['camera.control.test.permission', true]]}, function() {
+        SpecialPowers.pushPrefEnv({'set': [['camera.control.test.enabled', hwType]]}, function() {
+          resolve();
+        });
       });
     });
   },
 
   /* Returns a promise which is resolved when all of the SpecialPowers
      parameters that were set while testing are flushed. This includes
      camera.control.test.enabled and camera.control.test.is_low_memory. */
   _teardown: function() {
@@ -153,16 +174,20 @@ CameraTestSuite.prototype = {
     this._tests.push({
       name: aName,
       cb: aCb
     });
   },
 
   /* Execute all test cases (after setup is called). */
   _run: function() {
+    if (this._reloading) {
+      return;
+    }
+
     var test = this._tests.shift();
     var self = this;
     if (test) {
       info(test.name + ' started');
 
       function runNextTest() {
         self.run();
       }
@@ -343,28 +368,40 @@ CameraTestSuite.prototype = {
      when a test case does not expect a particular call
      to fail but otherwise does not require any special
      handling of that situation beyond failing the test
      case and logging why.*/
   _rejectGetCamera: function(e) {
     return this.logError('get camera failed', e);
   },
 
+  _rejectConfigure: function(e) {
+    return this.logError('set configuration failed', e);
+  },
+
   _rejectRelease: function(e) {
     return this.logError('release camera failed', e);
   },
 
   _rejectAutoFocus: function(e) {
     return this.logError('auto focus failed', e);
   },
 
   _rejectTakePicture: function(e) {
     return this.logError('take picture failed', e);
   },
 
+  _rejectStartRecording: function(e) {
+    return this.logError('start recording failed', e);
+  },
+
+  _rejectStopRecording: function(e) {
+    return this.logError('stop recording failed', e);
+  },
+
   _rejectPreviewStarted: function(e) {
     return this.logError('preview start failed', e);
   },
 
   /* The success handlers below are intended to be used
      when a test case does not expect a particular call
      to succed but otherwise does not require any special
      handling of that situation beyond failing the test
@@ -379,18 +416,30 @@ CameraTestSuite.prototype = {
 
   _expectedRejectGetCamera: function(p) {
     /* Copy handle to ensure it gets released at the end
        of the test case */
     self.camera = p.camera;
     return this.expectedError('expected get camera to fail');
   },
 
+  _expectedRejectConfigure: function(p) {
+    return this.expectedError('expected set configuration to fail');
+  },
+
   _expectedRejectAutoFocus: function(p) {
     return this.expectedError('expected auto focus to fail');
   },
 
   _expectedRejectTakePicture: function(p) {
     return this.expectedError('expected take picture to fail');
   },
+
+  _expectedRejectStartRecording: function(p) {
+    return this.expectedError('expected start recording to fail');
+  },
+
+  _expectedRejectStopRecording: function(p) {
+    return this.expectedError('expected stop recording to fail');
+  },
 };
 
 ise(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
--- a/dom/camera/test/mochitest.ini
+++ b/dom/camera/test/mochitest.ini
@@ -1,17 +1,21 @@
 [DEFAULT]
 support-files = camera_common.js
 
 [test_camera.html]
+skip-if = toolkit != 'gonk'
 [test_camera_2.html]
+skip-if = toolkit != 'gonk'
 [test_camera_3.html]
+skip-if = toolkit != 'gonk'
 [test_camera_hardware_init_failure.html]
 [test_camera_hardware_failures.html]
 [test_bug975472.html]
 [test_camera_fake_parameters.html]
 [test_camera_hardware_face_detection.html]
 [test_camera_hardware_auto_focus_moving_cb.html]
 [test_bug1022766.html]
 [test_bug1037322.html]
 [test_bug1099390.html]
 [test_bug1104913.html]
+skip-if = toolkit != 'gonk'
 [test_camera_bad_initial_config.html]
--- a/dom/camera/test/test_bug1037322.html
+++ b/dom/camera/test/test_bug1037322.html
@@ -7,83 +7,63 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'high',
-  previewSize: {
-    width: 320,
-    height: 240
-  }
-};
+var suite = new CameraTestSuite();
 
-function onError(e) {
-  ok(false, "Error: " + JSON.stringify(e));
-}
-
-var Camera = {
-  cameraObj: null,
+suite.test('bug-1037322', function() {
+  var cameraManager = navigator.mozCameras;
+  var whichCamera = cameraManager.getListOfCameras()[0];
 
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
+  var postConfig = {
+    mode: 'picture',
+    recorderProfile: 'low',
+    previewSize: {
+      width: 320,
+      height: 240
+    }
+  };
 
-  start: function test_start() {
-    function setConfig_onSuccess(cfg) {
-      // Check our specific configuration
-      ok(cfg.mode === config.mode, "Configured mode = " + cfg.mode);
-      ok(cfg.previewSize.width === config.previewSize.width &&
-         cfg.previewSize.height === config.previewSize.height,
-         "Configured preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
-      ok(cfg.recorderProfile === config.recorderProfile,
-         "Configured recorder profile = '" + cfg.recorderProfile + "'");
-
-      SimpleTest.finish();
-    }
+  function resolveGetCamera(p) {
+    suite.camera = p.camera;
 
-    function getCamera_onSuccess(d) {
-      var camera = d.camera;
-      var cfg = d.configuration;
-      Camera.cameraObj = camera;
-      Camera.viewfinder.mozSrcObject = camera;
-      Camera.viewfinder.play();
+    // Check the default configuration
+    var cfg = p.configuration;
+    ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
+    ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
+       "Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
+    ok(cfg.recorderProfile === "default",
+       "Initial recorder profile = '" + cfg.recorderProfile + "'");
+  }
 
-      // Check the default configuration
-      ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
-      ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
-         "Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
-      ok(cfg.recorderProfile === "default",
-         "Initial recorder profile = '" + cfg.recorderProfile + "'");
+  function configure(p) {
+    // Apply our specific configuration
+    return suite.camera.setConfiguration(postConfig);
+  }
 
-      // Apply our specific configuration
-      camera.setConfiguration(config).then(setConfig_onSuccess, onError);
-    }
-
-    var cfg = {
-      mode: 'unspecified',
-    };
-    navigator.mozCameras.getCamera(whichCamera, cfg).then(getCamera_onSuccess, onError);
+  function resolveConfigure(cfg) {
+    // Check our specific configuration
+    ok(cfg.mode === postConfig.mode, "Configured mode = " + cfg.mode);
+    ok(cfg.previewSize.width === postConfig.previewSize.width &&
+       cfg.previewSize.height === postConfig.previewSize.height,
+       "Configured preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
+    ok(cfg.recorderProfile === postConfig.recorderProfile,
+       "Configured recorder profile = '" + cfg.recorderProfile + "'");
   }
-}
 
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
-  if (Camera.cameraObj) {
-    Camera.cameraObj.release();
-    Camera.cameraObj = null;
-  }
+  return cameraManager.getCamera(whichCamera, {mode: 'unspecified'})
+    .then(resolveGetCamera, suite.rejectGetCamera)
+    .then(configure)
+    .then(resolveConfigure, suite.rejectConfigure);
 });
 
-Camera.start();
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/camera/test/test_bug1099390.html
+++ b/dom/camera/test/test_bug1099390.html
@@ -7,102 +7,49 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'high',
-  previewSize: {
-    width: 352,
-    height: 288
-  }
-};
-
-function onError(e) {
-  ok(false, "Error " + e);
-}
-
-var Camera = {
-  _cameraObj: null,
+var suite = new CameraTestSuite();
 
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
-
-  release: function release() {
-    viewfinder.mozSrcObject = null;
-    if (Camera._cameraObj) {
-      Camera._cameraObj.release();
-      Camera._cameraObj = null;
-    }
-  },
-
-  test: function test(cam) {
-    var gotCloseEvent = false;
-    var gotReleasePromise = false;
-
-    function gotAll() {
-      var all = gotCloseEvent && gotReleasePromise;
-      if (all) {
-        info("Got all expected notifications");
-      }
-      return all;
-    };
+suite.test('bug-1099390', function() {
+  function release(p) {
+    return new Promise(function(resolve, reject) {
+      var gotCloseEvent = false;
+      var gotReleasePromise = false;
 
-    var onClosed = function(e) {
-      cam.removeEventListener('close', onClosed);
-      ok(!gotCloseEvent, "gotCloseEvent was " + gotCloseEvent);
-      ok(e.reason === "HardwareReleased", "'close' event reason is: " + e.reason);
-      gotCloseEvent = true;
-      if (gotAll()) {
-        SimpleTest.finish();
-      }
-    };
+      var onClosed = function(e) {
+        suite.camera.removeEventListener('close', onClosed);
+        ok(!gotCloseEvent, "gotCloseEvent was " + gotCloseEvent);
+        ok(e.reason === "HardwareReleased", "'close' event reason is: " + e.reason);
+        gotCloseEvent = true;
+        if (gotReleasePromise) {
+          resolve();
+        }
+      };
 
-    cam.addEventListener('close', onClosed);
-
-    var onResolve = function() {
-      ok(!gotReleasePromise, "gotReleasePromise was " + gotReleasePromise);
-      gotReleasePromise = true;
-      if (gotAll()) {
-        SimpleTest.finish();
-      }
-    };
-
-    cam.release().then(onResolve, onError);
-  }, // test()
+      suite.camera.addEventListener('close', onClosed);
 
-  start: function start() {
-    function onSuccess(d) {
-      Camera._cameraObj = d.camera;
-      var cam = d.camera;
-
-      var onPreviewStateChange = function(e) {
-        if (e.newState === 'started') {
-          cam.removeEventListener('previewstatechange', onPreviewStateChange);
-          Camera.test(cam);
+      suite.camera.release().then(function(p) {
+        ok(true, "released camera");
+        gotReleasePromise = true;
+        if (gotCloseEvent) {
+          resolve();
         }
-      }; // onPreviewStateChange
-      cam.addEventListener('previewstatechange', onPreviewStateChange);
-    }; // onSuccess()
+      }).catch(reject);
+    });
+  }
 
-    navigator.mozCameras.getCamera(whichCamera, config).then(onSuccess, onError);
-  }, // start()
-}
-
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener('beforeunload', function() {
-  Camera.release();
+  return suite.getCamera()
+    .then(release, suite.rejectGetCamera);
 });
 
-Camera.start();
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/camera/test/test_bug975472.html
+++ b/dom/camera/test/test_bug975472.html
@@ -7,244 +7,177 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-const Cr = Components.results;
+var suite = new CameraTestSuite();
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'high',
-  previewSize: {
-    width: 320,
-    height: 240
-  }
-};
-var options = {
-  rotation: 0,
-  position: {
-    latitude: 43.645687,
-    longitude: -79.393661
-  },
-  dateTime: Date.now()
-};
-
-function onError(e) {
-  ok(false, "Error " + e);
-}
-function next() {
-  Camera.nextTest();
+function cameraRelease(p) {
+  return suite.camera.release();
 }
 
-// The array of tests
-var tests = [
-  {
-    key: "release-after-release",
-    func: function testAutoFocus(camera) {
-      function onSuccess(success) {
-        ok(true, "release() succeeded");
-        next();
-      }
-      function onError(error) {
-        ok(false, "release() failed with: " + error);
-      }
-      camera.release().then(onSuccess, onError);
+suite.test('release-after-release', function() {
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(cameraRelease, suite.rejectRelease)
+    .catch(suite.rejectRelease);
+});
+
+suite.test('set-picture-size-after-release', function() {
+  function setPictureSize(p) {
+    try {
+      suite.camera.setPictureSize({ width: 0, height: 0 });
+      ok(false, "SetPictureSize() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "setPictureSize() failed with: " + e.name);
     }
-  },
-  {
-    key: "set-picture-size-after-release",
-    func: function testSetPictureSize(camera) {
-      try {
-        camera.setPictureSize({ width: 0, height: 0 });
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "setPictureSize() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "set-thumbnail-size-after-release",
-    func: function testSetThumbnailSize(camera) {
-      try {
-        camera.setThumbnailSize({ width: 0, height: 0 });
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "setThumbnailSize() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "get-sensor-angle-after-release",
-    func: function testGetSensorAngle(camera) {
-      ok(camera.sensorAngle == 0, "camera.sensorAngle = " + camera.sensorAngle);
-      next();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(setPictureSize, suite.rejectRelease);
+});
+
+suite.test('set-thumbnail-size-after-release', function() {
+  function setThumbnailSize(p) {
+    try {
+      suite.camera.setThumbnailSize({ width: 0, height: 0 });
+      ok(false, "SetThumbnailSize() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "setThumbnailSize() failed with: " + e.name);
     }
-  },
-  {
-    key: "resume-preview-after-release",
-    func: function testResumePreview(camera) {
-      try {
-        camera.resumePreview();
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "resumePreview() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "auto-focus-after-release",
-    func: function testAutoFocus(camera) {
-      function onSuccess(success) {
-        ok(false, "autoFocus() succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "autoFocus() failed with: " + error.name);
-        next();
-      }
-      camera.autoFocus().then(onSuccess, onError);
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(setThumbnailSize, suite.rejectRelease);
+});
+
+suite.test('get-sensor-angle-after-release', function() {
+  function getSensorAngle(p) {
+    ok(suite.camera.sensorAngle == 0, "camera.sensorAngle = " + suite.camera.sensorAngle);
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(getSensorAngle, suite.rejectRelease);
+});
+
+suite.test('resume-preview-after-release', function() {
+  function resumePreview(p) {
+    try {
+      suite.camera.resumePreview();
+      ok(false, "resumePreview() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "resumePreview() failed with: " + e.name);
     }
-  },
-  {
-    key: "take-picture-after-release",
-    func: function testTakePicture(camera) {
-      function onSuccess(picture) {
-        ok(false, "takePicture() succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "takePicture() failed with: " + error.name);
-        next();
-      }
-      camera.takePicture(null).then(onSuccess, onError);
-    }
-  },
-  {
-    key: "start-recording-after-release",
-    func: function testStartRecording(camera) {
-      function onSuccess(picture) {
-        ok(false, "startRecording() process succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "startRecording() failed with: " + error.name);
-        next();
-      }
-      var recordingOptions = {
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(resumePreview, suite.rejectRelease);
+});
+
+suite.test('auto-focus-after-release', function() {
+  function autoFocus(p) {
+    return suite.camera.autoFocus();
+  }
+
+  function rejectAutoFocus(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "autoFocus() failed with: " + error.name);
+    return Promise.resolve();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(autoFocus, suite.rejectRelease)
+    .then(suite.expectedRejectAutoFocus, rejectAutoFocus);
+});
+
+suite.test('take-picture-after-release', function() {
+  function takePicture(p) {
+    return suite.camera.takePicture(null);
+  }
+
+  function rejectTakePicture(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "takePicture() failed with: " + error.name);
+    return Promise.resolve();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(takePicture, suite.rejectRelease)
+    .then(suite.expectedRejectTakePicture, rejectTakePicture);
+});
+
+suite.test('start-recording-after-release', function() {
+  function startRecording(p) {
+    return suite.camera.startRecording(
+      {
         profile: 'high',
         rotation: 0
-      };
-      camera.startRecording(recordingOptions,
-                            navigator.getDeviceStorage('videos'),
-                            'bug975472.mp4').then(onSuccess, onError);
-    }
-  },
-  {
-    key: "stop-recording-after-release",
-    func: function testStopRecording(camera) {
-      try {
-        camera.stopRecording();
-      } catch(e) {
-        ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
-          "stopRecording() failed with: " + e.name);
-        next();
-      }
-    }
-  },
-  {
-    key: "set-configuration-after-release",
-    func: function testSetConfiguration(camera) {
-      function onSuccess(picture) {
-        ok(false, "setConfiguration() process succeeded incorrectly");
-      }
-      function onError(error) {
-        ok(error.name === "NS_ERROR_NOT_AVAILABLE",
-          "setConfiguration() failed with: " + error.name);
-        next();
-      }
-      camera.setConfiguration(config).then(onSuccess, onError);
-    }
-  },
-];
-
-var testGenerator = function() {
-  for (var i = 0; i < tests.length; ++i ) {
-    yield tests[i];
+      },
+      navigator.getDeviceStorage('videos'),
+      'bug975472.mp4'
+    );
   }
-}();
 
-var Camera = {
-  cameraObj: null,
-  _otherPictureSize: null,
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
-  onCameraReady: function () {
-    Camera.nextTest = function() {
-      try {
-        var t = testGenerator.next();
-        info("test: " + t.key);
-        t.func(Camera.cameraObj);
-      } catch(e) {
-        if (e instanceof StopIteration) {
-          SimpleTest.finish();
-        } else {
-          throw e;
-        }
-      }
-    };
-    // Release the camera hardware, and call all of the asynchronous methods
-    // to make sure they properly handle being in this state.
-    Camera.cameraObj.release();
-    next();
-  },
-  release: function release() {
-    cameraObj = null;
-  },
-  start: function run_test() {
-    function onSuccess(d) {
-      var camera = d.camera;
-      Camera.cameraObj = camera;
-      var onPreviewStateChange = function(e) {
-        if (e.newState === 'started') {
-          info("viewfinder is ready and playing");
-          Camera.cameraObj.removeEventListener('previewstatechange', onPreviewStateChange);
-          Camera.onCameraReady();
-        }
-      };
-      camera.addEventListener('previewstatechange', onPreviewStateChange);
-      Camera.viewfinder.mozSrcObject = camera;
-      Camera.viewfinder.play();
-      ok(camera.capabilities.pictureSizes.length > 0,
-        "capabilities.pictureSizes.length = " +
-        camera.capabilities.pictureSizes.length);
-      Camera._otherPictureSize = camera.capabilities.pictureSizes.slice(-1)[0];
-      camera.setPictureSize(camera.capabilities.pictureSizes[0]);
-      options.pictureSize = Camera._otherPictureSize;
-      options.fileFormat = camera.capabilities.fileFormats[0];
-      info("getCamera callback, setting pictureSize = " + options.pictureSize.toSource());
-    };
-    navigator.mozCameras.getCamera(whichCamera, config).then(onSuccess, onError);
+  function rejectStartRecording(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "takePicture() failed with: " + error.name);
+    return Promise.resolve();
   }
-}
 
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
-  Camera.cameraObj.release();
-  Camera.cameraObj = null;
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(startRecording, suite.rejectRelease)
+    .then(suite.expectedRejectStartRecording, rejectStartRecording);
 });
 
-Camera.start();
+suite.test('stop-recording-after-release', function() {
+  function stopRecording(p) {
+    try {
+      suite.camera.stopRecording();
+      ok(false, "stopRecording() should have failed");
+    } catch(e) {
+      ok(e.result === SpecialPowers.Cr.NS_ERROR_NOT_AVAILABLE,
+        "stopRecording() failed with: " + e.name);
+    }
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(stopRecording, suite.rejectRelease);
+});
+
+suite.test('set-configuration-after-release', function() {
+  function configure(p) {
+    return suite.camera.setConfiguration(null);
+  }
+
+  function rejectConfigure(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "takePicture() failed with: " + error.name);
+    return Promise.resolve();
+  }
+
+  return suite.getCamera()
+    .then(cameraRelease, suite.rejectGetCamera)
+    .then(configure, suite.rejectRelease)
+    .then(suite.expectedRejectConfigure, rejectConfigure);
+});
+
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/camera/test/test_camera_bad_initial_config.html
+++ b/dom/camera/test/test_camera_bad_initial_config.html
@@ -7,52 +7,38 @@
   <script type="text/javascript" src="camera_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <video id="viewfinder" width="200" height="200" autoplay></video>
 <img src="#" alt="This image is going to load" id="testimage"/>
 <script class="testbody" type="text/javascript;version=1.7">
 
-var whichCamera = navigator.mozCameras.getListOfCameras()[0];
-var config = {
-  mode: 'picture',
-  recorderProfile: 'foobar',
-};
+var suite = new CameraTestSuite();
 
-var Camera = {
-  cameraObj: null,
-
-  get viewfinder() {
-    return document.getElementById('viewfinder');
-  },
+suite.test('bad-initial-config', function() {
+  function getCamera() {
+    var whichCamera = navigator.mozCameras.getListOfCameras()[0];
+    var config = {
+      mode: 'picture',
+      recorderProfile: 'foobar',
+    };
 
-  start: function test_start() {
-    function getCamera_onSuccess(d) {
-      ok(false, "Get camera should have failed");
-      SimpleTest.finish();
-    }
-
-    function getCamera_onError(e) {
-      ok(true, "Get camera failed as expected: " + JSON.stringify(e));
-      SimpleTest.finish();
-    }
+    return navigator.mozCameras.getCamera(whichCamera, config);
+  }
 
-    navigator.mozCameras.getCamera(whichCamera, config).then(getCamera_onSuccess, getCamera_onError);
+  function rejectGetCamera(error) {
+    ok(error.name === "NS_ERROR_NOT_AVAILABLE",
+      "getCamera() failed with: " + error.name);
+    return Promise.resolve();
   }
-}
-
-SimpleTest.waitForExplicitFinish();
 
-window.addEventListener('beforeunload', function() {
-  Camera.viewfinder.mozSrcObject = null;
-  if (Camera.cameraObj) {
-    Camera.cameraObj.release();
-    Camera.cameraObj = null;
-  }
+  return getCamera()
+    .then(suite.expectedRejectGetCamera, rejectGetCamera);
 });
 
-Camera.start();
+suite.setup()
+  .then(suite.run);
 
 </script>
 </body>
 
 </html>
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -386,17 +386,17 @@ MediaDevice::MediaDevice(MediaEngineSour
   : mHasFacingMode(false)
   , mSource(aSource) {
   mSource->GetName(mName);
   mSource->GetUUID(mID);
 }
 
 VideoDevice::VideoDevice(MediaEngineVideoSource* aSource)
   : MediaDevice(aSource) {
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (mName.EqualsLiteral("back")) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::Environment;
   } else if (mName.EqualsLiteral("front")) {
     mHasFacingMode = true;
     mFacingMode = dom::VideoFacingModeEnum::User;
   }
 #endif // MOZ_B2G_CAMERA
@@ -1700,17 +1700,17 @@ MediaManager::GetUserMedia(
     if (isLoop &&
         (src == dom::MediaSourceEnum::Window ||
          src == dom::MediaSourceEnum::Application ||
          src == dom::MediaSourceEnum::Screen)) {
        privileged = false;
     }
   }
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
   // XXX No full support for picture in Desktop yet (needs proper UI)
   if (privileged ||
       (fake && !Preferences::GetBool("media.navigator.permission.fake"))) {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -625,16 +625,16 @@ private:
   nsAutoPtr<base::Thread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   nsRefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 };
 
 } // namespace mozilla
 
 #endif // MOZILLA_MEDIAMANAGER_H
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -26,17 +26,17 @@ GetUserMediaLog()
 #include "nsITabSource.h"
 #include "MediaTrackConstraints.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidJNIWrapper.h"
 #include "AndroidBridge.h"
 #endif
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
 #include "ICameraControl.h"
 #include "MediaEngineGonkVideoSource.h"
 #endif
 
 #undef LOG
 #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
 
 namespace mozilla {
@@ -57,33 +57,35 @@ MediaEngineWebRTC::MediaEngineWebRTC(Med
 {
 #ifndef MOZ_B2G_CAMERA
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
   if (compMgr) {
     compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
   }
 #else
+#ifdef MOZ_WIDGET_GONK
   AsyncLatencyLogger::Get()->AddRef();
 #endif
+#endif
   // XXX
   gFarendObserver = new AudioOutputObserver();
 
   NS_NewNamedThread("AudioGUM", getter_AddRefs(mThread));
   MOZ_ASSERT(mThread);
 }
 
 void
 MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
 {
   // We spawn threads to handle gUM runnables, so we must protect the member vars
   MutexAutoLock lock(mMutex);
 
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   if (aMediaSource != dom::MediaSourceEnum::Camera) {
     // only supports camera sources
     return;
   }
 
   /**
    * We still enumerate every time, in case a new device was plugged in since
    * the last call. TODO: Verify that WebRTC actually does deal with hotplugging
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -250,17 +250,17 @@ public:
 
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
 private:
   ~MediaEngineWebRTC() {
     Shutdown();
-#ifdef MOZ_B2G_CAMERA
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
     AsyncLatencyLogger::Get()->Release();
 #endif
     gFarendObserver = nullptr;
   }
 
   nsCOMPtr<nsIThread> mThread;
 
   Mutex mMutex;
--- a/dom/media/webrtc/moz.build
+++ b/dom/media/webrtc/moz.build
@@ -31,17 +31,17 @@ if CONFIG['MOZ_WEBRTC']:
         '/dom/base',
         '/dom/camera',
         '/media/libyuv/include',
         '/media/webrtc/signaling/src/common',
         '/media/webrtc/signaling/src/common/browser_logging',
         '/media/webrtc/trunk',
     ]
     # Gonk camera source.
-    if CONFIG['MOZ_B2G_CAMERA']:
+    if CONFIG['MOZ_B2G_CAMERA'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         EXPORTS += [
             'GonkCameraImage.h',
             'MediaEngineGonkVideoSource.h',
         ]
         UNIFIED_SOURCES += [
             'GonkCameraImage.cpp',
             'MediaEngineGonkVideoSource.cpp',
         ]
--- a/dom/nfc/NfcContentHelper.js
+++ b/dom/nfc/NfcContentHelper.js
@@ -157,24 +157,25 @@ NfcContentHelper.prototype = {
     this._requestMap[requestId] = callback;
 
     cpmm.sendAsyncMessage("NFC:ReadNDEF", {
       requestId: requestId,
       sessionToken: sessionToken
     });
   },
 
-  writeNDEF: function writeNDEF(records, sessionToken, callback) {
+  writeNDEF: function writeNDEF(records, isP2P, sessionToken, callback) {
     let requestId = callback.getCallbackId();
     this._requestMap[requestId] = callback;
 
     cpmm.sendAsyncMessage("NFC:WriteNDEF", {
       requestId: requestId,
       sessionToken: sessionToken,
-      records: records
+      records: records,
+      isP2P: isP2P
     });
   },
 
   makeReadOnly: function makeReadOnly(sessionToken, callback) {
     let requestId = callback.getCallbackId();
     this._requestMap[requestId] = callback;
 
     cpmm.sendAsyncMessage("NFC:MakeReadOnly", {
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -86,16 +86,24 @@ const NfcRequestType = {
   CHANGE_RF_STATE: "changeRFState",
   READ_NDEF: "readNDEF",
   WRITE_NDEF: "writeNDEF",
   MAKE_READ_ONLY: "makeReadOnly",
   FORMAT: "format",
   TRANSCEIVE: "transceive"
 };
 
+const CommandMsgTable = {};
+CommandMsgTable["NFC:ChangeRFState"] = NfcRequestType.CHANGE_RF_STATE;
+CommandMsgTable["NFC:ReadNDEF"] = NfcRequestType.READ_NDEF;
+CommandMsgTable["NFC:WriteNDEF"] = NfcRequestType.WRITE_NDEF;
+CommandMsgTable["NFC:MakeReadOnly"] = NfcRequestType.MAKE_READ_ONLY;
+CommandMsgTable["NFC:Format"] = NfcRequestType.FORMAT;
+CommandMsgTable["NFC:Transceive"] = NfcRequestType.TRANSCEIVE;
+
 // Should be consistent with NfcResponseType defined in NfcOptions.webidl.
 const NfcResponseType = {
   CHANGE_RF_STATE_RSP: "changeRFStateRsp",
   READ_NDEF_RSP: "readNDEFRsp",
   WRITE_NDEF_RSP: "writeNDEFRsp",
   MAKE_READ_ONLY_RSP: "makeReadOnlyRsp",
   FORMAT_RSP: "formatRsp",
   TRANSCEIVE_RSP: "transceiveRsp",
@@ -393,16 +401,30 @@ XPCOMUtils.defineLazyGetter(this, "gMess
           this.notifySendFileStatus(message);
           return null;
         case "NFC:CallDefaultFoundHandler":
           this.callDefaultFoundHandler(message.data);
           return null;
         case "NFC:CallDefaultLostHandler":
           this.callDefaultLostHandler(message.data);
           return null;
+        case "NFC:SendFile":
+          // Chrome process is the arbitrator / mediator between
+          // system app (content process) that issued nfc 'sendFile' operation
+          // and system app that handles the system message :
+          // 'nfc-manager-send-file'. System app subsequently handover's
+          // the data to alternate carrier's (BT / WiFi) 'sendFile' interface.
+
+          // Notify system app to initiate BT send file operation
+          let sysMsg = new NfcSendFileSysMsg(message.data.requestId,
+                                             message.data.sessionToken,
+                                             message.data.blob);
+          gSystemMessenger.broadcastMessage("nfc-manager-send-file",
+                                            sysMsg);
+          return null;
         default:
           return this.nfc.receiveMessage(message);
       }
     },
 
     /**
      * nsIObserver interface methods.
      */
@@ -666,62 +688,28 @@ Nfc.prototype = {
     // Return early if we don't need the NFC Service.
     switch (message.name) {
       case "NFC:QueryInfo":
         return {rfState: this.rfState};
       default:
         break;
     }
 
-    if (["NFC:ChangeRFState",
-         "NFC:SendFile"].indexOf(message.name) == -1) {
+    if (message.name != "NFC:ChangeRFState") {
       // Update the current sessionId before sending to the NFC service.
       message.data.sessionId = SessionHelper.getId(message.data.sessionToken);
     }
 
-    switch (message.name) {
-      case "NFC:ChangeRFState":
-        this.sendToNfcService(NfcRequestType.CHANGE_RF_STATE, message.data);
-        break;
-      case "NFC:ReadNDEF":
-        this.sendToNfcService(NfcRequestType.READ_NDEF, message.data);
-        break;
-      case "NFC:WriteNDEF":
-        message.data.isP2P = SessionHelper.isP2PSession(message.data.sessionId);
-        this.sendToNfcService(NfcRequestType.WRITE_NDEF, message.data);
-        break;
-      case "NFC:MakeReadOnly":
-        this.sendToNfcService(NfcRequestType.MAKE_READ_ONLY, message.data);
-        break;
-      case "NFC:Format":
-        this.sendToNfcService(NfcRequestType.FORMAT, message.data);
-        break;
-      case "NFC:Transceive":
-        this.sendToNfcService(NfcRequestType.TRANSCEIVE, message.data);
-        break;
-      case "NFC:SendFile":
-        // Chrome process is the arbitrator / mediator between
-        // system app (content process) that issued nfc 'sendFile' operation
-        // and system app that handles the system message :
-        // 'nfc-manager-send-file'. System app subsequently handover's
-        // the data to alternate carrier's (BT / WiFi) 'sendFile' interface.
-
-        // Notify system app to initiate BT send file operation
-        let sysMsg = new NfcSendFileSysMsg(message.data.requestId,
-                                           message.data.sessionToken,
-                                           message.data.blob);
-        gSystemMessenger.broadcastMessage("nfc-manager-send-file",
-                                          sysMsg);
-        break;
-      default:
-        debug("UnSupported : Message Name " + message.name);
-        return null;
+    let command = CommandMsgTable[message.name];
+    if (!command) {
+      debug("Unknown message: " + message.name);
+      return null;
     }
     this.targetsByRequestId[message.data.requestId] = message.target;
-
+    this.sendToNfcService(command, message.data);
     return null;
   },
 
   removeTarget: function removeTarget(target) {
     Object.keys(this.targetsByRequestId).forEach((requestId) => {
       if (this.targetsByRequestId[requestId] === target) {
         delete this.targetsByRequestId[requestId];
       }
--- a/dom/nfc/nsINfcContentHelper.idl
+++ b/dom/nfc/nsINfcContentHelper.idl
@@ -114,17 +114,17 @@ interface nsINfcRequestCallback : nsISup
 interface nsINfcBrowserAPI : nsISupports
 {
   const int32_t SYSTEM_APP_ID = -1;
 
   void setFocusApp(in uint64_t tabId,
                    in boolean isFocus);
 };
 
-[scriptable, uuid(39e1b25b-5063-449e-b9e8-5514cdca9c3a)]
+[scriptable, uuid(9cf841c9-0347-4564-99e5-18c0f74eca4d)]
 interface nsINfcContentHelper : nsISupports
 {
   void init(in nsIDOMWindow window);
 
   /**
    * Read current NDEF data on the tag.
    *
    * @param sessionToken
@@ -137,23 +137,26 @@ interface nsINfcContentHelper : nsISuppo
                 in nsINfcRequestCallback callback);
 
   /**
    * Write NDEF data to a peer device or a tag.
    *
    * @param records
    *        NDEF records to be written
    *
+   * @param isP2P
+   *        If this write is for P2P.
    * @param sessionToken
    *        Current token
    *
    * @param callback
    *        Called when request is finished
    */
   void writeNDEF(in nsIVariant records,
+                 in boolean isP2P,
                  in DOMString sessionToken,
                  in nsINfcRequestCallback callback);
 
   /**
    * Make a tag read-only
    *
    * @param sessionToken
    *        Current token
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -177,17 +177,17 @@ MozNFCTagImpl.prototype = {
       ndefLen += record.size;
     }
 
     if (ndefLen > this.maxNDEFSize) {
       throw new this._window.DOMError("NotSupportedError", "Exceed max NDEF size");
     }
 
     let callback = new NfcCallback(this._window);
-    this._nfcContentHelper.writeNDEF(records, this.session, callback);
+    this._nfcContentHelper.writeNDEF(records, false, this.session, callback);
     return callback.promise;
   },
 
   makeReadOnly: function makeReadOnly() {
     if (this.isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCTag object is invalid");
     }
 
@@ -276,17 +276,17 @@ MozNFCPeerImpl.prototype = {
   // NFCPeer interface:
   sendNDEF: function sendNDEF(records) {
     if (this.isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
     }
 
     // Just forward sendNDEF to writeNDEF
     let callback = new NfcCallback(this._window);
-    this._nfcContentHelper.writeNDEF(records, this.session, callback);
+    this._nfcContentHelper.writeNDEF(records, true, this.session, callback);
     return callback.promise;
   },
 
   sendFile: function sendFile(blob) {
     if (this.isLost) {
       throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
     }
 
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -16,16 +16,17 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+/* global RIL */
 XPCOMUtils.defineLazyGetter(this, "RIL", function () {
   let obj = {};
   Cu.import("resource://gre/modules/ril_consts.js", obj);
   return obj;
 });
 
 const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
 
@@ -54,35 +55,38 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:IccCloseChannel",
   "RIL:IccExchangeAPDU",
   "RIL:ReadIccContacts",
   "RIL:UpdateIccContact",
   "RIL:MatchMvno",
   "RIL:GetServiceState"
 ];
 
+/* global cpmm */
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
+/* global UUIDGenerator */
 XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
                   "@mozilla.org/uuid-generator;1",
                   "nsIUUIDGenerator");
 
+/* global gNumRadioInterfaces */
 XPCOMUtils.defineLazyGetter(this, "gNumRadioInterfaces", function() {
   let appInfo = Cc["@mozilla.org/xre/app-info;1"];
   let isParentProcess = !appInfo || appInfo.getService(Ci.nsIXULRuntime)
                           .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
 
   if (isParentProcess) {
     let ril = { numRadioInterfaces: 0 };
     try {
       ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
     } catch(e) {}
-    return ril.numRadioInterfaces
+    return ril.numRadioInterfaces;
   }
 
   return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
 });
 
 function IccInfo() {}
 IccInfo.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -604,36 +604,80 @@ GetCurrentBatteryCharging(int* aCharging
 
   return false;
 }
 
 void
 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
 {
   int charge;
+  static bool previousCharging = false;
+  static double previousLevel = 0.0, remainingTime = 0.0;
+  static struct timespec lastLevelChange;
+  struct timespec now;
+  double dtime, dlevel;
 
   if (GetCurrentBatteryCharge(&charge)) {
     aBatteryInfo->level() = (double)charge / 100.0;
   } else {
     aBatteryInfo->level() = dom::battery::kDefaultLevel;
   }
 
   int charging;
 
   if (GetCurrentBatteryCharging(&charging)) {
     aBatteryInfo->charging() = charging;
   } else {
     aBatteryInfo->charging() = true;
   }
 
-  if (!aBatteryInfo->charging() || (aBatteryInfo->level() < 1.0)) {
+  if (aBatteryInfo->charging() != previousCharging){
     aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+    memset(&lastLevelChange, 0, sizeof(struct timespec));
+  }
+
+  if (aBatteryInfo->charging()) {
+    if (aBatteryInfo->level() == 1.0) {
+      aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
+    } else if (aBatteryInfo->level() != previousLevel){
+      if (lastLevelChange.tv_sec != 0) {
+        clock_gettime(CLOCK_MONOTONIC, &now);
+        dtime = now.tv_sec - lastLevelChange.tv_sec;
+        dlevel = aBatteryInfo->level() - previousLevel;
+
+        if (dlevel <= 0.0) {
+          aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+        } else {
+          remainingTime = (double) round(dtime / dlevel * (1.0 - aBatteryInfo->level()));
+          aBatteryInfo->remainingTime() = remainingTime;
+        }
+
+        lastLevelChange = now;
+      } else { // lastLevelChange.tv_sec == 0
+        clock_gettime(CLOCK_MONOTONIC, &lastLevelChange);
+        aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+      }
+
+    } else {
+      clock_gettime(CLOCK_MONOTONIC, &now);
+      dtime = now.tv_sec - lastLevelChange.tv_sec;
+      if (dtime < remainingTime) {
+        aBatteryInfo->remainingTime() = round(remainingTime - dtime);
+      } else {
+        aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
+      }
+
+    }
+
   } else {
-    aBatteryInfo->remainingTime() = dom::battery::kDefaultRemainingTime;
+    aBatteryInfo->remainingTime() = dom::battery::kUnknownRemainingTime;
   }
+
+  previousCharging = aBatteryInfo->charging();
+  previousLevel = aBatteryInfo->level();
 }
 
 namespace {
 
 /**
  * RAII class to help us remember to close file descriptors.
  */
 
--- a/layout/reftests/forms/input/checkbox/reftest.list
+++ b/layout/reftests/forms/input/checkbox/reftest.list
@@ -6,9 +6,9 @@ skip-if(B2G||Mulet) fails-if(Android) ==
 != checked-notref.html about:blank
 != checked-native.html about:blank
 != checked-native-notref.html about:blank
 != indeterminate-checked.html indeterminate-checked-notref.html
 != indeterminate-unchecked.html indeterminate-unchecked-notref.html
 != indeterminate-native-checked.html indeterminate-native-checked-notref.html
 != indeterminate-native-unchecked.html indeterminate-native-unchecked-notref.html
 == indeterminate-selector.html indeterminate-selector-ref.html
-skip-if(!gtk2Widget) == gtk-theme-width-height.html gtk-theme-width-height-ref.html
+skip-if(!gtk2Widget||Mulet) == gtk-theme-width-height.html gtk-theme-width-height-ref.html # bug 1141511: Disable some gtk2Widget-dependant reftests on Mulet
--- a/layout/reftests/forms/input/radio/reftest.list
+++ b/layout/reftests/forms/input/radio/reftest.list
@@ -1,8 +1,8 @@
 == label-dynamic.html label-dynamic-ref.html
 != checked.html checked-notref.html
 != checked-native.html checked-native-notref.html
 != checked.html about:blank
 != checked-notref.html about:blank
 != checked-native.html about:blank
 != checked-native-notref.html about:blank
-skip-if(!gtk2Widget) == gtk-theme-width-height.html gtk-theme-width-height-ref.html
+skip-if(!gtk2Widget||Mulet) == gtk-theme-width-height.html gtk-theme-width-height-ref.html # bug 1141511: Disable some gtk2Widget-dependant reftests on Mulet
--- a/layout/reftests/outline/reftest.list
+++ b/layout/reftests/outline/reftest.list
@@ -1,9 +1,9 @@
 == outline-and-box-shadow.html outline-and-box-shadow-ref.html
 == outline-and-3d-transform-1a.html outline-and-3d-transform-1-ref.html
 == outline-and-3d-transform-1b.html outline-and-3d-transform-1-ref.html
 fuzzy-if(d2d,16,96) == outline-and-3d-transform-2.html outline-and-3d-transform-2-ref.html
 == outline-overflow-block-abspos.html outline-overflow-block-ref.html
 == outline-overflow-block-float.html outline-overflow-block-ref.html
 == outline-overflow-inlineblock-abspos.html outline-overflow-inlineblock-ref.html
 == outline-overflow-inlineblock-float.html outline-overflow-inlineblock-ref.html
-pref(layout.css.outline-style-auto.enabled,true) skip-if(!gtk2Widget&&!winWidget&&!cocoaWidget) == outline-auto-001.html outline-auto-001-ref.html # only works on platforms that supports NS_THEME_FOCUS_OUTLINE
+pref(layout.css.outline-style-auto.enabled,true) skip-if((!gtk2Widget&&!winWidget&&!cocoaWidget)||Mulet) == outline-auto-001.html outline-auto-001-ref.html # only works on platforms that supports NS_THEME_FOCUS_OUTLINE # bug 1141511:  Disable some gtk2Widget-dependant reftests on Mulet
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -196,17 +196,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'winspool',
     ]
 
 if CONFIG['MOZ_B2G_BT_BLUEZ'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     OS_LIBS += [
         'dbus',
     ]
 
-if CONFIG['MOZ_B2G_CAMERA']:
+if CONFIG['MOZ_B2G_CAMERA'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     OS_LIBS += [
         'stagefright',
         'stagefright_omx',
     ]
 
 if CONFIG['OS_ARCH'] == 'Linux' and CONFIG['OS_TARGET'] != 'Android':
     OS_LIBS += [
         'rt',