Merge b2g-inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 05 Dec 2013 17:53:41 -0500
changeset 174708 2831368a5e052002621ae52c314c2c37ec346650
parent 174662 7f4ce3bb50d043727cc829a4e0c0df2cd67cc18f (current diff)
parent 174707 d212ce6a9a18e59fcc4bb97871a5a11b725e6cf2 (diff)
child 174709 5b2094cfc80ff16cf9dccd18f395fc24eefaf9d7
child 174753 2b398758366ce3cae904394dc9d79c35c75f5e89
child 174804 3ef4df94f3dd8cadc4643417847ec4e1f960c26e
child 174819 8bba470af6efa8babd9efbe70c1ddf95f2eeeae1
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.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 b2g-inbound to m-c.
gfx/layers/ipc/AsyncPanZoomController.cpp
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1341,17 +1341,17 @@ window.addEventListener('ContentStart', 
                                                               videoCount: 0};
         }
         commandHandler(requestURL, { type: aData,
                                      isAudio: props.get('isAudio'),
                                      isVideo: props.get('isVideo')});
         break;
       case 'content-shutdown':
         // iterate through all the existing active processes
-        Object.keys(gRecordingActiveProcesses[processId]).foreach(function(requestURL) {
+        Object.keys(gRecordingActiveProcesses[processId]).forEach(function(requestURL) {
           commandHandler(requestURL, { type: aData,
                                        isAudio: true,
                                        isVideo: true});
         });
         break;
     }
 
     // clean up process record if no page record in it.
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "1ab9ef0530a59b513469e362eeb060d7b008e6d9", 
+    "revision": "2f52419acd2edacec369138b143454dd8afccebd", 
     "repo_path": "/integration/gaia-central"
 }
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -378,17 +378,17 @@ nsXMLHttpRequest::InitParameters(bool aA
   // Check for permissions.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
   if (!window || !window->GetDocShell()) {
     return;
   }
 
   // Chrome is always allowed access, so do the permission check only
   // for non-chrome pages.
-  if (!IsSystemXHR()) {
+  if (!IsSystemXHR() && aSystem) {
     nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
     if (!doc) {
       return;
     }
 
     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
     nsCOMPtr<nsIPermissionManager> permMgr =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -231,17 +231,17 @@ public:
     BindToOwner(aGlobalObject);
     mBaseURI = aBaseURI;
   }
 
   void InitParameters(bool aAnon, bool aSystem);
 
   void SetParameters(bool aAnon, bool aSystem)
   {
-    mIsAnon = aAnon;
+    mIsAnon = aAnon || aSystem;
     mIsSystem = aSystem;
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIXMLHttpRequest
   NS_DECL_NSIXMLHTTPREQUEST
 
--- a/content/base/test/file_restrictedEventSource.sjs
+++ b/content/base/test/file_restrictedEventSource.sjs
@@ -1,18 +1,16 @@
 function handleRequest(request, response)
 {
   if ((request.queryString == "test=user1_xhr" &&
        request.hasHeader("Authorization") &&
        request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=") ||
       (request.queryString == "test=user1_evtsrc" &&
        request.hasHeader("Authorization") &&
-       request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=" &&
-       request.hasHeader("Cookie") &&
-       request.getHeader("Cookie") == "test=5c")) {
+       request.getHeader("Authorization") == "Basic dXNlciAxOnBhc3N3b3JkIDE=")) {
     response.setStatusLine(null, 200, "OK");
     response.setHeader("Content-Type", "text/event-stream", false);
     response.setHeader("Access-Control-Allow-Origin", "http://mochi.test:8888", false);
     response.setHeader("Access-Control-Allow-Credentials", "true", false);
     response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
     if (request.queryString == "test=user1_xhr") {
       response.setHeader("Set-Cookie", "test=5c", false);
     }
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -516,16 +516,17 @@ support-files =
 [test_bug869006.html]
 [test_bug876282.html]
 [test_bug890580.html]
 [test_bug894874.html]
 [test_bug895239.html]
 [test_bug895974.html]
 [test_bug902847.html]
 [test_bug907892.html]
+[test_bug927196.html]
 [test_caretPositionFromPoint.html]
 [test_classList.html]
 [test_copypaste.html]
 [test_copypaste.xhtml]
 [test_createHTMLDocument.html]
 [test_declare_stylesheet_obsolete.html]
 [test_domparser_null_char.html]
 [test_domparsing.html]
--- a/content/base/test/test_XHR_parameters.html
+++ b/content/base/test/test_XHR_parameters.html
@@ -48,22 +48,21 @@ function runTests() {
     try {
       xhr = new XMLHttpRequest(value);
     } catch (ex) {
       ok(false, "Got unexpected exception: " + ex);
       return;
     }
     ok(xhr instanceof XMLHttpRequest, "passed " + JSON.stringify(value));
 
-    // If the page doesnt have privileges to create a system or anon XHR,
-    // these flags will always be false no matter what is passed.
-    let expectedAnon = false;
+    // If the page doesnt have privileges to create a system XHR,
+    // this flag will always be false no matter what is passed.
+    let expectedAnon = Boolean(value && value.mozAnon);
     let expectedSystem = false;
     if (havePrivileges) {
-      expectedAnon = Boolean(value && value.mozAnon);
       expectedSystem = Boolean(value && value.mozSystem);
     }
     is(xhr.mozAnon, expectedAnon, "testing mozAnon");
     is(xhr.mozSystem, expectedSystem, "testing mozSystem");
   }
 
   function testInvalidParameter(value) {
     let expectedError;
@@ -79,21 +78,20 @@ function runTests() {
   }
 
   // Run the tests once without API privileges...
   validParameters.forEach(testValidParameter);
   invalidParameters.forEach(testInvalidParameter);
 
   // ...and once with privileges.
   havePrivileges = true;
-  SpecialPowers.addPermission("systemXHR", true, document);
+  SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], function() {
+    validParameters.forEach(testValidParameter);
+    invalidParameters.forEach(testInvalidParameter);
 
-  validParameters.forEach(testValidParameter);
-  invalidParameters.forEach(testInvalidParameter);
-  SpecialPowers.removePermission("systemXHR", document);
-
-  SimpleTest.finish();
+    SimpleTest.finish();
+  });
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/base/test/test_bug338583.html
+++ b/content/base/test/test_bug338583.html
@@ -459,18 +459,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       ok(gEventSourceObj5_b.hits['fn_onmessage'] == 0, "Test 5.b failed");
       gEventSourceObj5_b.close();
       setTestHasFinished(test_id);
     }, parseInt(3000*stress_factor));
   }
 
   function doTest5_c(test_id)
   {
-    // credentials using the auth cache and cookies
-    var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+    // credentials using the auth cache
+    var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
     xhr.withCredentials = true;
     // also, test mixed mode UI
     xhr.open("GET", "https://example.com/tests/content/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.c");
 
       gEventSourceObj5_c = new EventSource("https://example.com/tests/content/base/test/file_restrictedEventSource.sjs?test=user1_evtsrc",
@@ -489,17 +489,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         gEventSourceObj5_c.close();
         doTest5_d(test_id);
       }, parseInt(3000*stress_factor));
     };
   }
 
   function doTest5_d(test_id)
   {
-    var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+    var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
     xhr.withCredentials = true;
     xhr.open("GET", "https://example.com/tests/content/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.d");
   
       gEventSourceObj5_d = new EventSource("https://example.com/tests/content/base/test/file_restrictedEventSource.sjs?test=user2_evtsrc");
       ok(!gEventSourceObj5_d.withCredentials, "Wrong withCredentials in test 5.d");
@@ -516,18 +516,18 @@ https://bugzilla.mozilla.org/show_bug.cg
         gEventSourceObj5_d.close();
         setTestHasFinished(test_id);
       }, parseInt(3000*stress_factor));
     };
   }
 
   function doTest5_e(test_id)
   {
-    // credentials using the auth cache and cookies
-    var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+    // credentials using the auth cache
+    var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
     xhr.withCredentials = true;
     xhr.open("GET", "http://example.org/tests/content/base/test/file_restrictedEventSource.sjs?test=user1_xhr", true, "user 1", "password 1");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.e");
 
       gEventSourceObj5_e = new EventSource("http://example.org/tests/content/base/test/file_restrictedEventSource.sjs?test=user1_evtsrc",
                                            { get withCredentials() { return true; } } );
@@ -545,17 +545,17 @@ https://bugzilla.mozilla.org/show_bug.cg
         gEventSourceObj5_e.close();
         doTest5_f(test_id);
       }, parseInt(5000*stress_factor));
     };
   }
 
   function doTest5_f(test_id)
   {
-    var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+    var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
     xhr.withCredentials = true;
     xhr.open("GET", "http://example.org/tests/content/base/test/file_restrictedEventSource.sjs?test=user2_xhr", true, "user 2", "password 2");
     xhr.send();
     xhr.onloadend = function() {
       ok(xhr.status == 200, "Failed to set credentials in test 5.f");
 
       gEventSourceObj5_f = new EventSource("http://example.org/tests/content/base/test/file_restrictedEventSource.sjs?test=user2_evtsrc",
                                            { });
--- a/content/base/test/test_bug426308.html
+++ b/content/base/test/test_bug426308.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 426308 **/
 
 const SJS_URL = "http://example.org:80/tests/content/base/test/bug426308-redirect.sjs";
 
 function startTest() {
-  var req = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+  var req = new XMLHttpRequest({mozAnon: true, mozSystem: true});
   req.open("GET", SJS_URL + "?" + window.location.href, false);
   req.send(null);
 
   is(req.status, 200, "Redirect did not happen");
 
   SimpleTest.finish();
 }
 
--- a/content/base/test/test_bug431701.html
+++ b/content/base/test/test_bug431701.html
@@ -46,17 +46,17 @@ function frameDoc(id) {
 
 function createDoc() {
   return document.implementation.createDocument('', 'html', null);
 }
 
 function xhrDoc(idx) {
   return function() {
     // Defy same-origin restrictions!
-    var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+    var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
     xhr.open("GET", docSources[idx], false);
     xhr.send();
     return xhr.responseXML;
   };
 }
 
 // Each row has the document getter function, then the characterSet,
 // inputEncoding expected for that document.
--- a/content/base/test/test_bug804395.html
+++ b/content/base/test/test_bug804395.html
@@ -14,41 +14,41 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 function test200() {
-  var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+  var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
   xhr.open('GET', 'jar:http://example.org/tests/content/base/test/file_bug804395.jar!/foo.bar', true);
   xhr.onreadystatechange = function() {
     if (xhr.readyState == 4) {
       ok(xhr.status == 200, "Existing file must have Status 200!");
       runTests();
     }
   }
   xhr.send(null);
 }
 
 function test404() {
-  var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+  var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
   xhr.open('GET', 'jar:http://example.org/tests/content/base/test/file_bug804395.jar!/foo.do_not_exist', true);
   xhr.onreadystatechange = function() {
     if (xhr.readyState == 4) {
       ok(xhr.status == 404, "Non existing file must have Status 404!");
       runTests();
     }
   }
   xhr.send(null);
 }
 
 function test0() {
-  var xhr = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+  var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true});
   xhr.open('GET', 'jar:http://example.org/tests/content/base/test/file_bug804395.jar!/foo.bar', true);
   ok(xhr.status == 0, "Not Sent request must have status 0");
   runTests();
 }
 
 var tests = [ test200, test404, test0 ];
 function runTests() {
   if (!tests.length) {
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug927196.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=426308
+-->
+<head>
+  <title>Test for Bug 426308</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=927196">Mozilla Bug 927196</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 927196 **/
+
+function startTest() {
+  req = new XMLHttpRequest({mozSystem: true});
+  is(req.mozAnon, true, "XMLHttpRequest should be mozAnon");
+
+  req = new XMLHttpRequest({mozAnon: true});
+  is(req.mozAnon, true, "XMLHttpRequest should be mozAnon");
+  is(req.mozSystem, false, "XMLHttpRequest should not be mozSystem");
+
+  req = new XMLHttpRequest({mozAnon: true, mozSystem: true});
+  is(req.mozAnon, true, "XMLHttpRequest should be mozAnon");
+  is(req.mozSystem, true, "XMLHttpRequest should be mozSystem");
+
+  req = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+  is(req.mozAnon, true, "XMLHttpRequest should be mozAnon");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+var req = new XMLHttpRequest({mozAnon: true});
+is(req.mozAnon, true, "XMLHttpRequest should be mozAnon");
+is(req.mozSystem, false, "XMLHttpRequest should not be mozSystem");
+
+req = new XMLHttpRequest({mozAnon: true, mozSystem: true});
+is(req.mozAnon, false, "XMLHttpRequest should be mozAnon");
+is(req.mozSystem, false, "XMLHttpRequest should not be mozSystem");
+
+addLoadEvent(function() {
+   SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], startTest);
+});
+</script>
+</pre>
+</body>
+</html>
--- a/content/base/test/test_xhr_forbidden_headers.html
+++ b/content/base/test/test_xhr_forbidden_headers.html
@@ -65,17 +65,17 @@ function  startTest() {
       value = channel.getRequestHeader(headers[i]);
     }
     catch(e) {}
 
     isnot(value, "test" + i, "Setting " + headers[i] + " header in unprivileged context");
   }
 
   // Try setting headers in privileged context
-  request = new XMLHttpRequest({mozAnon: false, mozSystem: true});
+  request = new XMLHttpRequest({mozAnon: true, mozSystem: true});
   request.open("GET", window.location.href);
   for (i = 0; i < headers.length; i++)
     request.setRequestHeader(headers[i], "test" + i);
 
   // Read out headers
   var channel = SpecialPowers.wrap(request).channel.QueryInterface(SpecialPowers.Ci.nsIHttpChannel);
   for (i = 0; i < headers.length; i++) {
     var value = channel.getRequestHeader(headers[i]);
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -279,23 +279,26 @@ MediaEngineWebRTCAudioSource::Init()
 
   // Check for availability.
   webrtc::VoEHardware* ptrVoEHw = webrtc::VoEHardware::GetInterface(mVoiceEngine);
   if (ptrVoEHw->SetRecordingDevice(mCapIndex)) {
     ptrVoEHw->Release();
     return;
   }
 
+#ifndef MOZ_B2G
+  // Because of the permission mechanism of B2G, we need to skip the status
+  // check here.
   bool avail = false;
   ptrVoEHw->GetRecordingDeviceStatus(avail);
   ptrVoEHw->Release();
   if (!avail) {
     return;
   }
-
+#endif // MOZ_B2G
   // Set "codec" to PCM, 32kHz on 1 channel
   webrtc::VoECodec* ptrVoECodec;
   webrtc::CodecInst codec;
   ptrVoECodec = webrtc::VoECodec::GetInterface(mVoiceEngine);
   if (!ptrVoECodec) {
     return;
   }
 
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -1,24 +1,29 @@
 /* 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/. */
 
+#ifdef MOZ_WIDGET_GONK
+#include "GonkPermission.h"
+#include "mozilla/dom/ContentParent.h"
+#endif // MOZ_WIDGET_GONK
 #include "nsContentPermissionHelper.h"
 #include "nsIContentPermissionPrompt.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMElement.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/unused.h"
 #include "nsComponentManagerUtils.h"
 
 using mozilla::unused;          // <snicker>
 using namespace mozilla::dom;
+using namespace mozilla;
 
 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy()
 {
   MOZ_COUNT_CTOR(nsContentPermissionRequestProxy);
 }
 
 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy()
 {
@@ -125,16 +130,24 @@ nsContentPermissionRequestProxy::Allow()
   }
 
   // Don't send out the delete message when the managing protocol (PBrowser) is
   // being destroyed and PContentPermissionRequest will soon be.
   if (mParent->IsBeingDestroyed()) {
     return NS_ERROR_FAILURE;
   }
 
+#ifdef MOZ_WIDGET_GONK
+  if (mType.Equals("audio-capture")) {
+    GonkPermissionService::GetInstance()->addGrantInfo(
+      "android.permission.RECORD_AUDIO",
+      static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
+  }
+#endif
+
   unused << ContentPermissionRequestParent::Send__delete__(mParent, true);
   mParent = nullptr;
   return NS_OK;
 }
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/icc/tests/marionette/manifest.ini
+++ b/dom/icc/tests/marionette/manifest.ini
@@ -2,16 +2,17 @@
 b2g = true
 browser = false
 qemu = true
 
 [test_stk_proactive_command.js]
 [test_icc_contact.js]
 [test_icc_card_lock.js]
 [test_icc_card_state.js]
+[test_icc_info.js]
 [test_stk_refresh.js]
 [test_stk_poll_off.js]
 [test_stk_setup_event_list.js]
 [test_stk_setup_call.js]
 [test_stk_send_ss.js]
 [test_stk_send_ussd.js]
 [test_stk_send_sms.js]
 [test_stk_send_dtmf.js]
--- a/dom/icc/tests/marionette/test_icc_contact.js
+++ b/dom/icc/tests/marionette/test_icc_contact.js
@@ -72,17 +72,17 @@ function testAddContact(type, pin2) {
     getRequest.onerror = function onerror() {
       ok(false, "Cannot get " + type + " contacts: " + getRequest.error.name);
       taskHelper.runNext();
     };
   };
 
   updateRequest.onerror = function onerror() {
     if (type === "fdn" && pin2 === undefined) {
-      ok(updateRequest.error.name === "pin2 is empty",
+      ok(updateRequest.error.name === "SimPin2",
          "expected error when pin2 is not provided");
     } else {
       ok(false, "Cannot add " + type + " contact: " + updateRequest.error.name);
     }
     taskHelper.runNext();
   };
 }
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -136,59 +136,44 @@ static nsresult ValidateTrackConstraints
     nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(),
                                       aNormalized.mMandatory,
                                       aOutUnknownConstraint);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
-/**
- * Send an error back to content. The error is the form a string.
- * Do this only on the main thread. The success callback is also passed here
- * so it can be released correctly.
- */
-class ErrorCallbackRunnable : public nsRunnable
+ErrorCallbackRunnable::ErrorCallbackRunnable(
+  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+  const nsAString& aErrorMsg, uint64_t aWindowID)
+  : mSuccess(aSuccess)
+  , mError(aError)
+  , mErrorMsg(aErrorMsg)
+  , mWindowID(aWindowID)
+  , mManager(MediaManager::GetInstance()) {
+  }
+
+NS_IMETHODIMP
+ErrorCallbackRunnable::Run()
 {
-public:
-  ErrorCallbackRunnable(
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    const nsAString& aErrorMsg, uint64_t aWindowID)
-    : mSuccess(aSuccess)
-    , mError(aError)
-    , mErrorMsg(aErrorMsg)
-    , mWindowID(aWindowID)
-    , mManager(MediaManager::GetInstance()) {}
+  // Only run if the window is still active.
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-  NS_IMETHOD
-  Run()
-  {
-    // Only run if the window is still active.
-    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
 
-    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
-    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
-
-    if (!(mManager->IsWindowStillActive(mWindowID))) {
-      return NS_OK;
-    }
-    // This is safe since we're on main-thread, and the windowlist can only
-    // be invalidated from the main-thread (see OnNavigation)
-    error->OnError(mErrorMsg);
+  if (!(mManager->IsWindowStillActive(mWindowID))) {
     return NS_OK;
   }
-
-private:
-  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
-  const nsString mErrorMsg;
-  uint64_t mWindowID;
-  nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
-};
+  // This is safe since we're on main-thread, and the windowlist can only
+  // be invalidated from the main-thread (see OnNavigation)
+  error->OnError(mErrorMsg);
+  return NS_OK;
+}
 
 /**
  * Invoke the "onSuccess" callback in content. The callback will take a
  * DOMBlob in the case of {picture:true}, and a MediaStream in the case of
  * {audio:true} or {video:true}. There is a constructor available for each
  * form. Do this only on the main thread.
  */
 class SuccessCallbackRunnable : public nsRunnable
@@ -577,28 +562,30 @@ public:
     trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
 
     // The listener was added at the begining in an inactive state.
     // Activate our listener. We'll call Start() on the source when get a callback
     // that the MediaStream has started consuming. The listener is freed
     // when the page is invalidated (on navigation or close).
     mListener->Activate(stream.forget(), mAudioSource, mVideoSource);
 
+    // Note: includes JS callbacks; must be released on MainThread
     TracksAvailableCallback* tracksAvailableCallback =
       new TracksAvailableCallback(mManager, mSuccess, mWindowID, trackunion);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of trackunion to the MediaOperationRunnable
     // to ensure it's kept alive until the MediaOperationRunnable runs (at least).
     nsIThread *mediaThread = MediaManager::GetThread();
     nsRefPtr<MediaOperationRunnable> runnable(
       new MediaOperationRunnable(MEDIA_START, mListener, trackunion,
                                  tracksAvailableCallback,
-                                 mAudioSource, mVideoSource, false, mWindowID));
+                                 mAudioSource, mVideoSource, false, mWindowID,
+                                 mError.forget()));
     mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
 #ifdef MOZ_WEBRTC
     // Right now these configs are only of use if webrtc is available
     nsresult rv;
     nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
@@ -1798,17 +1785,17 @@ GetUserMediaCallbackMediaStreamListener:
   nsRefPtr<MediaOperationRunnable> runnable;
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   runnable = new MediaOperationRunnable(MEDIA_STOP,
                                         this, nullptr, nullptr,
                                         mAudioSource, mVideoSource,
-                                        mFinished, mWindowID);
+                                        mFinished, mWindowID, nullptr);
   mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   mFinished = true;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -207,71 +207,125 @@ class GetUserMediaNotificationEvent: pub
                                   GetUserMediaStatus aStatus,
                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
     : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
     , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
 
     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
                                   already_AddRefed<DOMMediaStream> aStream,
                                   DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
-                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
+                                  bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
+                                  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
     : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
-      mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+      mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
+      mError(aError) {}
     virtual ~GetUserMediaNotificationEvent()
     {
 
     }
 
     NS_IMETHOD Run() MOZ_OVERRIDE;
 
   protected:
     nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
     nsRefPtr<DOMMediaStream> mStream;
     nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
     GetUserMediaStatus mStatus;
     bool mIsAudio;
     bool mIsVideo;
     uint64_t mWindowID;
+    nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef enum {
   MEDIA_START,
   MEDIA_STOP
 } MediaOperation;
 
+class MediaManager;
+
+/**
+ * Send an error back to content. The error is the form a string.
+ * Do this only on the main thread. The success callback is also passed here
+ * so it can be released correctly.
+ */
+class ErrorCallbackRunnable : public nsRunnable
+{
+public:
+  ErrorCallbackRunnable(
+    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    const nsAString& aErrorMsg, uint64_t aWindowID);
+  NS_IMETHOD Run();
+private:
+  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  const nsString mErrorMsg;
+  uint64_t mWindowID;
+  nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
+};
+
+class ReleaseMediaOperationResource : public nsRunnable
+{
+public:
+  ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
+    DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback):
+    mStream(aStream),
+    mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
+  NS_IMETHOD Run() MOZ_OVERRIDE {return NS_OK;}
+private:
+  nsRefPtr<DOMMediaStream> mStream;
+  nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
+};
+
 // Generic class for running long media operations like Start off the main
 // thread, and then (because nsDOMMediaStreams aren't threadsafe),
 // ProxyReleases mStream since it's cycle collected.
 class MediaOperationRunnable : public nsRunnable
 {
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationRunnable(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
     bool aNeedsFinish,
-    uint64_t aWindowID)
+    uint64_t aWindowID,
+    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
     : mType(aType)
     , mStream(aStream)
     , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
     , mFinish(aNeedsFinish)
     , mWindowID(aWindowID)
-    {}
+    , mError(aError)
+  {}
 
   ~MediaOperationRunnable()
   {
     // MediaStreams can be released on any thread.
   }
 
+  nsresult returnAndCallbackError(nsresult rv, const char* errorLog)
+  {
+    MM_LOG(("%s , rv=%d", errorLog, rv));
+    NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
+          mOnTracksAvailableCallback.forget()));
+    nsString log;
+
+    log.AssignASCII(errorLog, strlen(errorLog));
+    NS_DispatchToMainThread(new ErrorCallbackRunnable(nullptr, mError.forget(),
+      log, mWindowID));
+    return NS_OK;
+  }
+
   NS_IMETHOD
   Run() MOZ_OVERRIDE
   {
     SourceMediaStream *source = mListener->GetSourceStream();
     // No locking between these is required as all the callbacks for the
     // same MediaStream will occur on the same thread.
     if (!source) // means the stream was never Activated()
       return NS_OK;
@@ -285,41 +339,43 @@ public:
           source->SetPullEnabled(true);
 
           DOMMediaStream::TrackTypeHints expectedTracks = 0;
           if (mAudioSource) {
             rv = mAudioSource->Start(source, kAudioTrack);
             if (NS_SUCCEEDED(rv)) {
               expectedTracks |= DOMMediaStream::HINT_CONTENTS_AUDIO;
             } else {
-              MM_LOG(("Starting audio failed, rv=%d",rv));
+              return returnAndCallbackError(rv, "Starting audio failed");
             }
           }
           if (mVideoSource) {
             rv = mVideoSource->Start(source, kVideoTrack);
             if (NS_SUCCEEDED(rv)) {
               expectedTracks |= DOMMediaStream::HINT_CONTENTS_VIDEO;
             } else {
-              MM_LOG(("Starting video failed, rv=%d",rv));
+              return returnAndCallbackError(rv, "Starting video failed");
             }
           }
 
           mOnTracksAvailableCallback->SetExpectedTracks(expectedTracks);
 
           MM_LOG(("started all sources"));
           // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
           // because mOnTracksAvailableCallback needs to be added to mStream
           // on the main thread.
-          nsRefPtr<GetUserMediaNotificationEvent> event =
+          nsIRunnable *event =
             new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
                                               mStream.forget(),
                                               mOnTracksAvailableCallback.forget(),
                                               mAudioSource != nullptr,
                                               mVideoSource != nullptr,
-                                              mWindowID);
+                                              mWindowID, mError.forget());
+          // event must always be released on mainthread due to the JS callbacks
+          // in the TracksAvailableCallback
           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
         }
         break;
 
       case MEDIA_STOP:
         {
           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
           if (mAudioSource) {
@@ -329,23 +385,24 @@ public:
           if (mVideoSource) {
             mVideoSource->Stop(source, kVideoTrack);
             mVideoSource->Deallocate();
           }
           // Do this after stopping all tracks with EndTrack()
           if (mFinish) {
             source->Finish();
           }
-          nsRefPtr<GetUserMediaNotificationEvent> event =
+          nsIRunnable *event =
             new GetUserMediaNotificationEvent(mListener,
                                               GetUserMediaNotificationEvent::STOPPING,
                                               mAudioSource != nullptr,
                                               mVideoSource != nullptr,
                                               mWindowID);
-
+          // event must always be released on mainthread due to the JS callbacks
+          // in the TracksAvailableCallback
           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
         }
         break;
 
       default:
         MOZ_ASSERT(false,"invalid MediaManager operation");
         break;
     }
@@ -356,16 +413,17 @@ private:
   MediaOperation mType;
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
   bool mFinish;
   uint64_t mWindowID;
+  nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
--- a/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
+++ b/dom/mobilemessage/interfaces/nsIDOMMobileMessageManager.idl
@@ -6,17 +6,17 @@
 
 interface nsIDOMEventListener;
 interface nsIDOMMozSmsFilter;
 interface nsIDOMMozSmsSegmentInfo;
 interface nsIDOMDOMCursor;
 interface nsIDOMDOMRequest;
 interface nsIDOMBlob;
 
-[scriptable, builtinclass, uuid(a99c3538-a8d6-492f-9ece-f6e92f9c00c5)]
+[scriptable, builtinclass, uuid(8ec8247d-3f5f-41af-9c72-9dc857e3be81)]
 interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
 {
   nsIDOMDOMRequest getSegmentInfoForText(in DOMString text);
 
 
   /**
    * Function to send SMS.
    *
@@ -47,16 +47,17 @@ interface nsIDOMMozMobileMessageManager 
   [implicit_jscontext, optional_argc]
   nsIDOMDOMRequest sendMMS(in jsval parameters,
                            [optional] in jsval sendParameters);
 
   [binaryname(GetMessageMoz)]
   nsIDOMDOMRequest getMessage(in long id);
 
   // The parameter can be either a message id or a nsIDOMMoz{Mms,Sms}Message.
+  [implicit_jscontext]
   nsIDOMDOMRequest delete(in jsval param);
 
   // Iterates through nsIDOMMoz{Mms,Sms}Message.
   nsIDOMDOMCursor getMessages(in nsIDOMMozSmsFilter filter, in boolean reverse);
 
   nsIDOMDOMRequest markMessageRead(in long id, in boolean value,
                                    [optional] in boolean aSendReadReport);
 
--- a/dom/mobilemessage/src/MobileMessageManager.cpp
+++ b/dom/mobilemessage/src/MobileMessageManager.cpp
@@ -176,20 +176,23 @@ MobileMessageManager::Send(const JS::Val
     return NS_ERROR_INVALID_ARG;
   }
 
   nsresult rv;
   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_STATE(sc);
 
-  JS::Rooted<JSObject*> global(aCx, sc->GetWindowProxy());
-  NS_ASSERTION(global, "Failed to get global object!");
+  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
 
-  JSAutoCompartment ac(aCx, global);
+  mozilla::Maybe<JSAutoCompartment> ac;
+  if (!global) {
+    global = sc->GetWindowProxy();
+    ac.construct(aCx, global);
+  }
 
   nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
 
   // Use the default one unless |aSendParams.serviceId| is available.
   uint32_t serviceId;
   rv = smsService->GetSmsDefaultServiceId(&serviceId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -296,80 +299,77 @@ MobileMessageManager::GetMessageMoz(int3
   nsresult rv = mobileMessageDBService->GetMessageMoz(aId, msgCallback);
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(aRequest);
   return NS_OK;
 }
 
 nsresult
-MobileMessageManager::GetMessageId(AutoPushJSContext &aCx,
-                                   const JS::Value &aMessage, int32_t &aId)
+MobileMessageManager::GetMessageId(JSContext* aCx,
+                                   const JS::Value& aMessage, int32_t* aId)
 {
   nsCOMPtr<nsIDOMMozSmsMessage> smsMessage =
     do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, &aMessage.toObject()));
   if (smsMessage) {
-    return smsMessage->GetId(&aId);
+    return smsMessage->GetId(aId);
   }
 
   nsCOMPtr<nsIDOMMozMmsMessage> mmsMessage =
     do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, &aMessage.toObject()));
   if (mmsMessage) {
-    return mmsMessage->GetId(&aId);
+    return mmsMessage->GetId(aId);
   }
 
   return NS_ERROR_INVALID_ARG;
 }
 
 NS_IMETHODIMP
-MobileMessageManager::Delete(const JS::Value& aParam, nsIDOMDOMRequest** aRequest)
+MobileMessageManager::Delete(const JS::Value& aParam, JSContext* aCx,
+                             nsIDOMDOMRequest** aRequest)
 {
   // We expect Int32, SmsMessage, MmsMessage, Int32[], SmsMessage[], MmsMessage[]
   if (!aParam.isObject() && !aParam.isInt32()) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  nsresult rv;
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  AutoPushJSContext cx(sc->GetNativeContext());
-  NS_ENSURE_STATE(sc);
-
+  nsresult rv = NS_OK;
   int32_t id, *idArray;
   uint32_t size;
 
   if (aParam.isInt32()) {
     // Single Integer Message ID
     id = aParam.toInt32();
 
     size = 1;
     idArray = &id;
-  } else if (!JS_IsArrayObject(cx, &aParam.toObject())) {
+  } else if (!JS_IsArrayObject(aCx, &aParam.toObject())) {
     // Single SmsMessage/MmsMessage object
-    rv = GetMessageId(cx, aParam, id);
+    rv = GetMessageId(aCx, aParam, &id);
     NS_ENSURE_SUCCESS(rv, rv);
 
     size = 1;
     idArray = &id;
   } else {
     // Int32[], SmsMessage[], or MmsMessage[]
-    JS::Rooted<JSObject*> ids(cx, &aParam.toObject());
+    JS::Rooted<JSObject*> ids(aCx, &aParam.toObject());
 
-    JS_ALWAYS_TRUE(JS_GetArrayLength(cx, ids, &size));
+    JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, ids, &size));
     nsAutoArrayPtr<int32_t> idAutoArray(new int32_t[size]);
 
-    JS::Rooted<JS::Value> idJsValue(cx);
+    JS::Rooted<JS::Value> idJsValue(aCx);
     for (uint32_t i = 0; i < size; i++) {
-      if (!JS_GetElement(cx, ids, i, &idJsValue)) {
+      if (!JS_GetElement(aCx, ids, i, &idJsValue)) {
         return NS_ERROR_INVALID_ARG;
       }
 
       if (idJsValue.isInt32()) {
         idAutoArray[i] = idJsValue.toInt32();
       } else if (idJsValue.isObject()) {
-        rv = GetMessageId(cx, idJsValue, id);
+        rv = GetMessageId(aCx, idJsValue, &id);
         NS_ENSURE_SUCCESS(rv, rv);
 
         idAutoArray[i] = id;
       }
     }
 
     idArray = idAutoArray.forget();
   }
--- a/dom/mobilemessage/src/MobileMessageManager.h
+++ b/dom/mobilemessage/src/MobileMessageManager.h
@@ -42,16 +42,16 @@ private:
 
   nsresult DispatchTrustedSmsEventToSelf(const char* aTopic,
                                          const nsAString& aEventName,
                                          nsISupports* aMsg);
 
   /**
    * Helper to get message ID from SMS/MMS Message object
    */
-  nsresult GetMessageId(AutoPushJSContext &aCx, const JS::Value &aMessage,
-                        int32_t &aId);
+  nsresult GetMessageId(JSContext* aCx, const JS::Value& aMessage,
+                        int32_t* aId);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_mobilemessage_MobileMessageManager_h
--- a/dom/network/tests/marionette/manifest.ini
+++ b/dom/network/tests/marionette/manifest.ini
@@ -1,17 +1,16 @@
 [DEFAULT]
 b2g = true
 browser = false
 qemu = true
 
 [test_mobile_networks.js]
 disabled = Bug 808783
 [test_mobile_voice_state.js]
-[test_mobile_iccinfo.js]
 [test_mobile_operator_names.js]
 [test_mobile_preferred_network_type.js]
 disabled = Bug 808783
 [test_mobile_data_connection.js]
 [test_mobile_data_location.js]
 [test_mobile_data_state.js]
 [test_mobile_mmi.js]
 [test_mobile_roaming_preference.js]
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1216,17 +1216,18 @@ RadioInterface.prototype = {
         break;
       case "cardstatechange":
         this.rilContext.cardState = message.cardState;
         gMessageManager.sendIccMessage("RIL:CardStateChanged",
                                        this.clientId, message);
         break;
       case "sms-received":
         let ackOk = this.handleSmsReceived(message);
-        if (ackOk) {
+        // Note: ACK has been done by modem for NEW_SMS_ON_SIM
+        if (ackOk && message.simStatus === undefined) {
           this.workerMessenger.send("ackSMS", { result: RIL.PDU_FCS_OK });
         }
         return;
       case "broadcastsms-received":
       case "cellbroadcast-received":
         message.timestamp = Date.now();
         gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
                                                  this.clientId, message);
@@ -2057,20 +2058,23 @@ RadioInterface.prototype = {
                                            this.clientId, mwi);
       return true;
     }
 
     let notifyReceived = function notifyReceived(rv, domMessage) {
       let success = Components.isSuccessCode(rv);
 
       // Acknowledge the reception of the SMS.
-      this.workerMessenger.send("ackSMS", {
-        result: (success ? RIL.PDU_FCS_OK
-                         : RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED)
-      });
+      // Note: Ack has been done by modem for NEW_SMS_ON_SIM
+      if (message.simStatus === undefined) {
+        this.workerMessenger.send("ackSMS", {
+          result: (success ? RIL.PDU_FCS_OK
+                           : RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED)
+        });
+      }
 
       if (!success) {
         // At this point we could send a message to content to notify the user
         // that storing an incoming SMS failed, most likely due to a full disk.
         if (DEBUG) {
           this.debug("Could not store SMS, error code " + rv);
         }
         return;
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -662,16 +662,23 @@ this.USIM_TAG_NAME[ICC_USIM_EFANR_TAG] =
 this.USIM_TAG_NAME[ICC_USIM_EFPBC_TAG] = "pbc";
 this.USIM_TAG_NAME[ICC_USIM_EFGRP_TAG] = "grp";
 this.USIM_TAG_NAME[ICC_USIM_EFAAS_TAG] = "aas";
 this.USIM_TAG_NAME[ICC_USIM_EFGSD_TAG] = "gsd";
 this.USIM_TAG_NAME[ICC_USIM_EFUID_TAG] = "uid";
 this.USIM_TAG_NAME[ICC_USIM_EFEMAIL_TAG] = "email";
 this.USIM_TAG_NAME[ICC_USIM_EFCCP1_TAG] = "ccp1";
 
+// Error message for ICC contact.
+this.CONTACT_ERR_REQUEST_NOT_SUPPORTED = GECKO_ERROR_REQUEST_NOT_SUPPORTED;
+this.CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED = "ContactTypeNotSupported";
+this.CONTACT_ERR_FIELD_NOT_SUPPORTED = "FieldNotSupported";
+this.CONTACT_ERR_NO_FREE_RECORD_FOUND = "NoFreeRecordFound";
+this.CONTACT_ERR_CANNOT_ACCESS_PHONEBOOK = "CannotAccessPhoneBook";
+
 // CDMA IMSI_M's byte const.
 // 3GPP2 C.S0065 Sec. 5.2.2
 this.CSIM_IMSI_M_MIN2_BYTE = 1;
 this.CSIM_IMSI_M_MIN1_BYTE = 3;
 this.CSIM_IMSI_M_MNC_BYTE = 6;
 this.CSIM_IMSI_M_PROGRAMMED_BYTE = 7;
 this.CSIM_IMSI_M_MCC_BYTE = 8;
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -846,17 +846,17 @@ let RIL = {
    *
    * @param contactType
    *        "adn" or "fdn".
    * @param requestId
    *        Request id from RadioInterfaceLayer.
    */
   readICCContacts: function readICCContacts(options) {
     if (!this.appType) {
-      options.errorMsg = GECKO_ERROR_REQUEST_NOT_SUPPORTED;
+      options.errorMsg = CONTACT_ERR_REQUEST_NOT_SUPPORTED;
       this.sendChromeMessage(options);
       return;
     }
 
     ICCContactHelper.readICCContacts(
       this.appType,
       options.contactType,
       function onsuccess(contacts) {
@@ -894,17 +894,17 @@ let RIL = {
     }.bind(this);
 
     let onerror = function onerror(errorMsg) {
       options.errorMsg = errorMsg;
       RIL.sendChromeMessage(options);
     }.bind(this);
 
     if (!this.appType || !options.contact) {
-      onerror(GECKO_ERROR_REQUEST_NOT_SUPPORTED);
+      onerror(CONTACT_ERR_REQUEST_NOT_SUPPORTED );
       return;
     }
 
     let contact = options.contact;
     let iccid = RIL.iccInfo.iccid;
     let isValidRecordId = false;
     if (typeof contact.contactId === "string" &&
         contact.contactId.startsWith(iccid)) {
@@ -6088,18 +6088,30 @@ RIL[UNSOLICITED_RESPONSE_NEW_SMS] = func
   // Not reserved FCS values, send ACK now.
   this.acknowledgeGsmSms(result == PDU_FCS_OK, result);
 };
 RIL[UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT] = function UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT(length) {
   let result = this._processSmsStatusReport(length);
   this.acknowledgeGsmSms(result == PDU_FCS_OK, result);
 };
 RIL[UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM] = function UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM(length) {
-  // let info = Buf.readInt32List();
-  //TODO
+  let recordNumber = Buf.readInt32List()[0];
+
+  ICCRecordHelper.readSMS(
+    recordNumber,
+    function onsuccess(message) {
+      if (message && message.simStatus === 3) { //New Unread SMS
+        this._processSmsMultipart(message);
+      }
+    }.bind(this),
+    function onerror(errorMsg) {
+      if (DEBUG) {
+        debug("Failed to Read NEW SMS on SIM #" + recordNumber + ", errorMsg: " + errorMsg);
+      }
+    });
 };
 RIL[UNSOLICITED_ON_USSD] = function UNSOLICITED_ON_USSD() {
   let [typeCode, message] = Buf.readStringList();
   if (DEBUG) {
     debug("On USSD. Type Code: " + typeCode + " Message: " + message);
   }
 
   this._ussdSession = (typeCode != "0" && typeCode != "2");
@@ -10828,16 +10840,17 @@ let ICCFileHelper = {
 
   /**
    * This function handles EFs for SIM.
    */
   getSimEFPath: function getSimEFPath(fileId) {
     switch (fileId) {
       case ICC_EF_FDN:
       case ICC_EF_MSISDN:
+      case ICC_EF_SMS:
         return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM;
       case ICC_EF_AD:
       case ICC_EF_MBDN:
       case ICC_EF_PLMNsel:
       case ICC_EF_SPN:
       case ICC_EF_SPDI:
       case ICC_EF_SST:
       case ICC_EF_PHASE:
@@ -10864,16 +10877,17 @@ let ICCFileHelper = {
       case ICC_EF_MSISDN:
       case ICC_EF_SPN:
       case ICC_EF_SPDI:
       case ICC_EF_CBMI:
       case ICC_EF_CBMID:
       case ICC_EF_CBMIR:
       case ICC_EF_OPL:
       case ICC_EF_PNN:
+      case ICC_EF_SMS:
         return EF_PATH_MF_SIM + EF_PATH_ADF_USIM;
       default:
         // The file ids in USIM phone book entries are decided by the
         // card manufacturer. So if we don't match any of the cases
         // above and if its a USIM return the phone book path.
         return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM + EF_PATH_DF_PHONEBOOK;
     }
   },
@@ -11143,29 +11157,30 @@ let ICCIOHelper = {
       options.callback(options);
     }
   },
 
   /**
    * Process ICC IO error.
    */
   processICCIOError: function processICCIOError(options) {
-    let error = options.onerror || debug;
-
-    // See GSM11.11, TS 51.011 clause 9.4, and ISO 7816-4 for the error
-    // description.
-    let errorMsg = "ICC I/O Error code " +
-                   RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError] +
-                   " EF id = " + options.fileId.toString(16) +
-                   " command = " + options.command.toString(16);
-    if (options.sw1 && options.sw2) {
-      errorMsg += "(" + options.sw1.toString(16) +
-                  "/" + options.sw2.toString(16) + ")";
-    }
-    error(errorMsg);
+    let requestError = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
+    if (DEBUG) {
+      // See GSM11.11, TS 51.011 clause 9.4, and ISO 7816-4 for the error
+      // description.
+      let errorMsg = "ICC I/O Error code " + requestError +
+                     " EF id = " + options.fileId.toString(16) +
+                     " command = " + options.command.toString(16);
+      if (options.sw1 && options.sw2) {
+        errorMsg += "(" + options.sw1.toString(16) +
+                    "/" + options.sw2.toString(16) + ")";
+      }
+      debug(errorMsg);
+    }
+    onerror(requestError);
   },
 };
 ICCIOHelper[ICC_COMMAND_SEEK] = null;
 ICCIOHelper[ICC_COMMAND_READ_BINARY] = function ICC_COMMAND_READ_BINARY(options) {
   this.processICCIOReadBinary(options);
 };
 ICCIOHelper[ICC_COMMAND_READ_RECORD] = function ICC_COMMAND_READ_RECORD(options) {
   this.processICCIOReadRecord(options);
@@ -11636,18 +11651,20 @@ let ICCRecordHelper = {
       }
 
       Buf.readStringDelimiter(strLen);
 
       if (options.p1 < options.totalRecords) {
         ICCIOHelper.loadNextRecord(options);
       } else {
         // No free record found.
-        let error = onerror || debug;
-        error("No free record found.");
+        if (DEBUG) {
+          debug(CONTACT_ERR_NO_FREE_RECORD_FOUND);
+        }
+        onerror(CONTACT_ERR_NO_FREE_RECORD_FOUND);
       }
     }
 
     ICCIOHelper.loadLinearFixedEF({fileId: fileId,
                                    callback: callback.bind(this),
                                    onerror: onerror});
   },
 };
@@ -12233,16 +12250,56 @@ let SimRecordHelper = {
       } catch (e) {
         if (DEBUG) debug("readPLMNEntries: PLMN entry " + index + " is invalid.");
         break;
       }
       index ++;
     }
     return plmnList;
   },
+
+  /**
+   * Read the SMS from the ICC.
+   *
+   * @param recordNumber The number of the record shall be loaded.
+   * @param onsuccess    Callback to be called when success.
+   * @param onerror      Callback to be called when error.
+   */
+  readSMS: function readSMS(recordNumber, onsuccess, onerror) {
+    function callback(options) {
+      let strLen = Buf.readInt32();
+
+      // TS 51.011, 10.5.3 EF_SMS
+      // b3 b2 b1
+      //  0  0  1 message received by MS from network; message read
+      //  0  1  1 message received by MS from network; message to be read
+      //  1  1  1 MS originating message; message to be sent
+      //  1  0  1 MS originating message; message sent to the network:
+      let status = GsmPDUHelper.readHexOctet();
+
+      let message = GsmPDUHelper.readMessage();
+      message.simStatus = status;
+
+      // Consumes the remaining buffer
+      Buf.seekIncoming(Buf.getReadAvailable() - Buf.PDU_HEX_OCTET_SIZE);
+
+      Buf.readStringDelimiter(strLen);
+
+      if (message) {
+        onsuccess(message);
+      } else {
+        onerror("Failed to decode SMS on SIM #" + recordNumber);
+      }
+    }
+
+    ICCIOHelper.loadLinearFixedEF({fileId: ICC_EF_SMS,
+                                   recordNumber: recordNumber,
+                                   callback: callback,
+                                   onerror: onerror});
+  },
 };
 
 let RuimRecordHelper = {
   fetchRuimRecords: function fetchRuimRecords() {
     this.getIMSI_M();
     this.readCST();
     this.readCDMAHome();
     RIL.getCdmaSubscription();
@@ -12876,18 +12933,20 @@ let ICCContactHelper = {
         } else {
           this.readUSimContacts(onsuccess, onerror);
         }
         break;
       case "fdn":
         ICCRecordHelper.readADNLike(ICC_EF_FDN, onsuccess, onerror);
         break;
       default:
-        let error = onerror || debug;
-        error(GECKO_ERROR_REQUEST_NOT_SUPPORTED);
+        if (DEBUG) {
+          debug("Unsupported contactType :" + contactType);
+        }
+        onerror(CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED);
         break;
     }
   },
 
   /**
    * Helper function to find free contact record.
    *
    * @param appType       One of CARD_APPTYPE_*.
@@ -12907,34 +12966,38 @@ let ICCContactHelper = {
 
           ICCRecordHelper.readPBR(gotPbrCb, onerror);
         }
         break;
       case "fdn":
         ICCRecordHelper.findFreeRecordId(ICC_EF_FDN, onsuccess.bind(null, 0), onerror);
         break;
       default:
-        let error = onerror || debug;
-        error(GECKO_ERROR_REQUEST_NOT_SUPPORTED);
+        if (DEBUG) {
+          debug("Unsupported contactType :" + contactType);
+        }
+        onerror(CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED);
         break;
     }
   },
 
    /**
     * Find free ADN record id in USIM.
     *
     * @param pbrs          All Phonebook Reference Files read.
     * @param onsuccess     Callback to be called when success.
     * @param onerror       Callback to be called when error.
     */
   findUSimFreeADNRecordId: function findUSimFreeADNRecordId(pbrs, onsuccess, onerror) {
     (function findFreeRecordId(pbrIndex) {
       if (pbrIndex >= pbrs.length) {
-        let error = onerror || debug;
-        error("No free record found.");
+        if (DEBUG) {
+          debug(CONTACT_ERR_NO_FREE_RECORD_FOUND);
+        }
+        onerror(CONTACT_ERR_NO_FREE_RECORD_FOUND);
         return;
       }
 
       let pbr = pbrs[pbrIndex];
       ICCRecordHelper.findFreeRecordId(
         pbr.adn.fileId,
         onsuccess.bind(this, pbrIndex),
         findFreeRecordId.bind(null, pbrIndex + 1));
@@ -12968,34 +13031,36 @@ let ICCContactHelper = {
    * @param appType       One of CARD_APPTYPE_*.
    * @param contactType   "adn" or "fdn".
    * @param contact       The contact will be updated.
    * @param pin2          PIN2 is required for FDN.
    * @param onsuccess     Callback to be called when success.
    * @param onerror       Callback to be called when error.
    */
   updateICCContact: function updateICCContact(appType, contactType, contact, pin2, onsuccess, onerror) {
-   let error = onerror || debug;
     switch (contactType) {
       case "adn":
         if (!this.hasDfPhoneBook(appType)) {
           ICCRecordHelper.updateADNLike(ICC_EF_ADN, contact, null, onsuccess, onerror);
         } else {
           this.updateUSimContact(contact, onsuccess, onerror);
         }
         break;
       case "fdn":
         if (!pin2) {
-          error("pin2 is empty");
+          onerror(GECKO_ERROR_SIM_PIN2);
           return;
         }
         ICCRecordHelper.updateADNLike(ICC_EF_FDN, contact, pin2, onsuccess, onerror);
         break;
       default:
-        error(GECKO_ERROR_REQUEST_NOT_SUPPORTED);
+        if (DEBUG) {
+          debug("Unsupported contactType :" + contactType);
+        }
+        onerror(CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED);
         break;
     }
   },
 
   /**
    * Read contacts from USIM.
    *
    * @param onsuccess     Callback to be called when success.
@@ -13156,18 +13221,20 @@ let ICCContactHelper = {
       switch (ef) {
         case USIM_PBR_EMAIL:
           ICCRecordHelper.readEmail(fileId, fileType, recordId, gotFieldCb, onerror);
           break;
         case USIM_PBR_ANR:
           ICCRecordHelper.readANR(fileId, fileType, recordId, gotFieldCb, onerror);
           break;
         default:
-          let error = onerror || debug;
-          error("Unknown field " + field);
+          if (DEBUG) {
+            debug("Unsupported field :" + field);
+          }
+          onerror(CONTACT_ERR_FIELD_NOT_SUPPORTED);
           break;
       }
     }.bind(this);
 
     this.getContactFieldRecordId(pbr, contact, field, gotRecordIdCb, onerror);
   },
 
   /**
@@ -13197,34 +13264,38 @@ let ICCContactHelper = {
 
         if (onsuccess) {
           onsuccess(recordId);
         }
       }.bind(this);
 
       ICCRecordHelper.readIAP(pbr.iap.fileId, contact.recordId, gotIapCb, onerror);
     } else {
-      let error = onerror | debug;
-      error("USIM PBR files in Type 3 format are not supported.");
+      if (DEBUG) {
+        debug("USIM PBR files in Type 3 format are not supported.");
+      }
+      onerror(CONTACT_ERR_REQUEST_NOT_SUPPORTED);
     }
   },
 
   /**
    * Update USIM contact.
    *
    * @param contact       The contact will be updated.
    * @param onsuccess     Callback to be called when success.
    * @param onerror       Callback to be called when error.
    */
   updateUSimContact: function updateUSimContact(contact, onsuccess, onerror) {
     let gotPbrCb = function gotPbrCb(pbrs) {
       let pbr = pbrs[contact.pbrIndex];
       if (!pbr) {
-        let error = onerror || debug;
-        error("Cannot access Phonebook.");
+        if (DEBUG) {
+          debug(CONTACT_ERR_CANNOT_ACCESS_PHONEBOOK);
+        }
+        onerror(CONTACT_ERR_CANNOT_ACCESS_PHONEBOOK);
         return;
       }
       this.updatePhonebookSet(pbr, contact, onsuccess, onerror);
     }.bind(this);
 
     ICCRecordHelper.readPBR(gotPbrCb, onerror);
   },
 
@@ -13291,16 +13362,21 @@ let ICCContactHelper = {
    * @param onsuccess     Callback to be called when success.
    * @param onerror       Callback to be called when error.
    */
   updateContactField: function updateContactField(pbr, contact, field, onsuccess, onerror) {
     if (pbr[field].fileType === ICC_USIM_TYPE1_TAG) {
       this.updateContactFieldType1(pbr, contact, field, onsuccess, onerror);
     } else if (pbr[field].fileType === ICC_USIM_TYPE2_TAG) {
       this.updateContactFieldType2(pbr, contact, field, onsuccess, onerror);
+    } else {
+      if (DEBUG) {
+        debug("USIM PBR files in Type 3 format are not supported.");
+      }
+      onerror(CONTACT_ERR_REQUEST_NOT_SUPPORTED);
     }
   },
 
   /**
    * Update Type 1 USIM contact fields.
    *
    * @param pbr           The phonebook reference file.
    * @param contact       The contact needs to be updated.
@@ -13308,16 +13384,21 @@ let ICCContactHelper = {
    * @param onsuccess     Callback to be called when success.
    * @param onerror       Callback to be called when error.
    */
   updateContactFieldType1: function updateContactFieldType1(pbr, contact, field, onsuccess, onerror) {
     if (field === USIM_PBR_EMAIL) {
       ICCRecordHelper.updateEmail(pbr, contact.recordId, contact.email, null, onsuccess, onerror);
     } else if (field === USIM_PBR_ANR0) {
       ICCRecordHelper.updateANR(pbr, contact.recordId, contact.anr[0], null, onsuccess, onerror);
+    } else {
+     if (DEBUG) {
+       debug("Unsupported field :" + field);
+     }
+     onerror(CONTACT_ERR_FIELD_NOT_SUPPORTED);
     }
   },
 
   /**
    * Update Type 2 USIM contact fields.
    *
    * @param pbr           The phonebook reference file.
    * @param contact       The contact needs to be updated.
@@ -13342,17 +13423,23 @@ let ICCContactHelper = {
         return;
       }
 
       // Case 2.
       if (field === USIM_PBR_EMAIL) {
         ICCRecordHelper.updateEmail(pbr, recordId, contact.email, contact.recordId, onsuccess, onerror);
       } else if (field === USIM_PBR_ANR0) {
         ICCRecordHelper.updateANR(pbr, recordId, contact.anr[0], contact.recordId, onsuccess, onerror);
-      }
+      } else {
+        if (DEBUG) {
+          debug("Unsupported field :" + field);
+        }
+        onerror(CONTACT_ERR_FIELD_NOT_SUPPORTED);
+      }
+
     }.bind(this);
 
     ICCRecordHelper.readIAP(pbr.iap.fileId, contact.recordId, gotIapCb, onerror);
   },
 
   /**
    * Add Type 2 USIM contact fields.
    *
@@ -13371,18 +13458,20 @@ let ICCContactHelper = {
       if (field === USIM_PBR_EMAIL) {
         ICCRecordHelper.updateEmail(pbr, recordId, contact.email, contact.recordId, updateCb, onerror);
       } else if (field === USIM_PBR_ANR0) {
         ICCRecordHelper.updateANR(pbr, recordId, contact.anr[0], contact.recordId, updateCb, onerror);
       }
     }.bind(this);
 
     let errorCb = function errorCb(errorMsg) {
-      let error = onerror || debug;
-      error(errorMsg + " USIM field " + field);
+      if (DEBUG) {
+        debug(errorMsg + " USIM field " + field);
+      }
+      onerror(errorMsg);
     }.bind(this);
 
     ICCRecordHelper.findFreeRecordId(pbr[field].fileId, successCb, errorCb);
   },
 
   /**
    * Update IAP value.
    *
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -1868,16 +1868,114 @@ add_test(function test_find_free_icc_con
     do_check_eq(pbrIndex, 1);
     do_check_eq(recordId, 1);
   }
   contactHelper.findFreeICCContact(CARD_APPTYPE_USIM, "adn", successCb, errorCb);
 
   run_next_test();
 });
 
+/**
+ * Test error message returned in onerror for readICCContacts.
+ */
+add_test(function test_error_message_read_icc_contact () {
+  let worker = newUint8Worker();
+  let ril = worker.RIL;
+
+  function do_test(options, expectedErrorMsg) {
+    ril.sendChromeMessage = function (message) {
+      do_check_eq(message.errorMsg, expectedErrorMsg);
+    }
+    ril.readICCContacts(options);
+  }
+
+  // Error 1, didn't specify correct contactType.
+  do_test({}, CONTACT_ERR_REQUEST_NOT_SUPPORTED);
+
+  // Error 2, specifying a non-supported contactType.
+  ril.appType = CARD_APPTYPE_USIM;
+  do_test({contactType: "sdn"}, CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED);
+
+  // Error 3, suppose we update the supported PBR fields in USIM_PBR_FIELDS,
+  // but forget to add implemenetations for it.
+  USIM_PBR_FIELDS.push("pbc");
+  do_test({contactType: "adn"}, CONTACT_ERR_FIELD_NOT_SUPPORTED);
+
+  run_next_test();
+});
+
+/**
+ * Test error message returned in onerror for updateICCContact.
+ */
+add_test(function test_error_message_update_icc_contact() {
+  let worker = newUint8Worker();
+  let ril = worker.RIL;
+
+  const ICCID = "123456789";
+  ril.iccInfo.iccid = ICCID;
+
+  function do_test(options, expectedErrorMsg) {
+    ril.sendChromeMessage = function (message) {
+      do_check_eq(message.errorMsg, expectedErrorMsg);
+    }
+    ril.updateICCContact(options);
+  }
+
+  // Error 1, didn't specify correct contactType.
+  do_test({}, CONTACT_ERR_REQUEST_NOT_SUPPORTED);
+
+  // Error 2, specifying a correct contactType, but without providing 'contact'.
+  do_test({contactType: "adn"}, CONTACT_ERR_REQUEST_NOT_SUPPORTED);
+
+  // Error 3, specifying a non-supported contactType.
+  ril.appType = CARD_APPTYPE_USIM;
+  do_test({contactType: "sdn", contact: {}}, CONTACT_ERR_CONTACT_TYPE_NOT_SUPPORTED);
+
+  // Error 4, without supplying pin2.
+  do_test({contactType: "fdn", contact: {contactId: ICCID + "1"}}, GECKO_ERROR_SIM_PIN2);
+
+  // Error 5, No free record found in EF_ADN.
+  let record = worker.ICCRecordHelper;
+  record.readPBR = function (onsuccess, onerror) {
+    onsuccess([{adn: {fileId: 0x4f3a}}]);
+  };
+
+  let io = worker.ICCIOHelper;
+  io.loadLinearFixedEF = function (options) {
+    options.totalRecords = 1;
+    options.p1 = 1;
+    options.callback(options);
+  };
+
+  do_test({contactType: "adn", contact: {}}, CONTACT_ERR_NO_FREE_RECORD_FOUND);
+
+  // Error 6, ICC IO Error.
+  io.loadLinearFixedEF = function (options) {
+    ril[REQUEST_SIM_IO](0, {rilRequestError: ERROR_GENERIC_FAILURE});
+  };
+  do_test({contactType: "adn", contact: {contactId: ICCID + "1"}},
+          GECKO_ERROR_GENERIC_FAILURE);
+
+  // Error 7, suppose we update the supported PBR fields in USIM_PBR_FIELDS,
+  // but forget to add implemenetations for it.
+  USIM_PBR_FIELDS.push("pbc");
+  do_test({contactType: "adn", contact: {contactId: ICCID + "1"}},
+          CONTACT_ERR_FIELD_NOT_SUPPORTED);
+
+  // Error 8, EF_PBR doesn't exist.
+  record.readPBR = function (onsuccess, onerror) {
+    onsuccess([]);
+  };
+
+  do_test({contactType: "adn", contact: {contactId: ICCID + "1"}},
+          CONTACT_ERR_CANNOT_ACCESS_PHONEBOOK);
+
+  run_next_test();
+});
+
 add_test(function test_personalization_state() {
   let worker = newUint8Worker();
   let ril = worker.RIL;
 
   worker.ICCRecordHelper.readICCID = function fakeReadICCID() {};
 
   function testPersonalization(cardPersoState, geckoCardState) {
     let iccStatus = {
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -48,20 +48,20 @@ let emulator = (function() {
 }());
 
 /**
  * Telephony related helper functions.
  */
 (function() {
   function checkInitialState() {
     log("Verify initial state.");
-    ok(telephony, 'telephony');
-    is(telephony.active, null, 'telephony.active');
-    ok(telephony.calls, 'telephony.calls');
-    is(telephony.calls.length, 0, 'telephony.calls.length');
+    ok(telephony, "telephony");
+    is(telephony.active, null, "telephony.active");
+    ok(telephony.calls, "telephony.calls");
+    is(telephony.calls.length, 0, "telephony.calls.length");
   }
 
   /**
    * @return Promise
    */
   function clearCalls() {
     let deferred = Promise.defer();
 
@@ -99,53 +99,70 @@ function _startTest(permissions, test) {
     SpecialPowers.clearUserPref("dom.mozSettings.enabled");
     SpecialPowers.clearUserPref("dom.promise.enabled");
     for (let per of permissions) {
       SpecialPowers.removePermission(per, document);
     }
   }
 
   function setUp() {
-    log('== Test SetUp ==');
+    log("== Test SetUp ==");
     permissionSetUp();
     // Make sure that we get the telephony after adding permission.
     telephony = window.navigator.mozTelephony;
     ok(telephony);
     return clearCalls().then(checkInitialState);
   }
 
   // Extend finish() with tear down.
   finish = (function() {
     let originalFinish = finish;
 
     function tearDown() {
-      log('== Test TearDown ==');
+      log("== Test TearDown ==");
       emulator.waitFinish()
         .then(permissionTearDown)
         .then(function() {
           originalFinish.apply(this, arguments);
         });
     }
 
     return tearDown.bind(this);
   }());
 
   function mainTest() {
     setUp()
       .then(function onSuccess() {
-        log('== Test Start ==');
+        log("== Test Start ==");
         test();
       }, function onError(error) {
         SpecialPowers.Cu.reportError(error);
-        ok(false, 'SetUp error');
+        ok(false, "SetUp error");
       });
   }
 
   mainTest();
 }
 
 function startTest(test) {
-  _startTest(['telephony'], test);
+  _startTest(["telephony"], test);
 }
 
 function startTestWithPermissions(permissions, test) {
-  _startTest(permissions.concat('telephony'), test);
+  _startTest(permissions.concat("telephony"), test);
 }
+
+function startDSDSTest(test) {
+  let numRIL;
+  try {
+    numRIL = SpecialPowers.getIntPref("ril.numRadioInterfaces");
+  } catch (ex) {
+    numRIL = 1;  // Pref not set.
+  }
+
+  if (numRIL > 1) {
+    startTest(test);
+  } else {
+    log("Not a DSDS environment. Test is skipped.");
+    ok(true);  // We should run at least one test.
+    finish();
+  }
+}
--- a/dom/telephony/test/marionette/manifest.ini
+++ b/dom/telephony/test/marionette/manifest.ini
@@ -40,8 +40,9 @@ disabled = Bug 821966
 disabled = Bug 821927
 [test_multiple_hold.js]
 disabled = Bug 821958
 [test_outgoing_emergency_in_airplane_mode.js]
 [test_emergency_label.js]
 [test_conference.js]
 [test_dsds_default_service_id.js]
 [test_call_mute.js]
+[test_dsds_normal_call.js]
new file mode 100644
--- /dev/null
+++ b/dom/telephony/test/marionette/test_dsds_normal_call.js
@@ -0,0 +1,243 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+/**
+ * The functions are created to provide the string format of the emulator call
+ * list results.
+ *
+ * Usage:
+ *   let outInfo = OutCallStrPool("911");
+ *   outInfo.ringing == "outbound to 911        : ringing"
+ *   outInfo.active  == "outbound to 911        : active"
+ */
+function CallStrPool(prefix, number) {
+  let padding = "           : ";
+  let numberInfo = prefix + number + padding.substr(number.length);
+
+  let info = {};
+  let states = ["ringing", "incoming", "active", "held"];
+  for (let state of states) {
+    info[state] = numberInfo + state;
+  }
+
+  return info;
+}
+
+function OutCallStrPool(number) {
+  return CallStrPool("outbound to  ", number);
+}
+
+function InCallStrPool(number) {
+  return CallStrPool("inbound from ", number);
+}
+
+function checkEventCallState(event, call, state) {
+  is(call, event.call, "event.call");
+  is(call.state, state, "call state");
+}
+
+function checkTelephonyActiveAndCalls(active, calls) {
+  is(telephony.active, active, "telephony.active");
+  is(telephony.calls.length, calls.length, "telephony.calls");
+  for (let i = 0; i < calls.length; ++i) {
+    is(telephony.calls[i], calls[i]);
+  }
+}
+
+function checkEmulatorCallList(expectedCallList) {
+  let deferred = Promise.defer();
+
+  emulator.run("gsm list", function(result) {
+    log("Call list is now: " + result);
+    for (let i = 0; i < expectedCallList.length; ++i) {
+      is(result[i], expectedCallList[i], "emulator calllist");
+    }
+    is(result[expectedCallList.length], "OK", "emulator calllist");
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
+
+// Promise.
+function checkAll(active, calls, callList) {
+  checkTelephonyActiveAndCalls(active, calls);
+  return checkEmulatorCallList(callList);
+}
+
+function dial(number, serviceId) {
+  serviceId = typeof serviceId !== "undefined" ? serviceId : 0;
+  log("Make an outgoing call: " + number + ", serviceId: " + serviceId);
+
+  let deferred = Promise.defer();
+  let call = telephony.dial(number, serviceId);
+
+  ok(call);
+  is(call.number, number);
+  is(call.state, "dialing");
+
+  call.onalerting = function onalerting(event) {
+    call.onalerting = null;
+    log("Received 'onalerting' call event.");
+    is(call.serviceId, serviceId);
+    checkEventCallState(event, call, "alerting");
+    deferred.resolve(call);
+  };
+
+  return deferred.promise;
+}
+
+function remoteDial(number) {
+  log("Simulating an incoming call.");
+
+  let deferred = Promise.defer();
+
+  telephony.onincoming = function onincoming(event) {
+    log("Received 'incoming' call event.");
+    telephony.onimcoming = null;
+
+    let call = event.call;
+
+    ok(call);
+    is(call.number, number);
+    is(call.state, "incoming");
+
+    deferred.resolve(call);
+  };
+  emulator.run("gsm call " + number);
+
+  return deferred.promise;
+}
+
+function answer(call) {
+  log("Answering the incoming call.");
+
+  let deferred = Promise.defer();
+
+  call.onconnecting = function onconnectingIn(event) {
+    log("Received 'connecting' call event for incoming call.");
+    call.onconnecting = null;
+    checkEventCallState(event, call, "connecting");
+  };
+
+  call.onconnected = function onconnectedIn(event) {
+    log("Received 'connected' call event for incoming call.");
+    call.onconnected = null;
+    checkEventCallState(event, call, "connected");
+    ok(!call.onconnecting);
+    deferred.resolve(call);
+  };
+  call.answer();
+
+  return deferred.promise;
+}
+
+function remoteAnswer(call) {
+  log("Remote answering the call.");
+
+  let deferred = Promise.defer();
+
+  call.onconnected = function onconnected(event) {
+    log("Received 'connected' call event.");
+    call.onconnected = null;
+    checkEventCallState(event, call, "connected");
+    deferred.resolve(call);
+  };
+  emulator.run("gsm accept " + call.number);
+
+  return deferred.promise;
+}
+
+function remoteHangUp(call) {
+  log("Remote hanging up the call.");
+
+  let deferred = Promise.defer();
+
+  call.ondisconnected = function ondisconnected(event) {
+    log("Received 'disconnected' call event.");
+    call.ondisconnected = null;
+    checkEventCallState(event, call, "disconnected");
+    deferred.resolve(call);
+  };
+  emulator.run("gsm cancel " + call.number);
+
+  return deferred.promise;
+}
+
+function muxModem(id) {
+  let deferred = Promise.defer();
+
+  emulator.run("mux modem " + id, function() {
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
+
+function testOutgoingCallForServiceId(number, serviceId) {
+  let outCall;
+  let outInfo = OutCallStrPool(number);
+
+  return Promise.resolve()
+    .then(() => dial(number, serviceId))
+    .then(call => {
+      outCall = call;
+      is(outCall.serviceId, serviceId);
+    })
+    .then(() => checkAll(outCall, [outCall], [outInfo.ringing]))
+    .then(() => remoteAnswer(outCall))
+    .then(() => checkAll(outCall, [outCall], [outInfo.active]))
+    .then(() => remoteHangUp(outCall))
+    .then(() => checkAll(null, [], []));
+}
+
+function testIncomingCallForServiceId(number, serviceId) {
+  let inCall;
+  let inInfo = InCallStrPool(number);
+
+  return Promise.resolve()
+    .then(() => remoteDial(number))
+    .then(call => {
+      inCall = call;
+      is(inCall.serviceId, serviceId);
+    })
+    .then(() => checkAll(null, [inCall], [inInfo.incoming]))
+    .then(() => answer(inCall))
+    .then(() => checkAll(inCall, [inCall], [inInfo.active]))
+    .then(() => remoteHangUp(inCall))
+    .then(() => checkAll(null, [], []));
+}
+
+function testOutgoingCall() {
+  log("= testOutgoingCall =");
+
+  return Promise.resolve()
+    .then(() => muxModem(0))
+    .then(() => testOutgoingCallForServiceId("0912345000", 0))
+    .then(() => muxModem(1))
+    .then(() => testOutgoingCallForServiceId("0912345001", 1))
+    .then(() => muxModem(0));
+}
+
+function testIncomingCall() {
+  log("= testIncomingCall =");
+
+  return Promise.resolve()
+    .then(() => muxModem(0))
+    .then(() => testIncomingCallForServiceId("0912345000", 0))
+    .then(() => muxModem(1))
+    .then(() => testIncomingCallForServiceId("0912345001", 1))
+    .then(() => muxModem(0));
+}
+
+startDSDSTest(function() {
+  testOutgoingCall()
+    .then(testIncomingCall)
+    .then(null, () => {
+      ok(false, "promise rejects during test.");
+    })
+    .then(finish);
+});
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js
+++ b/dom/tests/mochitest/ajax/offline/offlineTests.js
@@ -18,25 +18,23 @@ OfflineCacheContents.prototype = {
 QueryInterface: function(iid) {
     if (!iid.equals(Ci.nsISupports) &&
         !iid.equals(Ci.nsICacheListener)) {
       throw Cr.NS_ERROR_NO_INTERFACE;
     }
     return this;
   },
 onCacheEntryAvailable: function(desc, accessGranted, status) {
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-
     if (!desc) {
       this.fetch(this.callback);
       return;
     }
 
     var stream = desc.QueryInterface(Ci.nsICacheEntryDescriptor).openInputStream(0);
-    var sstream = SpecialPowers.Cc["@mozilla.org/scriptableinputstream;1"]
+    var sstream = Cc["@mozilla.org/scriptableinputstream;1"]
                  .createInstance(SpecialPowers.Ci.nsIScriptableInputStream);
     sstream.init(stream);
     this.contents[desc.key] = sstream.read(sstream.available());
     sstream.close();
     desc.close();
     this.fetch(this.callback);
   },
 
@@ -93,48 +91,39 @@ setupChild: function()
  *
  * @return boolean Whether this window is the slave window
  *                 to actually run the test in.
  */
 setup: function()
 {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-  var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
   try {
-    this._allowedByDefault = prefBranch.getBoolPref("offline-apps.allow_by_default");
+    this._allowedByDefault = SpecialPowers.getBoolPref("offline-apps.allow_by_default");
   } catch (e) {}
 
   if (this._allowedByDefault) {
     this._masterWindow = window;
 
     return true;
   }
 
   if (!window.opener || !window.opener.OfflineTest ||
       !window.opener.OfflineTest._hasSlave) {
     // Offline applications must be toplevel windows and have the
     // offline-app permission.  Because we were loaded without the
     // offline-app permission and (probably) in an iframe, we need to
     // enable the pref and spawn a new window to perform the actual
     // tests.  It will use this window to report successes and
     // failures.
-    var pm = Cc["@mozilla.org/permissionmanager;1"]
-      .getService(Ci.nsIPermissionManager);
-    var uri = Cc["@mozilla.org/network/io-service;1"]
-      .getService(Ci.nsIIOService)
-      .newURI(window.location.href, null, null);
-    var principal = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                      .getService(Ci.nsIScriptSecurityManager)
-                      .getNoAppCodebasePrincipal(uri);
 
-    if (pm.testPermissionFromPrincipal(principal, "offline-app") != 0) {
+    if (SpecialPowers.testPermission("offline-app", Ci.nsIPermissionManager.ALLOW_ACTION, document)) {
       ok(false, "Previous test failed to clear offline-app permission!  Expect failures.");
     }
-    pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
+    SpecialPowers.addPermission("offline-app", Ci.nsIPermissionManager.ALLOW_ACTION, document);
 
     // Tests must run as toplevel windows.  Open a slave window to run
     // the test.
     this._hasSlave = true;
     window.open(window.location, "offlinetest");
 
     return false;
   }
@@ -150,28 +139,17 @@ teardownAndFinish: function()
 },
 
 teardown: function(callback)
 {
   // First wait for any pending scheduled updates to finish
   this.waitForUpdates(function(self) {
     // Remove the offline-app permission we gave ourselves.
 
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-
-    var pm = Cc["@mozilla.org/permissionmanager;1"]
-             .getService(Ci.nsIPermissionManager);
-    var uri = Cc["@mozilla.org/network/io-service;1"]
-              .getService(Ci.nsIIOService)
-              .newURI(window.location.href, null, null);
-    var principal = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                      .getService(Ci.nsIScriptSecurityManager)
-                      .getNoAppCodebasePrincipal(uri);
-
-    pm.removeFromPrincipal(principal, "offline-app");
+    SpecialPowers.removePermission("offline-app", window.document);
 
     // Clear all overrides on the server
     for (override in self._pathOverrides)
       self.deleteData(self._pathOverrides[override]);
     for (statedSJS in self._SJSsStated)
       self.setSJSState(self._SJSsStated[statedSJS], "");
 
     self.clear();
@@ -222,33 +200,31 @@ clear: function()
   var applicationCache = this.getActiveCache();
   if (applicationCache) {
     applicationCache.discard();
   }
 },
 
 waitForUpdates: function(callback)
 {
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-
   var self = this;
   var observer = {
     notified: false,
     observe: function(subject, topic, data) {
       if (subject) {
         subject.QueryInterface(SpecialPowers.Ci.nsIOfflineCacheUpdate);
         dump("Update of " + subject.manifestURI.spec + " finished\n");
       }
 
       SimpleTest.executeSoon(function() {
         if (observer.notified) {
           return;
         }
 
-        var updateservice = SpecialPowers.Cc["@mozilla.org/offlinecacheupdate-service;1"]
+        var updateservice = Cc["@mozilla.org/offlinecacheupdate-service;1"]
                             .getService(SpecialPowers.Ci.nsIOfflineCacheUpdateService);
         var updatesPending = updateservice.numUpdates;
         if (updatesPending == 0) {
           try {
             SpecialPowers.removeObserver(observer, "offline-cache-update-completed");
           } catch(ex) {}
           dump("All pending updates done\n");
           observer.notified = true;
@@ -275,17 +251,16 @@ failEvent: function(e)
 // The offline API as specified has no way to watch the load of a resource
 // added with applicationCache.mozAdd().
 waitForAdd: function(url, onFinished) {
   // Check every half second for ten seconds.
   var numChecks = 20;
 
   var waitForAddListener = {
     onCacheEntryAvailable: function(entry, access, status) {
-      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
       if (entry) {
         entry.close();
         onFinished();
         return;
       }
 
       if (--numChecks == 0) {
         onFinished();
@@ -365,17 +340,16 @@ getActiveSession: function()
                                     Ci.nsICache.STORE_OFFLINE,
                                     true);
 },
 
 priv: function(func)
 {
   var self = this;
   return function() {
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     func(arguments);
   }
 },
 
 checkCacheEntries: function(entries, callback)
 {
   var checkNextEntry = function() {
     if (entries.length == 0) {
@@ -386,17 +360,16 @@ checkCacheEntries: function(entries, cal
     }
   }
 
   checkNextEntry();
 },
 
 checkCache: function(url, expectEntry, callback)
 {
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   var cacheSession = this.getActiveSession();
   this._checkCache(cacheSession, url, expectEntry, callback);
 },
 
 _checkCache: function(cacheSession, url, expectEntry, callback)
 {
   if (!cacheSession) {
     if (expectEntry) {
@@ -405,17 +378,16 @@ checkCache: function(url, expectEntry, c
       this.ok(true, url + " should not exist in the offline cache (no session)");
     }
     if (callback) setTimeout(this.priv(callback), 0);
     return;
   }
 
   var _checkCacheListener = {
     onCacheEntryAvailable: function(entry, access, status) {
-      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
       if (entry) {
         if (expectEntry) {
           OfflineTest.ok(true, url + " should exist in the offline cache");
         } else {
           OfflineTest.ok(false, url + " should not exist in the offline cache");
         }
         entry.close();
       } else {
--- a/dom/tests/mochitest/ajax/offline/test_bug445544.html
+++ b/dom/tests/mochitest/ajax/offline/test_bug445544.html
@@ -1,16 +1,17 @@
 <html manifest="445544.cacheManifest">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=445544
 -->
 
 <head>
 <title>Test for Bug 445544</title>
 
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 
 <script type="text/javascript">
 
 var gTestWin;
 var gTimeoutId;
 
--- a/dom/tests/mochitest/ajax/offline/test_bug460353.html
+++ b/dom/tests/mochitest/ajax/offline/test_bug460353.html
@@ -13,40 +13,33 @@
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 
 <script class="testbody" type="text/javascript">
 
 var result = new Array();
 var expectedUpdates = 3; // 2 iframes and our self
 
-init();
+applicationCache.oncached = onUpdatePassed;
+applicationCache.onnoupdate = onUpdatePassed;
+
+SpecialPowers.pushPermissions([{'type': 'offline-app', 'allow': true, 'context': document}], init);
 
 function onUpdatePassed()
 {
   if (!(--expectedUpdates))
     SimpleTest.executeSoon(finish);
 }
 
 function init()
 {
-  var Cc = SpecialPowers.Cc;
-  var Ci = SpecialPowers.Ci;
-  var pm = Cc["@mozilla.org/permissionmanager;1"]
-    .getService(Ci.nsIPermissionManager);
-  var uri = Cc["@mozilla.org/network/io-service;1"]
-    .getService(Ci.nsIIOService)
-    .newURI(window.location.href, null, null);
-  var principal = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(uri);
-  pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
-
-  applicationCache.oncached = onUpdatePassed;
-  applicationCache.onnoupdate = onUpdatePassed;
+  var iframes = document.getElementsByTagName('iframe');
+  iframes[0].src = "460353_iframe_nomanifest.html";
+  iframes[1].src = "460353_iframe_ownmanifest.html";
+  iframes[2].src = "460353_iframe_samemanifest.html";
 }
 
 function frameOnLoad(frameid, status)
 {
   var obj = new Object();
   result[frameid] = obj;
 
   result[frameid].load = true;
@@ -74,28 +67,16 @@ function finish()
   SimpleTest.ok(result["diff"].updateOK || false, "Frame with different manifest cache update passed OK");
   SimpleTest.is(result["diff"].cacheStatus || -1, 1, "Frame with different manifest cache status was IDLE");
 
   SimpleTest.ok(result["noman"].load || false, "Frame with no manifest loads");
   SimpleTest.ok(result["noman"].update == undefined, "Frame with no manifest cache update didn't notify");
   SimpleTest.ok(result["noman"].updateOK == undefined, "Frame with no manifest cache update didn't pass");
   SimpleTest.is(result["noman"].cacheStatus || -1, -1, "Frame with no manifest cache status was undefined");
 
-  var Cc = SpecialPowers.Cc;
-  var Ci = SpecialPowers.Ci;
-  var pm = Cc["@mozilla.org/permissionmanager;1"]
-           .getService(Ci.nsIPermissionManager);
-  var uri = Cc["@mozilla.org/network/io-service;1"]
-            .getService(Ci.nsIIOService)
-            .newURI(window.location.href, null, null);
-  var principal = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(uri);
-  pm.removeFromPrincipal(principal, "offline-app");
-
   OfflineTest.waitForUpdates(function() {
     cleanCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest");
     cleanCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs");
 
     SimpleTest.finish();
   });
 }
 
@@ -107,13 +88,13 @@ function cleanCache(manifestURL)
     cache.discard();
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 
 <body>
-  <iframe src="460353_iframe_nomanifest.html"></iframe>
-  <iframe src="460353_iframe_ownmanifest.html"></iframe>
-  <iframe src="460353_iframe_samemanifest.html"></iframe>
+  <iframe></iframe>
+  <iframe></iframe>
+  <iframe></iframe>
 </body>
 </html>
--- a/dom/tests/mochitest/ajax/offline/test_bug744719-cancel.html
+++ b/dom/tests/mochitest/ajax/offline/test_bug744719-cancel.html
@@ -9,18 +9,20 @@
 <script type="text/javascript">
 
 /*
   Manifest refers a large number of resource to load.  The 10th item however is a reference to a non-existing
   resource that cancels the load.  This test checks we cancel all loads and don't leak any of the other resources
   after cancelation.
 */
 
-ok(applicationCache.mozItems.length == 0,
-   "applicationCache.mozItems should be available and empty before associating with a cache.");
+if (SpecialPowers.isMainProcess()) {
+  ok(applicationCache.mozItems.length == 0,
+     "applicationCache.mozItems should be available and empty before associating with a cache.");
+}
 
 function updateCanceled()
 {
   OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/744719-cancel.cacheManifest", false, null);
   OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false, null);
   OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false, null);
 
   OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/nonexistent744719?010", false, null);
--- a/dom/tests/mochitest/ajax/offline/test_bug744719.html
+++ b/dom/tests/mochitest/ajax/offline/test_bug744719.html
@@ -8,18 +8,20 @@
 
 <script type="text/javascript">
 
 /*
   Simply load a large number of resources and check all are properly cached.  This should cover all parts
   of the parallel loading code.
 */
 
-ok(applicationCache.mozItems.length == 0,
-   "applicationCache.mozItems should be available and empty before associating with a cache.");
+if (SpecialPowers.isMainProcess()) {
+  ok(applicationCache.mozItems.length == 0,
+     "applicationCache.mozItems should be available and empty before associating with a cache.");
+}
 
 function manifestUpdated()
 {
   OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/744719.cacheManifest", true);
   OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true);
   OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
 
   var URL = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/subresource744719.html?";
--- a/dom/tests/mochitest/ajax/offline/test_missingManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_missingManifest.html
@@ -4,44 +4,30 @@
 
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
 var gTestWin;
 
-var Cc = SpecialPowers.Cc;
-var Ci = SpecialPowers.Ci;
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPermissions([{'type': 'offline-app', 'allow': true, 'context': document}], startTest);
 
-// Enable the offline-app permission before loading the new window
-var pm = Cc["@mozilla.org/permissionmanager;1"]
-        .getService(Ci.nsIPermissionManager);
-var uri = Cc["@mozilla.org/network/io-service;1"]
-         .getService(Ci.nsIIOService)
-         .newURI(window.location.href, null, null);
-var principal = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                  .getService(Ci.nsIScriptSecurityManager)
-                  .getNoAppCodebasePrincipal(uri);
+function startTest() {
+  // now this will properly load the manifest.
+  gTestWin = window.open("missing.html");
+}
 
-pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
-
-// now this will properly load the manifest.
-gTestWin = window.open("missing.html");
-
-function finish()
-{
-  pm.removeFromPrincipal(principal, "offline-app");
-
+function finish() {
   gTestWin.close();
   SimpleTest.finish();
 }
 
-SimpleTest.waitForExplicitFinish();
-
 </script>
 
 </head>
 
 <body>
 
 </body>
 </html>
--- a/dom/tests/mochitest/ajax/offline/test_noManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_noManifest.html
@@ -17,17 +17,19 @@ function expectInvalidState(fn, desc) {
       gotInvalidState = true;
     }
   }
 
   ok(gotInvalidState, desc);
 }
 
 is(typeof(applicationCache), "object");
-is(applicationCache.mozLength, 0, "applicationCache.mozLength should be 0");
+if (SpecialPowers.isMainProcess()) {
+  is(applicationCache.mozLength, 0, "applicationCache.mozLength should be 0");
+}
 is(applicationCache.status, 0, "applicationCache.status should be 0 (UNCACHED)");
 
 expectInvalidState(function() { applicationCache.update(); },
                    "applicationCache.update should throw InvalidStateError");
 expectInvalidState(function() { applicationCache.swapCache(); },
                    "applicationCache.update should throw InvalidStateError");
 
 
--- a/dom/tests/mochitest/ajax/offline/test_obsolete.html
+++ b/dom/tests/mochitest/ajax/offline/test_obsolete.html
@@ -4,46 +4,33 @@
 
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
 var gTestWin;
 
-var Cc = SpecialPowers.Cc;
-var Ci = SpecialPowers.Ci;
+SpecialPowers.pushPermissions([{'type': 'offline-app', 'allow': true, 'context': document}], startTest);
 
-// Enable the offline-app permission before loading the new window
-var pm = Cc["@mozilla.org/permissionmanager;1"]
-        .getService(Ci.nsIPermissionManager);
-var uri = Cc["@mozilla.org/network/io-service;1"]
-         .getService(Ci.nsIIOService)
-         .newURI(window.location.href, null, null);
-var principal = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                  .getService(Ci.nsIScriptSecurityManager)
-                  .getNoAppCodebasePrincipal(uri);
-pm.addFromPrincipal(principal, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
-
-// Make the obsoleting.sjs return a valid manifest
-var req = new XMLHttpRequest();
-req.open("GET", "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/obsoletingManifest.sjs?state=manifestPresent");
-req.setRequestHeader("Content-Type", "text/cache-manifest");
-req.send("");
-req.onreadystatechange = function() {
-  if (req.readyState == 4) {
-    // now this will properly load the manifest.
-    gTestWin = window.open("obsolete.html");
+function startTest() {
+  // Make the obsoleting.sjs return a valid manifest
+  var req = new XMLHttpRequest();
+  req.open("GET", "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/obsoletingManifest.sjs?state=manifestPresent");
+  req.setRequestHeader("Content-Type", "text/cache-manifest");
+  req.send("");
+  req.onreadystatechange = function() {
+    if (req.readyState == 4) {
+      // now this will properly load the manifest.
+      gTestWin = window.open("obsolete.html");
+    }
   }
 }
 
-function finish()
-{
-  pm.removeFromPrincipal(principal, "offline-app");
-
+function finish() {
   gTestWin.close();
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 
--- a/dom/tests/mochitest/ajax/offline/updatingImplicit.html
+++ b/dom/tests/mochitest/ajax/offline/updatingImplicit.html
@@ -1,12 +1,13 @@
 <html xmlns="http://www.w3.org/1999/xhtml" manifest="http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs">
 <head>
 <title>Updating implicit</title>
 
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 
 <script type="text/javascript">
 
 function manifestUpdated()
 {
   // Assert that we are properly associated with the application
   // cache.
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1497,17 +1497,20 @@ XMLHttpRequest::Constructor(const Global
 {
   JSContext* cx = aGlobal.GetContext();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
   MOZ_ASSERT(workerPrivate);
 
   nsRefPtr<XMLHttpRequest> xhr = new XMLHttpRequest(workerPrivate);
 
   if (workerPrivate->XHRParamsAllowed()) {
-    xhr->mMozAnon = aParams.mMozAnon;
+    if (aParams.mMozSystem)
+      xhr->mMozAnon = true;
+    else
+      xhr->mMozAnon = aParams.mMozAnon;
     xhr->mMozSystem = aParams.mMozSystem;
   }
 
   return xhr.forget();
 }
 
 void
 XMLHttpRequest::ReleaseProxy(ReleaseType aType)
--- a/dom/workers/test/test_xhr_system.html
+++ b/dom/workers/test/test_xhr_system.html
@@ -21,34 +21,32 @@ function message(event) {
   else if(event.data.test == 'is')
     is(event.data.a, event.data.b, event.data.event);
   else if(event.data.test == 'finish') {
     run();
   }
 };
 
 function test1() {
-  // ...and once with privileges.
-  SpecialPowers.addPermission("systemXHR", true, document);
   var worker = new Worker("test_xhr_system.js");
   worker.onmessage = message;
   worker.postMessage(true);
 }
 
 var tests = [ test1 ];
 function run() {
   if (!tests.length) {
-    SpecialPowers.removePermission("systemXHR", document);
     SimpleTest.finish();
     return;
   }
 
   var func = tests.shift();
   func();
 }
 
 SimpleTest.waitForExplicitFinish();
-run();
+
+SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], run);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/test_xhr_system.js
+++ b/dom/workers/test/test_xhr_system.js
@@ -6,17 +6,17 @@ function is(a, b, msg) {
   postMessage({ event: msg, test: 'is', a: a, b: b });
 }
 
 self.onmessage = function onmessage(event) {
 
   // An XHR with system privileges will be able to do cross-site calls.
 
   const TEST_URL = "http://example.com/tests/content/base/test/test_XHR_system.html";
-  is(location.hostname, "mochi.test", "hostname");
+  is(location.hostname, "mochi.test", "hostname should be mochi.test");
 
   var xhr = new XMLHttpRequest({mozSystem: true});
   is(xhr.mozSystem, true, ".mozSystem == true");
   xhr.open("GET", TEST_URL);
   xhr.onload = function onload() {
     is(xhr.status, 200);
     ok(xhr.responseText != null);
     ok(xhr.responseText.length);
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -184,23 +184,32 @@ APZCTreeManager::UpdatePanZoomController
           aParent->SetLastChild(apzc);
         } else {
           mRootApzc = apzc;
         }
 
         // Let this apzc be the parent of other controllers when we recurse downwards
         aParent = apzc;
 
-        if (newApzc && apzc->IsRootForLayersId()) {
-          // If we just created a new apzc that is the root for its layers ID, then
-          // we need to update its zoom constraints which might have arrived before this
-          // was created
+        if (newApzc) {
           bool allowZoom;
           CSSToScreenScale minZoom, maxZoom;
-          if (state->mController->GetRootZoomConstraints(&allowZoom, &minZoom, &maxZoom)) {
+          if (apzc->IsRootForLayersId()) {
+            // If we just created a new apzc that is the root for its layers ID, then
+            // we need to update its zoom constraints which might have arrived before this
+            // was created
+            if (state->mController->GetRootZoomConstraints(&allowZoom, &minZoom, &maxZoom)) {
+              apzc->UpdateZoomConstraints(allowZoom, minZoom, maxZoom);
+            }
+          } else {
+            // For an apzc that is not the root for its layers ID, we give it the
+            // same zoom constraints as its parent. This ensures that if e.g.
+            // user-scalable=no was specified, none of the APZCs allow double-tap
+            // to zoom.
+            apzc->GetParent()->GetZoomConstraints(&allowZoom, &minZoom, &maxZoom);
             apzc->UpdateZoomConstraints(allowZoom, minZoom, maxZoom);
           }
         }
       }
     }
 
     container->SetAsyncPanZoomController(apzc);
   }
@@ -580,18 +589,38 @@ APZCTreeManager::ContentReceivedTouch(co
 
 void
 APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
                                        bool aAllowZoom,
                                        const CSSToScreenScale& aMinScale,
                                        const CSSToScreenScale& aMaxScale)
 {
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
-  if (apzc) {
-    apzc->UpdateZoomConstraints(aAllowZoom, aMinScale, aMaxScale);
+  // For a given layers id, non-root APZCs inherit the zoom constraints
+  // of their root.
+  if (apzc && apzc->IsRootForLayersId()) {
+    MonitorAutoLock lock(mTreeLock);
+    UpdateZoomConstraintsRecursively(apzc.get(), aAllowZoom, aMinScale, aMaxScale);
+  }
+}
+
+void
+APZCTreeManager::UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
+                                                  bool aAllowZoom,
+                                                  const CSSToScreenScale& aMinScale,
+                                                  const CSSToScreenScale& aMaxScale)
+{
+  mTreeLock.AssertCurrentThreadOwns();
+
+  aApzc->UpdateZoomConstraints(aAllowZoom, aMinScale, aMaxScale);
+  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
+    // We can have subtrees with their own layers id - leave those alone.
+    if (!child->IsRootForLayersId()) {
+      UpdateZoomConstraintsRecursively(child, aAllowZoom, aMinScale, aMaxScale);
+    }
   }
 }
 
 void
 APZCTreeManager::UpdateScrollOffset(const ScrollableLayerGuid& aGuid,
                                     const CSSPoint& aScrollOffset)
 {
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
--- a/gfx/layers/composite/APZCTreeManager.h
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -283,16 +283,20 @@ private:
                      const uint64_t& aLayersId,
                      nsTArray< nsRefPtr<AsyncPanZoomController> >* aOutRootApzcs);
   already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2);
   already_AddRefed<AsyncPanZoomController> RootAPZCForLayersId(AsyncPanZoomController* aApzc);
   already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent, ScreenPoint aPoint);
   nsEventStatus ProcessTouchEvent(const WidgetTouchEvent& touchEvent, ScrollableLayerGuid* aOutTargetGuid, WidgetTouchEvent* aOutEvent);
   nsEventStatus ProcessMouseEvent(const WidgetMouseEvent& mouseEvent, ScrollableLayerGuid* aOutTargetGuid, WidgetMouseEvent* aOutEvent);
   nsEventStatus ProcessEvent(const WidgetInputEvent& inputEvent, ScrollableLayerGuid* aOutTargetGuid, WidgetInputEvent* aOutEvent);
+  void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
+                                        bool aAllowZoom,
+                                        const CSSToScreenScale& aMinScale,
+                                        const CSSToScreenScale& aMaxScale);
 
   /**
    * Recursive helper function to build the APZC tree. The tree of APZC instances has
    * the same shape as the layer tree, but excludes all the layers that are not scrollable.
    * Note that this means APZCs corresponding to layers at different depths in the tree
    * may end up becoming siblings. It also means that the "root" APZC may have siblings.
    * This function walks the layer tree backwards through siblings and constructs the APZC
    * tree also as a last-child-prev-sibling tree because that simplifies the hit detection
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -1588,16 +1588,27 @@ void AsyncPanZoomController::UpdateZoomC
   if (gAsyncZoomDisabled) {
     return;
   }
   mAllowZoom = aAllowZoom;
   mMinZoom = (MIN_ZOOM > aMinZoom ? MIN_ZOOM : aMinZoom);
   mMaxZoom = (MAX_ZOOM > aMaxZoom ? aMaxZoom : MAX_ZOOM);
 }
 
+void
+AsyncPanZoomController::GetZoomConstraints(bool* aAllowZoom,
+                                           CSSToScreenScale* aMinZoom,
+                                           CSSToScreenScale* aMaxZoom)
+{
+  *aAllowZoom = mAllowZoom;
+  *aMinZoom = mMinZoom;
+  *aMaxZoom = mMaxZoom;
+}
+
+
 void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     controller->PostDelayedTask(aTask, aDelayMs);
   }
 }
 
 void AsyncPanZoomController::SendAsyncScrollEvent() {
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -128,18 +128,26 @@ public:
   void ContentReceivedTouch(bool aPreventDefault);
 
   /**
    * Updates any zoom constraints contained in the <meta name="viewport"> tag.
    * We try to obey everything it asks us elsewhere, but here we only handle
    * minimum-scale, maximum-scale, and user-scalable.
    */
   void UpdateZoomConstraints(bool aAllowZoom,
-                             const mozilla::CSSToScreenScale& aMinScale,
-                             const mozilla::CSSToScreenScale& aMaxScale);
+                             const CSSToScreenScale& aMinScale,
+                             const CSSToScreenScale& aMaxScale);
+
+  /**
+   * Return the zoom constraints last set for this APZC (in the constructor
+   * or in UpdateZoomConstraints()).
+   */
+  void GetZoomConstraints(bool* aAllowZoom,
+                          CSSToScreenScale* aMinScale,
+                          CSSToScreenScale* aMaxScale);
 
   /**
    * Schedules a runnable to run on the controller/UI thread at some time
    * in the future.
    */
   void PostDelayedTask(Task* aTask, int aDelayMs);
 
   // --------------------------------------------------------------------------
@@ -594,18 +602,18 @@ private:
 
   AxisX mX;
   AxisY mY;
 
   // Most up-to-date constraints on zooming. These should always be reasonable
   // values; for example, allowing a min zoom of 0.0 can cause very bad things
   // to happen.
   bool mAllowZoom;
-  mozilla::CSSToScreenScale mMinZoom;
-  mozilla::CSSToScreenScale mMaxZoom;
+  CSSToScreenScale mMinZoom;
+  CSSToScreenScale mMaxZoom;
 
   // The last time the compositor has sampled the content transform for this
   // frame.
   TimeStamp mLastSampleTime;
   // The last time a touch event came through on the UI thread.
   uint32_t mLastEventTime;
 
   // Start time of an animation. This is used for a zoom to animation to mark
--- a/hal/gonk/UeventPoller.cpp
+++ b/hal/gonk/UeventPoller.cpp
@@ -23,16 +23,17 @@
 #include <unistd.h>
 
 #include <arpa/inet.h>
 #include <linux/types.h>
 #include <linux/netlink.h>
 #include <netinet/in.h>
 #include <sys/socket.h>
 
+#include "nsDebug.h"
 #include "base/message_loop.h"
 #include "mozilla/FileUtils.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 #include "UeventPoller.h"
 
@@ -110,20 +111,36 @@ NetlinkPoller::OpenSocket()
   // set non-blocking
   if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1)
     return false;
 
   struct sockaddr_nl saddr;
   bzero(&saddr, sizeof(saddr));
   saddr.nl_family = AF_NETLINK;
   saddr.nl_groups = 1;
-  saddr.nl_pid = getpid();
+  saddr.nl_pid = gettid();
+
+  do {
+    if (bind(mSocket.get(), (struct sockaddr *)&saddr, sizeof(saddr)) == 0) {
+      break;
+    }
 
-  if (bind(mSocket.get(), (struct sockaddr *)&saddr, sizeof(saddr)) == -1)
-    return false;
+    if (errno != EADDRINUSE) {
+      return false;
+    }
+
+    if (saddr.nl_pid == 0) {
+      return false;
+    }
+
+    // Once there was any other place in the same process assigning saddr.nl_pid by
+    // gettid(), we can detect it and print warning message.
+    printf_stderr("The netlink socket address saddr.nl_pid=%u is in use. Let the kernel re-assign.\n", saddr.nl_pid);
+    saddr.nl_pid = 0;
+  } while (true);
 
   if (!mIOLoop->WatchFileDescriptor(mSocket.get(),
                                     true,
                                     MessageLoopForIO::WATCH_READ,
                                     &mReadWatcher,
                                     this)) {
       return false;
   }
--- a/testing/mochitest/manifest.webapp
+++ b/testing/mochitest/manifest.webapp
@@ -22,17 +22,16 @@
     "camera":{},
     "geolocation":{},
     "wifi-manage":{},
     "wifi":{},
     "desktop-notification":{},
     "idle":{},
     "network-events":{},
     "embed-apps":{},
-    "systemXHR":{},
     "audio-channel-content":{},
     "audio-channel-alarm":{}
   },
   "locales": {
     "en-US": {
       "name": "Mochitest",
       "description": "Mochitests"
     }
new file mode 100644
--- /dev/null
+++ b/widget/gonk/GonkPermission.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 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 <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPermissionController.h>
+#include <private/android_filesystem_config.h>
+#include "GonkPermission.h"
+
+#undef LOG
+#include <android/log.h>
+#define ALOGE(args...)  __android_log_print(ANDROID_LOG_ERROR, "gonkperm" , ## args)
+
+using namespace android;
+using namespace mozilla;
+
+bool
+GonkPermissionService::checkPermission(const String16& permission, int32_t pid,
+                                     int32_t uid)
+{
+  if (0 == uid)
+    return true;
+
+  // Camera/audio record permissions are only for apps with the
+  // "camera" permission.  These apps are also the only apps granted
+  // the AID_SDCARD_RW supplemental group (bug 785592)
+
+  if (uid < AID_APP) {
+    ALOGE("%s for pid=%d,uid=%d denied: not an app",
+      String8(permission).string(), pid, uid);
+    return false;
+  }
+
+  String8 perm8(permission);
+
+  if (perm8 != "android.permission.CAMERA" &&
+    perm8 != "android.permission.RECORD_AUDIO") {
+    ALOGE("%s for pid=%d,uid=%d denied: unsupported permission",
+      String8(permission).string(), pid, uid);
+    return false;
+  }
+
+  // Users granted the permission through a prompt dialog.
+  // Before permission managment of gUM is done, app cannot remember the
+  // permission.
+  PermissionGrant permGrant(perm8.string(), pid);
+  if (nsTArray<PermissionGrant>::NoIndex != mGrantArray.IndexOf(permGrant)) {
+    mGrantArray.RemoveElement(permGrant);
+    return true;
+  }
+
+  char filename[32];
+  snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
+  FILE *f = fopen(filename, "r");
+  if (!f) {
+    ALOGE("%s for pid=%d,uid=%d denied: unable to open %s",
+      String8(permission).string(), pid, uid, filename);
+    return false;
+  }
+
+  char line[80];
+  while (fgets(line, sizeof(line), f)) {
+    char *save;
+    char *name = strtok_r(line, "\t", &save);
+    if (!name)
+      continue;
+
+    if (strcmp(name, "Groups:"))
+      continue;
+    char *group;
+    while ((group = strtok_r(NULL, " \n", &save))) {
+      #define _STR(x) #x
+      #define STR(x) _STR(x)
+      if (!strcmp(group, STR(AID_SDCARD_RW))) {
+        fclose(f);
+        return true;
+      }
+    }
+    break;
+  }
+  fclose(f);
+
+  ALOGE("%s for pid=%d,uid=%d denied: missing group",
+    String8(permission).string(), pid, uid);
+  return false;
+}
+
+static GonkPermissionService* gGonkPermissionService = NULL;
+
+/* static */
+void
+GonkPermissionService::instantiate()
+{
+  defaultServiceManager()->addService(String16(getServiceName()),
+    GetInstance());
+}
+
+/* static */
+GonkPermissionService*
+GonkPermissionService::GetInstance()
+{
+  if (!gGonkPermissionService) {
+    gGonkPermissionService = new GonkPermissionService();
+  }
+  return gGonkPermissionService;
+}
+
+void
+GonkPermissionService::addGrantInfo(const char* permission, int32_t pid)
+{
+  mGrantArray.AppendElement(PermissionGrant(permission, pid));
+}
new file mode 100644
--- /dev/null
+++ b/widget/gonk/GonkPermission.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 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 GONKPERMISSION_H
+#define GONKPERMISSION_H
+
+#include <binder/BinderService.h>
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+class PermissionGrant
+{
+public:
+  PermissionGrant(const char* perm, int32_t p) : mPid(p)
+  {
+    mPermission.Assign(perm);
+  }
+
+  PermissionGrant(const nsACString& permission, int32_t pid) : mPid(pid),
+    mPermission(permission)
+  {
+  }
+
+  bool operator==(const PermissionGrant& other) const
+  {
+    return (mPid == other.pid() && mPermission.Equals(other.permission()));
+  }
+
+  int32_t pid() const
+  {
+    return mPid;
+  }
+
+  const nsACString& permission() const
+  {
+    return mPermission;
+  }
+
+private:
+  int32_t mPid;
+  nsCString mPermission;
+};
+
+class PermissionGrant;
+
+class GonkPermissionService :
+  public android::BinderService<GonkPermissionService>,
+  public android::BnPermissionController
+{
+public:
+  virtual ~GonkPermissionService() {}
+  static GonkPermissionService* GetInstance();
+  static const char *getServiceName() {
+    return "permission";
+  }
+
+  static void instantiate();
+
+  virtual android::status_t dump(int fd, const android::Vector<android::String16>& args) {
+    return android::NO_ERROR;
+  }
+  virtual bool checkPermission(const android::String16& permission, int32_t pid,
+      int32_t uid);
+
+  void addGrantInfo(const char* permission, int32_t pid);
+private:
+  GonkPermissionService(): android::BnPermissionController() {}
+  nsTArray<PermissionGrant> mGrantArray;
+};
+} // namespace mozilla
+#endif // GONKPERMISSION_H
--- a/widget/gonk/moz.build
+++ b/widget/gonk/moz.build
@@ -10,16 +10,17 @@
 #
 # 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.
 
 EXPORTS += [
+    'GonkPermission.h',
     'OrientationObserver.h',
 ]
 
 DIRS += ['libdisplay', 'nativewindow']
 
 # libui files
 SOURCES += ['libui/' + src for src in [
     'EventHub.cpp',
@@ -41,16 +42,17 @@ SOURCES += ['libui/' + src for src in [
     'VelocityTracker.cpp',
     'VirtualKeyMap.cpp',
 ]]
 
 SOURCES += [
     'Framebuffer.cpp',
     'GfxInfo.cpp',
     'GonkMemoryPressureMonitoring.cpp',
+    'GonkPermission.cpp',
     'HwcComposer2D.cpp',
     'HwcUtils.cpp',
     'nsAppShell.cpp',
     'nsIdleServiceGonk.cpp',
     'nsLookAndFeel.cpp',
     'nsWidgetFactory.cpp',
     'nsWindow.cpp',
     'OrientationObserver.cpp',
--- a/widget/gonk/nsAppShell.cpp
+++ b/widget/gonk/nsAppShell.cpp
@@ -24,16 +24,17 @@
 #include <sys/epoll.h>
 #include <sys/ioctl.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "base/basictypes.h"
+#include "GonkPermission.h"
 #include "nscore.h"
 #ifdef MOZ_OMX_DECODER
 #include "MediaResourceManagerService.h"
 #endif
 #include "mozilla/TouchEvents.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Hal.h"
 #include "mozilla/MouseEvents.h"
@@ -745,16 +746,17 @@ nsAppShell::Init()
 
     if (XRE_GetProcessType() == GeckoProcessType_Default) {
 #ifdef MOZ_OMX_DECODER
         android::MediaResourceManagerService::instantiate();
 #endif
 #if ANDROID_VERSION >= 18
         android::FakeSurfaceComposer::instantiate();
 #endif
+        GonkPermissionService::instantiate();
     }
 
     nsCOMPtr<nsIObserverService> obsServ = GetObserverService();
     if (obsServ) {
         obsServ->AddObserver(this, "browser-ui-startup-complete", false);
     }
 
 #ifdef MOZ_NUWA_PROCESS