Merge last PGO-green changeset of mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Mon, 01 Oct 2012 14:02:06 +0100
changeset 108836 d4355e045ea76e8bd1f473bdae0e52194e11a4f3
parent 108816 74c6fc2c4ad1993020627a800cf5681707cf5222 (current diff)
parent 108835 5b799e7132346d8cf4cde4c1738fdfb4f5edb2d6 (diff)
child 108837 9e8a91dfbc7e07bf80fcaba5db3aeb04ebee4989
child 108864 94526a90222e6c46c25723cda44d04625e8c4c2e
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
layout/reftests/bugs/solidblue.png
--- a/b2g/components/DirectoryProvider.js
+++ b/b2g/components/DirectoryProvider.js
@@ -5,18 +5,23 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
 const LOCAL_DIR = "/data/local";
 
+XPCOMUtils.defineLazyServiceGetter(Services, "env",
+                                   "@mozilla.org/process/environment;1",
+                                   "nsIEnvironment");
+
 function DirectoryProvider() {
 }
 
 DirectoryProvider.prototype = {
   classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
 
@@ -30,16 +35,46 @@ DirectoryProvider.prototype = {
       file.initWithPath(LOCAL_DIR);
       persistent.value = true;
       return file;
     } else if (prop == "coreAppsDir") {
       let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
       file.initWithPath("/system/b2g");
       persistent.value = true;
       return file;
+    } else if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
+      return this.getOSUpdateApplyToDir(persistent);
     }
 #endif
 
     return null;
+  },
+
+  getOSUpdateApplyToDir: function dp_getOSUpdateApplyToDir(persistent) {
+    // TODO add logic to check available storage space,
+    // and iterate through pref(s) to find alternative dirs if
+    // necessary.
+
+    let path = Services.env.get("EXTERNAL_STORAGE");
+    if (!path) {
+      path = LOCAL_PATH;
+    }
+
+    let dir = Cc["@mozilla.org/file/local;1"]
+                 .createInstance(Ci.nsILocalFile)
+    dir.initWithPath(path);
+
+    if (!dir.exists() && path != LOCAL_PATH) {
+      // Fallback to LOCAL_PATH if we didn't fallback earlier
+      dir.initWithPath(LOCAL_PATH);
+
+      if (!dir.exists()) {
+        throw Cr.NS_ERROR_FILE_NOT_FOUND;
+      }
+    }
+
+    dir.appendRelativePath("updates");
+    persistent.value = false;
+    return dir;
   }
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -163,47 +163,89 @@ UpdatePrompt.prototype = {
 
     switch (aDetail.result) {
       case "wait":
         // Wait for a fixed period of time, allowing the user to temporarily
         // postpone applying an update
         this._applyWaitTimer = this.createTimer(APPLY_WAIT_TIMEOUT);
         break;
       case "restart":
-        this.restartProcess();
+        this.finishUpdate();
         break;
     }
   },
 
   downloadUpdate: function UP_downloadUpdate(aUpdate) {
     Services.aus.downloadUpdate(aUpdate, true);
     Services.aus.addDownloadListener(this);
   },
 
+  finishUpdate: function UP_finishUpdate() {
+    if (!this._update.isOSUpdate) {
+      // Standard gecko+gaia updates will just need to restart the process
+      this.restartProcess();
+      return;
+    }
+
+    let osApplyToDir;
+    try {
+      this._update.QueryInterface(Ci.nsIWritablePropertyBag);
+      osApplyToDir = this._update.getProperty("osApplyToDir");
+    } catch (e) {}
+
+    if (!osApplyToDir) {
+      log("Error: Update has no osApplyToDir");
+      return;
+    }
+
+    let updateFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+    updateFile.initWithPath(osApplyToDir + "/update.zip");
+    if (!updateFile.exists()) {
+      log("Error: FOTA update not found at " + updateFile.path);
+      return;
+    }
+
+    this.finishOSUpdate(updateFile.path);
+  },
+
   restartProcess: function UP_restartProcess() {
     log("Update downloaded, restarting to apply it");
 
     let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
                        .getService(Ci.nsIAppStartup);
     // NB: on Gonk, we rely on the system process manager to restart
     // us.  Trying to restart here would conflict with the process
     // manager.  We should be using a runtime check to detect Gonk
     // instead of this gross ifdef, but the ifdef works for now.
     appStartup.quit(appStartup.eForceQuit
 #ifndef ANDROID
                     | appStartup.eRestart
 #endif
       );
   },
 
+  finishOSUpdate: function UP_finishOSUpdate(aOsUpdatePath) {
+    let recoveryService = Cc["@mozilla.org/recovery-service;1"]
+                            .getService(Ci.nsIRecoveryService);
+
+    log("Rebooting into recovery to apply FOTA update: " + aOsUpdatePath);
+
+    try {
+      recoveryService.installFotaUpdate(aOsUpdatePath);
+    } catch(e) {
+      log("Error: Couldn't reboot into recovery to apply FOTA update " +
+          aOsUpdatePath);
+    }
+  },
+
   notify: function UP_notify(aTimer) {
     if (aTimer == this._applyPromptTimer) {
       log("Timed out waiting for result, restarting");
       this._applyPromptTimer = null;
-      this.restartProcess();
+      this.finishUpdate();
     } else if (aTimer == this._applyWaitTimer) {
       this._applyWaitTimer = null;
       this.showUpdatePrompt();
     }
   },
 
   createTimer: function UP_createTimer(aTimeoutMs) {
     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -19,16 +19,17 @@
 #include "nsDOMEvent.h"
 #include "nsIDOMBluetoothDeviceAddressEvent.h"
 #include "nsIDOMBluetoothDeviceEvent.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Util.h"
 
 using namespace mozilla;
 
 USING_BLUETOOTH_NAMESPACE
 
 DOMCI_DATA(BluetoothAdapter, BluetoothAdapter)
@@ -811,17 +812,47 @@ BluetoothAdapter::Disconnect(uint16_t aP
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::SendFile(const nsAString& aDeviceAddress,
                            nsIDOMBlob* aBlob,
                            nsIDOMDOMRequest** aRequest)
 {
-  // Will implement in another patch
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    NS_WARNING("BluetoothService not available!");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
+  if (!rs) {
+    NS_WARNING("No DOMRequest Service!");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDOMDOMRequest> req;
+  nsresult rv = rs->CreateRequest(GetOwner(), getter_AddRefs(req));
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Can't create DOMRequest!");
+    return NS_ERROR_FAILURE;
+  }
+
+  BlobChild* actor =
+      mozilla::dom::ContentChild::GetSingleton()->GetOrCreateActorForBlob(aBlob);
+
+  if (!actor) {
+    NS_WARNING("Can't create actor");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<BluetoothVoidReplyRunnable> result = new BluetoothVoidReplyRunnable(req);
+  bs->SendFile(aDeviceAddress, nullptr, actor, result);
+  req.forget(aRequest);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::StopSendingFile(const nsAString& aDeviceAddress,
                                   nsIDOMDOMRequest** aRequest)
 {
   // Will implement in another patch
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -7,27 +7,95 @@
 #include "base/basictypes.h"
 #include "BluetoothOppManager.h"
 
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothServiceUuid.h"
 #include "ObexBase.h"
 
-#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/RefPtr.h"
+#include "nsIInputStream.h"
 
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla::ipc;
 
 static mozilla::RefPtr<BluetoothOppManager> sInstance;
+static nsCOMPtr<nsIInputStream> stream = nullptr;
+static uint64_t sSentFileSize = 0;
+
+class ReadFileTask : public nsRunnable
+{
+public:
+  ReadFileTask(nsIDOMBlob* aBlob) : mBlob(aBlob)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (NS_IsMainThread()) {
+      NS_WARNING("Can't read file from main thread");
+      return NS_ERROR_FAILURE;
+    }
+
+    uint64_t fileSize;
+    nsresult rv = mBlob->GetSize(&fileSize);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Can't get file size");
+      return NS_ERROR_FAILURE;;
+    }
+
+    if (stream == nullptr) {
+      rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Can't get internal stream of blob");
+        return NS_ERROR_FAILURE;
+      }
+    }
+
+    /*
+     * 255 is the Minimum OBEX Packet Length (See section 3.3.1.4,
+     * IrOBEX ver 1.2)
+     */
+    char buf[255];
+    uint32_t numRead;
+    int offset = 0;
+
+    // function inputstream->Read() only works on non-main thread
+    rv = stream->Read(buf, sizeof(buf), &numRead);
+    if (NS_FAILED(rv)) {
+      // Needs error handling here
+      return NS_ERROR_FAILURE;
+    }
+
+    if (numRead > 0) {
+      if (sSentFileSize + numRead >= fileSize) {
+        sInstance->SendPutRequest((uint8_t*)buf, numRead, true);
+      } else {
+        sInstance->SendPutRequest((uint8_t*)buf, numRead, false);
+      }
+
+      sSentFileSize += numRead;
+    }
+
+    return NS_OK;
+  };
+
+private:
+  nsCOMPtr<nsIDOMBlob> mBlob;
+};
 
 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
                                            , mConnectionId(1)
                                            , mLastCommand(0)
+                                           , mBlob(nullptr)
+                                           , mRemoteObexVersion(0)
+                                           , mRemoteConnectionFlags(0)
+                                           , mRemoteMaxPacketLength(0)
 {
 }
 
 BluetoothOppManager::~BluetoothOppManager()
 {
 }
 
 //static
@@ -74,61 +142,220 @@ BluetoothOppManager::Connect(const nsASt
 
 void
 BluetoothOppManager::Disconnect()
 {
   CloseSocket();
 }
 
 bool
-BluetoothOppManager::SendFile(BlobParent* aParent,
+BluetoothOppManager::SendFile(BlobParent* aActor,
                               BluetoothReplyRunnable* aRunnable)
 {
-  // will implement in another patch.
+  if (mBlob) {
+    // Means there's a sending process. Reply error.
+    return false;
+  }
+
+  /*
+   * Process of sending a file:
+   *  - Keep blob because OPP connection has not been established yet.
+   *  - Create an OPP connection by SendConnectRequest()
+   *  - After receiving the response, start to read file and send.
+   */
+  mBlob = aActor->GetBlob();
+
+  SendConnectRequest();
+
   return true;
 }
 
 bool
 BluetoothOppManager::StopSendingFile(BluetoothReplyRunnable* aRunnable)
 {
   // will implement in another patch.
   return true;
 }
 
 // Virtual function of class SocketConsumer
 void
 BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
 {
   uint8_t responseCode = aMessage->mData[0];
+  int packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
+  int receivedLength = aMessage->mSize;
 
   if (mLastCommand == ObexRequestCode::Connect) {
     if (responseCode == ObexResponseCode::Success) {
       mConnected = true;
+
+      // Keep remote information
+      mRemoteObexVersion = aMessage->mData[3];
+      mRemoteConnectionFlags = aMessage->mData[4];
+      mRemoteMaxPacketLength =
+        (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
+
+      if (mBlob) {
+        /*
+         * Before sending content, we have to send a header including
+         * information such as file name, file length and content type.
+         */
+        nsresult rv;
+        nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
+        nsString fileName;
+        if (file) {
+          rv = file->GetName(fileName);
+        }
+
+        if (!file || fileName.IsEmpty()) {
+          fileName.AssignLiteral("Unknown");
+        }
+
+        uint64_t fileSize;
+        rv = mBlob->GetSize(&fileSize);
+        if (NS_FAILED(rv)) {
+          NS_WARNING("Can't get file size");
+          return;
+        }
+
+        sSentFileSize = 0;
+        sInstance->SendPutHeaderRequest(fileName, fileSize);
+      }
     }
   } else if (mLastCommand == ObexRequestCode::Disconnect) {
-    if (responseCode == ObexResponseCode::Success) {
+    if (responseCode != ObexResponseCode::Success) {
+      // FIXME: Needs error handling here
+      NS_WARNING("[OPP] Disconnect failed");
+    } else {
       mConnected = false;
+      mBlob = nullptr;
+    }
+  } else if (mLastCommand == ObexRequestCode::Put) {
+    if (responseCode != ObexResponseCode::Continue) {
+      // FIXME: Needs error handling here
+      NS_WARNING("[OPP] Put failed");
+    } else {
+      nsCOMPtr<nsIThread> t;
+      NS_NewThread(getter_AddRefs(t));
+
+      nsRefPtr<ReadFileTask> task = new ReadFileTask(mBlob);
+
+      if (NS_FAILED(t->Dispatch(task, NS_DISPATCH_NORMAL))) {
+        NS_WARNING("Cannot dispatch ring task!");
+      }
+    }
+  } else if (mLastCommand == ObexRequestCode::PutFinal) {
+    if (responseCode != ObexResponseCode::Success) {
+      // FIXME: Needs error handling here
+      NS_WARNING("[OPP] PutFinal failed");
+    } else {
+      SendDisconnectRequest();
     }
   }
 }
 
 void
-BluetoothOppManager::SendConnectReqeust()
+BluetoothOppManager::SendConnectRequest()
 {
+  if (mConnected) return;
+
   // Section 3.3.1 "Connect", IrOBEX 1.2
   // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2][Headers:var]
   uint8_t req[255];
   int index = 7;
 
   req[3] = 0x10; // version=1.0
   req[4] = 0x00; // flag=0x00
   req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
   req[6] = BluetoothOppManager::MAX_PACKET_LENGTH;
 
-  index += AppendHeaderConnectionId(&req[index], mConnectionId++);
+  index += AppendHeaderConnectionId(&req[index], mConnectionId);
   SetObexPacketInfo(req, ObexRequestCode::Connect, index);
   mLastCommand = ObexRequestCode::Connect;
 
   UnixSocketRawData* s = new UnixSocketRawData(index);
   memcpy(s->mData, req, s->mSize);
   SendSocketData(s);
 }
 
+void
+BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName, int aFileSize)
+{
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+
+  const PRUnichar* fileNamePtr = aFileName.BeginReading();
+  uint32_t len = aFileName.Length();
+  uint8_t* fileName = new uint8_t[(len + 1) * 2];
+  for (int i = 0; i < len; i++) {
+    fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
+    fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
+  }
+
+  fileName[len * 2] = 0x00;
+  fileName[len * 2 + 1] = 0x00;
+
+  int index = 3;
+  index += AppendHeaderConnectionId(&req[index], mConnectionId);
+  index += AppendHeaderName(&req[index], (char*)fileName, (len + 1) * 2);
+  index += AppendHeaderLength(&req[index], aFileSize);
+
+  SetObexPacketInfo(req, ObexRequestCode::Put, index);
+  mLastCommand = ObexRequestCode::Put;
+
+  UnixSocketRawData* s = new UnixSocketRawData(index);
+  memcpy(s->mData, req, s->mSize);
+  SendSocketData(s);
+
+  delete [] fileName;
+  delete [] req;
+}
+
+void
+BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
+                                    int aFileBodyLength,
+                                    bool aFinal)
+{
+  int sentFileBodyLength = 0;
+  int index = 3;
+  int packetLeftSpace = mRemoteMaxPacketLength - index - 3;
+
+  if (!mConnected) return;
+  if (aFileBodyLength > packetLeftSpace) {
+    NS_WARNING("Not allowed such a small MaxPacketLength value");
+    return;
+  }
+
+  // IrOBEX 1.2 3.3.3
+  // [opcode:1][length:2][Headers:var]
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+
+  index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
+
+  if (aFinal) {
+    SetObexPacketInfo(req, ObexRequestCode::PutFinal, index);
+    mLastCommand = ObexRequestCode::PutFinal;
+  } else {
+    SetObexPacketInfo(req, ObexRequestCode::Put, index);
+    mLastCommand = ObexRequestCode::Put;
+  }
+
+  UnixSocketRawData* s = new UnixSocketRawData(index);
+  memcpy(s->mData, req, s->mSize);
+  SendSocketData(s);
+
+  delete [] req;
+}
+
+void
+BluetoothOppManager::SendDisconnectRequest()
+{
+  // IrOBEX 1.2 3.3.2
+  // [opcode:1][length:2][Headers:var]
+  uint8_t req[255];
+  int index = 3;
+
+  SetObexPacketInfo(req, ObexRequestCode::Disconnect, index);
+  mLastCommand = ObexRequestCode::Disconnect;
+
+  UnixSocketRawData* s = new UnixSocketRawData(index);
+  memcpy(s->mData, req, s->mSize);
+  SendSocketData(s);
+}
--- a/dom/bluetooth/BluetoothOppManager.h
+++ b/dom/bluetooth/BluetoothOppManager.h
@@ -3,22 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/ipc/UnixSocket.h"
+#include "nsIDOMFile.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
-class BlobParent;
 
 class BluetoothOppManager : public mozilla::ipc::UnixSocketConsumer
 {
 public:
   /*
    * Channel of reserved services are fixed values, please check
    * function add_reserved_service_records() in
    * external/bluetooth/bluez/src/adapter.c for more information.
@@ -45,20 +46,31 @@ public:
                BluetoothReplyRunnable* aRunnable);
   void Disconnect();
 
   bool SendFile(BlobParent* aBlob,
                 BluetoothReplyRunnable* aRunnable);
 
   bool StopSendingFile(BluetoothReplyRunnable* aRunnable);
 
+  // xxx For runnable use
+  void SendConnectRequest();
+  void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
+  void SendPutRequest(uint8_t* aFileBody, int aFileBodyLength,
+                      bool aFinal);
+  void SendDisconnectRequest();
+
 private:
   BluetoothOppManager();
-  void SendConnectReqeust();
 
   bool mConnected;
   int mConnectionId;
   int mLastCommand;
+  uint8_t mRemoteObexVersion;
+  uint8_t mRemoteConnectionFlags;
+  int mRemoteMaxPacketLength;
+
+  nsCOMPtr<nsIDOMBlob> mBlob;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothService.h
+++ b/dom/bluetooth/BluetoothService.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetootheventservice_h__
 #define mozilla_dom_bluetooth_bluetootheventservice_h__
 
 #include "BluetoothCommon.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsIObserver.h"
 #include "nsIThread.h"
 #include "nsTObserverArray.h"
 
 namespace mozilla {
 namespace ipc {
@@ -274,16 +275,22 @@ public:
   virtual bool
   ConnectObjectPush(const nsAString& aDeviceAddress,
                     const nsAString& aAdapterPath,
                     BluetoothReplyRunnable* aRunnable) = 0;
 
   virtual void
   DisconnectObjectPush(BluetoothReplyRunnable* aRunnable) = 0;
 
+  virtual bool
+  SendFile(const nsAString& aDeviceAddress,
+           BlobParent* aBlobParent,
+           BlobChild* aBlobChild,
+           BluetoothReplyRunnable* aRunnable) = 0;
+
   bool
   IsEnabled() const
   {
     return mEnabled;
   }
 
 protected:
   BluetoothService()
--- a/dom/bluetooth/gonk/BluetoothGonkService.cpp
+++ b/dom/bluetooth/gonk/BluetoothGonkService.cpp
@@ -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.
  */
 
+#include "base/basictypes.h"
 #include "BluetoothGonkService.h"
 #include "BluetoothDBusService.h"
 
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsThreadUtils.h"
 #include <dlfcn.h>
 
--- a/dom/bluetooth/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth/ipc/BluetoothParent.cpp
@@ -216,16 +216,18 @@ BluetoothParent::RecvPBluetoothRequestCo
     case Request::TConnectHeadsetRequest:
       return actor->DoRequest(aRequest.get_ConnectHeadsetRequest());
     case Request::TConnectObjectPushRequest:
       return actor->DoRequest(aRequest.get_ConnectObjectPushRequest());
     case Request::TDisconnectHeadsetRequest:
       return actor->DoRequest(aRequest.get_DisconnectHeadsetRequest());
     case Request::TDisconnectObjectPushRequest:
       return actor->DoRequest(aRequest.get_DisconnectObjectPushRequest());
+    case Request::TSendFileRequest:
+      return actor->DoRequest(aRequest.get_SendFileRequest());
     default:
       MOZ_NOT_REACHED("Unknown type!");
       return false;
   }
 
   MOZ_NOT_REACHED("Should never get here!");
   return false;
 }
@@ -533,8 +535,20 @@ BluetoothRequestParent::DoRequest(const 
 {
   MOZ_ASSERT(mService);
   MOZ_ASSERT(mRequestType == Request::TDenyAuthorizationRequest);
 
   mService->DisconnectObjectPush(mReplyRunnable.get());
 
   return true;
 }
+
+bool
+BluetoothRequestParent::DoRequest(const SendFileRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  MOZ_ASSERT(mRequestType == Request::TSendFileRequest);
+
+  return mService->SendFile(aRequest.devicePath(),
+                            (BlobParent*)aRequest.blobParent(),
+                            (BlobChild*)aRequest.blobChild(),
+                            mReplyRunnable.get());
+}
--- a/dom/bluetooth/ipc/BluetoothParent.h
+++ b/dom/bluetooth/ipc/BluetoothParent.h
@@ -172,13 +172,16 @@ protected:
   bool
   DoRequest(const ConnectObjectPushRequest& aRequest);
 
   bool
   DoRequest(const DisconnectHeadsetRequest& aRequest);
 
   bool
   DoRequest(const DisconnectObjectPushRequest& aRequest);
+
+  bool
+  DoRequest(const SendFileRequest& aRequest);
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif // mozilla_dom_bluetooth_ipc_bluetoothparent_h__
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.cpp
@@ -321,16 +321,28 @@ BluetoothServiceChildProcess::ConnectObj
 
 void
 BluetoothServiceChildProcess::DisconnectObjectPush(
   BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable, DisconnectObjectPushRequest());
 }
 
+bool
+BluetoothServiceChildProcess::SendFile(
+  const nsAString& aDeviceAddress,
+  BlobParent* aBlobParent,
+  BlobChild* aBlobChild,
+  BluetoothReplyRunnable* aRunnable)
+{
+  SendRequest(aRunnable,
+              SendFileRequest(nsString(aDeviceAddress), nullptr, aBlobChild));
+  return true;
+}
+
 nsresult
 BluetoothServiceChildProcess::HandleStartup()
 {
   // Don't need to do anything here for startup since our Create function takes
   // care of the actor machinery.
   return NS_OK;
 }
 
--- a/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/ipc/BluetoothServiceChildProcess.h
@@ -133,16 +133,22 @@ public:
   virtual bool
   ConnectObjectPush(const nsAString& aDeviceAddress,
                     const nsAString& aAdapterPath,
                     BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
   virtual void
   DisconnectObjectPush(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
 
+  virtual bool
+  SendFile(const nsAString& aDeviceAddress,
+           BlobParent* aBlobParent,
+           BlobChild* aBlobChild,
+           BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
+
 protected:
   BluetoothServiceChildProcess();
   virtual ~BluetoothServiceChildProcess();
 
   void
   NoteDeadActor();
 
   void
--- a/dom/bluetooth/ipc/PBluetooth.ipdl
+++ b/dom/bluetooth/ipc/PBluetooth.ipdl
@@ -1,14 +1,15 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+include protocol PBlob;
 include protocol PBluetoothRequest;
 include protocol PContent;
 
 include BluetoothTypes;
 
 include "mozilla/dom/bluetooth/ipc/BluetoothMessageUtils.h";
 
 using mozilla::dom::bluetooth::BluetoothObjectType;
@@ -110,16 +111,22 @@ struct ConnectObjectPushRequest
 };
 
 struct DisconnectHeadsetRequest
 {};
 
 struct DisconnectObjectPushRequest
 {};
 
+struct SendFileRequest
+{
+  nsString devicePath;
+  PBlob blob;
+};
+
 union Request
 {
   DefaultAdapterPathRequest;
   SetPropertyRequest;
   GetPropertyRequest;
   StartDiscoveryRequest;
   StopDiscoveryRequest;
   PairRequest;
@@ -130,16 +137,17 @@ union Request
   DenyPairingConfirmationRequest;
   ConfirmAuthorizationRequest;
   DenyAuthorizationRequest;
   DevicePropertiesRequest;
   ConnectHeadsetRequest;
   ConnectObjectPushRequest;
   DisconnectHeadsetRequest;
   DisconnectObjectPushRequest;
+  SendFileRequest;
 };
 
 protocol PBluetooth
 {
   manager PContent;
   manages PBluetoothRequest;
 
   /**
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -2404,8 +2404,23 @@ BluetoothDBusService::GetSocketViaServic
     NS_WARNING("Cannot dispatch firmware loading task!");
     return NS_ERROR_FAILURE;
   }
 
   runnable.forget();
   return NS_OK;
 }
 
+bool
+BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
+                               BlobParent* aBlobParent,
+                               BlobChild* aBlobChild,
+                               BluetoothReplyRunnable* aRunnable)
+{
+  // Currently we only support one device sending one file at a time,
+  // so we don't need aDeviceAddress here because the target device
+  // has been determined when calling 'Connect()'. Nevertheless, keep
+  // it for future use.
+  BluetoothOppManager* opp = BluetoothOppManager::Get();
+  opp->SendFile(aBlobParent, aRunnable);
+
+  return true;
+}
--- a/dom/bluetooth/linux/BluetoothDBusService.h
+++ b/dom/bluetooth/linux/BluetoothDBusService.h
@@ -123,16 +123,22 @@ public:
   virtual bool
   ConnectObjectPush(const nsAString& aDeviceAddress,
                     const nsAString& aAdapterPath,
                     BluetoothReplyRunnable* aRunnable);
 
   virtual void
   DisconnectObjectPush(BluetoothReplyRunnable* aRunnable);
 
+  virtual bool
+  SendFile(const nsAString& aDeviceAddress,
+           BlobParent* aBlobParent,
+           BlobChild* aBlobChild,
+           BluetoothReplyRunnable* aRunnable);
+
 private:
   nsresult SendGetPropertyMessage(const nsAString& aPath,
                                   const char* aInterface,
                                   void (*aCB)(DBusMessage *, void *),
                                   BluetoothReplyRunnable* aRunnable);
   nsresult SendDiscoveryMessage(const nsAString& aAdapterPath,
                                 const char* aMessageName,
                                 BluetoothReplyRunnable* aRunnable);
--- a/dom/camera/CameraCommon.h
+++ b/dom/camera/CameraCommon.h
@@ -91,39 +91,16 @@ enum {
   CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
   CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
   CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
   CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
   CAMERA_PARAM_SUPPORTED_ZOOM,
   CAMERA_PARAM_SUPPORTED_ZOOMRATIOS
 };
 
-class CameraErrorResult : public nsRunnable
-{
-public:
-  CameraErrorResult(nsICameraErrorCallback* onError, const nsString& aErrorMsg)
-    : mOnErrorCb(onError)
-    , mErrorMsg(aErrorMsg)
-  { }
-
-  NS_IMETHOD Run()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (mOnErrorCb) {
-      mOnErrorCb->HandleEvent(mErrorMsg);
-    }
-    return NS_OK;
-  }
-
-protected:
-  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
-  const nsString mErrorMsg;
-};
-
 #ifdef PR_LOGGING
 
 static inline void nsLogAddRefCamera(const char *file, uint32_t line, void* p, uint32_t count, const char *clazz, uint32_t size)
 {
   if (count == 1) {
     DOM_CAMERA_LOGR("++++++++++++++++++++++++++++++++++++++++");
   }
   DOM_CAMERA_LOGR("%s:%d : CAMREF-ADD(%s): this=%p, mRefCnt=%d\n", file, line, clazz, p, count);
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -163,16 +163,96 @@ CameraControlImpl::Get(JSContext* aCx, u
     }
   }
 
   *aValue = JS::ObjectValue(*array);
   return NS_OK;
 }
 
 nsresult
+CameraControlImpl::Set(nsICameraShutterCallback* aOnShutter)
+{
+  mOnShutterCb = aOnShutter;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraShutterCallback** aOnShutter)
+{
+  *aOnShutter = mOnShutterCb;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Set(nsICameraClosedCallback* aOnClosed)
+{
+  mOnClosedCb = aOnClosed;
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed)
+{
+  *aOnClosed = mOnClosedCb;
+  return NS_OK;
+}
+
+void
+CameraControlImpl::Shutdown()
+{
+  DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
+  mAutoFocusOnSuccessCb = nullptr;
+  mAutoFocusOnErrorCb = nullptr;
+  mTakePictureOnSuccessCb = nullptr;
+  mTakePictureOnErrorCb = nullptr;
+  mStartRecordingOnSuccessCb = nullptr;
+  mStartRecordingOnErrorCb = nullptr;
+  mOnShutterCb = nullptr;
+  mOnClosedCb = nullptr;
+}
+
+void
+CameraControlImpl::OnShutterInternal()
+{
+  DOM_CAMERA_LOGI("** SNAP **\n");
+  if (mOnShutterCb) {
+    mOnShutterCb->HandleEvent();
+  }
+}
+
+void
+CameraControlImpl::OnShutter()
+{
+  nsCOMPtr<nsIRunnable> onShutter = NS_NewRunnableMethod(this, &CameraControlImpl::OnShutterInternal);
+  nsresult rv = NS_DispatchToMainThread(onShutter);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGW("Failed to dispatch onShutter event to main thread (%d)\n", rv);
+  }
+}
+
+void
+CameraControlImpl::OnClosedInternal()
+{
+  DOM_CAMERA_LOGI("Camera hardware was closed\n");
+  if (mOnClosedCb) {
+    mOnClosedCb->HandleEvent();
+  }
+}
+
+void
+CameraControlImpl::OnClosed()
+{
+  nsCOMPtr<nsIRunnable> onClosed = NS_NewRunnableMethod(this, &CameraControlImpl::OnClosedInternal);
+  nsresult rv = NS_DispatchToMainThread(onClosed);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGW("Failed to dispatch onClosed event to main thread (%d)\n", rv);
+  }
+}
+
+nsresult
 CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
 {
   /**
    * The camera preview stream object is DOM-facing, and as such
    * must be a cycle-collection participant created on the main
    * thread.
    */
   MOZ_ASSERT(NS_IsMainThread());
@@ -240,14 +320,14 @@ CameraControlImpl::ReceiveFrame(void* aB
   return mDOMPreview->ReceiveFrame(aBuffer, aFormat, aBuilder);
 }
 
 NS_IMETHODIMP
 GetPreviewStreamResult::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (mOnSuccessCb) {
+  if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
     nsCOMPtr<nsIDOMMediaStream> stream = new DOMCameraPreview(mCameraControl, mWidth, mHeight, mFramesPerSecond);
     mOnSuccessCb->HandleEvent(stream);
   }
   return NS_OK;
 }
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -4,17 +4,17 @@
 
 #ifndef DOM_CAMERA_CAMERACONTROLIMPL_H
 #define DOM_CAMERA_CAMERACONTROLIMPL_H
 
 #include "nsCOMPtr.h"
 #include "nsDOMFile.h"
 #include "DictionaryHelpers.h"
 #include "nsIDOMDeviceStorage.h"
-#include "nsIDOMCameraManager.h"
+#include "DOMCameraManager.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 class GetPreviewStreamTask;
@@ -39,30 +39,32 @@ class CameraControlImpl : public ICamera
   friend class TakePictureTask;
   friend class StartRecordingTask;
   friend class StopRecordingTask;
   friend class SetParameterTask;
   friend class GetParameterTask;
   friend class GetPreviewStreamVideoModeTask;
 
 public:
-  CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread)
+  CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
     : mCameraId(aCameraId)
     , mCameraThread(aCameraThread)
+    , mWindowId(aWindowId)
     , mFileFormat()
     , mMaxMeteringAreas(0)
     , mMaxFocusAreas(0)
     , mDOMPreview(nullptr)
     , mAutoFocusOnSuccessCb(nullptr)
     , mAutoFocusOnErrorCb(nullptr)
     , mTakePictureOnSuccessCb(nullptr)
     , mTakePictureOnErrorCb(nullptr)
     , mStartRecordingOnSuccessCb(nullptr)
     , mStartRecordingOnErrorCb(nullptr)
     , mOnShutterCb(nullptr)
+    , mOnClosedCb(nullptr)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   nsresult GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StartPreview(DOMCameraPreview* aDOMPreview);
   void StopPreview();
   nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
@@ -72,16 +74,20 @@ public:
   nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
 
   nsresult Set(uint32_t aKey, const nsAString& aValue);
   nsresult Get(uint32_t aKey, nsAString& aValue);
   nsresult Set(uint32_t aKey, double aValue);
   nsresult Get(uint32_t aKey, double* aValue);
   nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
   nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
+  nsresult Set(nsICameraShutterCallback* aOnShutter);
+  nsresult Get(nsICameraShutterCallback** aOnShutter);
+  nsresult Set(nsICameraClosedCallback* aOnClosed);
+  nsresult Get(nsICameraClosedCallback** aOnClosed);
 
   nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
   {
     return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
   }
 
   nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
   {
@@ -92,18 +98,26 @@ public:
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
   virtual double GetParameterDouble(uint32_t aKey) = 0;
   virtual void GetParameter(uint32_t aKey, nsTArray<CameraRegion>& aRegions) = 0;
   virtual void SetParameter(const char* aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, double aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const nsTArray<CameraRegion>& aRegions) = 0;
   virtual nsresult PushParameters() = 0;
+  virtual void Shutdown();
 
   bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
+  void OnShutter();
+  void OnClosed();
+
+  uint64_t GetWindowId()
+  {
+    return mWindowId;
+  }
 
 protected:
   virtual ~CameraControlImpl()
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   virtual nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) = 0;
@@ -112,18 +126,22 @@ protected:
   virtual nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus) = 0;
   virtual nsresult TakePictureImpl(TakePictureTask* aTakePicture) = 0;
   virtual nsresult StartRecordingImpl(StartRecordingTask* aStartRecording) = 0;
   virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0;
   virtual nsresult PushParametersImpl() = 0;
   virtual nsresult PullParametersImpl() = 0;
   virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0;
 
+  void OnShutterInternal();
+  void OnClosedInternal();
+
   uint32_t            mCameraId;
   nsCOMPtr<nsIThread> mCameraThread;
+  uint64_t            mWindowId;
   nsString            mFileFormat;
   uint32_t            mMaxMeteringAreas;
   uint32_t            mMaxFocusAreas;
 
   /**
    * 'mDOMPreview' is a raw pointer to the object that will receive incoming
    * preview frames.  This is guaranteed to be valid, or null.
    *
@@ -135,32 +153,60 @@ protected:
 
   nsCOMPtr<nsICameraAutoFocusCallback>      mAutoFocusOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback>          mAutoFocusOnErrorCb;
   nsCOMPtr<nsICameraTakePictureCallback>    mTakePictureOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback>          mTakePictureOnErrorCb;
   nsCOMPtr<nsICameraStartRecordingCallback> mStartRecordingOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback>          mStartRecordingOnErrorCb;
   nsCOMPtr<nsICameraShutterCallback>        mOnShutterCb;
+  nsCOMPtr<nsICameraClosedCallback>         mOnClosedCb;
 
 private:
   CameraControlImpl(const CameraControlImpl&) MOZ_DELETE;
   CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE;
 };
 
+// Error result runnable
+class CameraErrorResult : public nsRunnable
+{
+public:
+  CameraErrorResult(nsICameraErrorCallback* onError, const nsString& aErrorMsg, uint64_t aWindowId)
+    : mOnErrorCb(onError)
+    , mErrorMsg(aErrorMsg)
+    , mWindowId(aWindowId)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnErrorCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnErrorCb->HandleEvent(mErrorMsg);
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+  const nsString mErrorMsg;
+  uint64_t mWindowId;
+};
+
 // Return the resulting preview stream to JS.  Runs on the main thread.
 class GetPreviewStreamResult : public nsRunnable
 {
 public:
-  GetPreviewStreamResult(CameraControlImpl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond, nsICameraPreviewStreamCallback* onSuccess)
+  GetPreviewStreamResult(CameraControlImpl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond, nsICameraPreviewStreamCallback* onSuccess, uint64_t aWindowId)
     : mCameraControl(aCameraControl)
     , mWidth(aWidth)
     , mHeight(aHeight)
     , mFramesPerSecond(aFramesPerSecond)
     , mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   virtual ~GetPreviewStreamResult()
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
@@ -169,16 +215,17 @@ public:
   NS_IMETHOD Run();
 
 protected:
   nsRefPtr<CameraControlImpl> mCameraControl;
   uint32_t mWidth;
   uint32_t mHeight;
   uint32_t mFramesPerSecond;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
+  uint64_t mWindowId;
 };
 
 // Get the desired preview stream.
 class GetPreviewStreamTask : public nsRunnable
 {
 public:
   GetPreviewStreamTask(CameraControlImpl* aCameraControl, CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
     : mSize(aSize)
@@ -194,52 +241,54 @@ public:
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   NS_IMETHOD Run()
   {
     nsresult rv = mCameraControl->GetPreviewStreamImpl(this);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
-      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
+      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
   CameraSize mSize;
   nsRefPtr<CameraControlImpl> mCameraControl;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 // Return the autofocus status to JS.  Runs on the main thread.
 class AutoFocusResult : public nsRunnable
 {
 public:
-  AutoFocusResult(bool aSuccess, nsICameraAutoFocusCallback* onSuccess)
+  AutoFocusResult(bool aSuccess, nsICameraAutoFocusCallback* onSuccess, uint64_t aWindowId)
     : mSuccess(aSuccess)
     , mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
   { }
 
   virtual ~AutoFocusResult() { }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (mOnSuccessCb) {
+    if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
       mOnSuccessCb->HandleEvent(mSuccess);
     }
     return NS_OK;
   }
 
 protected:
   bool mSuccess;
   nsCOMPtr<nsICameraAutoFocusCallback> mOnSuccessCb;
+  uint64_t mWindowId;
 };
 
 // Autofocus the camera.
 class AutoFocusTask : public nsRunnable
 {
 public:
   AutoFocusTask(CameraControlImpl* aCameraControl, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
     : mCameraControl(aCameraControl)
@@ -256,58 +305,60 @@ public:
 
   NS_IMETHOD Run()
   {
     DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->AutoFocusImpl(this);
     DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
-      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
+      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   nsCOMPtr<nsICameraAutoFocusCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 // Return the captured picture to JS.  Runs on the main thread.
 class TakePictureResult : public nsRunnable
 {
 public:
-  TakePictureResult(nsIDOMBlob* aImage, nsICameraTakePictureCallback* onSuccess)
+  TakePictureResult(nsIDOMBlob* aImage, nsICameraTakePictureCallback* onSuccess, uint64_t aWindowId)
     : mImage(aImage)
     , mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   virtual ~TakePictureResult()
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-    if (mOnSuccessCb) {
+    if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
       mOnSuccessCb->HandleEvent(mImage);
     }
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
     return NS_OK;
   }
 
 protected:
   nsCOMPtr<nsIDOMBlob> mImage;
   nsCOMPtr<nsICameraTakePictureCallback> mOnSuccessCb;
+  uint64_t mWindowId;
 };
 
 // Capture a still image with the camera.
 class TakePictureTask : public nsRunnable
 {
 public:
   TakePictureTask(CameraControlImpl* aCameraControl, CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
     : mCameraControl(aCameraControl)
@@ -328,17 +379,17 @@ public:
 
   NS_IMETHOD Run()
   {
     DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->TakePictureImpl(this);
     DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
-      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
+      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return rv;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   CameraSize mSize;
   int32_t mRotation;
@@ -347,34 +398,36 @@ public:
   nsCOMPtr<nsICameraTakePictureCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
 };
 
 // Return the captured video to JS.  Runs on the main thread.
 class StartRecordingResult : public nsRunnable
 {
 public:
-  StartRecordingResult(nsICameraStartRecordingCallback* onSuccess)
+  StartRecordingResult(nsICameraStartRecordingCallback* onSuccess, uint64_t aWindowId)
     : mOnSuccessCb(onSuccess)
+    , mWindowId(aWindowId)
   { }
 
   virtual ~StartRecordingResult() { }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (mOnSuccessCb) {
+    if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
       mOnSuccessCb->HandleEvent();
     }
     return NS_OK;
   }
 
 protected:
   nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
+  uint64_t mWindowId;
 };
 
 // Start video recording.
 class StartRecordingTask : public nsRunnable
 {
 public:
   StartRecordingTask(CameraControlImpl* aCameraControl, nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
     : mCameraControl(aCameraControl)
@@ -392,22 +445,18 @@ public:
   }
 
   NS_IMETHOD Run()
   {
     DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
     nsresult rv = mCameraControl->StartRecordingImpl(this);
     DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv);
 
-    if (NS_SUCCEEDED(rv)) {
-      if (mOnSuccessCb) {
-        rv = NS_DispatchToMainThread(new StartRecordingResult(mOnSuccessCb));
-      }
-    } else if (mOnErrorCb) {
-      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
+    if (NS_FAILED(rv) && mOnErrorCb) {
+      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
     }
     return rv;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   nsCOMPtr<nsIDOMDeviceStorage> mStorageArea;
   nsString mFilename;
   nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
@@ -543,17 +592,17 @@ public:
 
   NS_IMETHOD Run()
   {
     DOM_CAMERA_LOGI("%s:%d -- BEFORE IMPL\n", __func__, __LINE__);
     nsresult rv = mCameraControl->GetPreviewStreamVideoModeImpl(this);
     DOM_CAMERA_LOGI("%s:%d -- AFTER IMPL : rv = %d\n", __func__, __LINE__, rv);
 
     if (NS_FAILED(rv) && mOnErrorCb) {
-      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
+      rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     return NS_OK;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   CameraRecordingOptions mOptions;
   nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -204,24 +204,34 @@ nsDOMCameraControl::GetExposureCompensat
 {
   return mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, aExposureCompensation);
 }
 
 /* attribute nsICameraShutterCallback onShutter; */
 NS_IMETHODIMP
 nsDOMCameraControl::GetOnShutter(nsICameraShutterCallback** aOnShutter)
 {
-  // TODO: see bug 779138.
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return mCameraControl->Get(aOnShutter);
 }
 NS_IMETHODIMP
 nsDOMCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter)
 {
-  // TODO: see bug 779138.
-  return NS_ERROR_NOT_IMPLEMENTED;
+  return mCameraControl->Set(aOnShutter);
+}
+
+/* attribute nsICameraClosedCallback onClosed; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetOnClosed(nsICameraClosedCallback** aOnClosed)
+{
+  return mCameraControl->Get(aOnClosed);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetOnClosed(nsICameraClosedCallback* aOnClosed)
+{
+  return mCameraControl->Set(aOnClosed);
 }
 
 /* [implicit_jscontext] void startRecording (in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
 nsDOMCameraControl::StartRecording(nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
   NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
 
@@ -324,38 +334,41 @@ nsDOMCameraControl::GetPreviewStreamVide
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, onError);
 }
 
 class GetCameraResult : public nsRunnable
 {
 public:
-  GetCameraResult(nsDOMCameraControl* aDOMCameraControl, nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+  GetCameraResult(nsDOMCameraControl* aDOMCameraControl, nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
     : mDOMCameraControl(aDOMCameraControl)
     , mResult(aResult)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
+    , mWindowId(aWindowId)
   { }
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    DOM_CAMERA_LOGT("%s : this=%p -- BEFORE CALLBACK\n", __func__, this);
-    if (NS_FAILED(mResult)) {
-      if (mOnErrorCb) {
-        mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+    if (nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      DOM_CAMERA_LOGT("%s : this=%p -- BEFORE CALLBACK\n", __func__, this);
+      if (NS_FAILED(mResult)) {
+        if (mOnErrorCb) {
+          mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+        }
+      } else {
+        if (mOnSuccessCb) {
+          mOnSuccessCb->HandleEvent(mDOMCameraControl);
+        }
       }
-    } else {
-      if (mOnSuccessCb) {
-        mOnSuccessCb->HandleEvent(mDOMCameraControl);
-      }
+      DOM_CAMERA_LOGT("%s : this=%p -- AFTER CALLBACK\n", __func__, this);
     }
-    DOM_CAMERA_LOGT("%s : this=%p -- AFTER CALLBACK\n", __func__, this);
 
     /**
      * Finally, release the extra reference to the DOM-facing camera control.
      * See the nsDOMCameraControl constructor for the corresponding call to
      * NS_ADDREF_THIS().
      */
     NS_RELEASE(mDOMCameraControl);
     return NS_OK;
@@ -365,16 +378,24 @@ protected:
   /**
    * 'mDOMCameraControl' is a raw pointer to a previously ADDREF()ed object,
    * which is released in Run().
    */
   nsDOMCameraControl* mDOMCameraControl;
   nsresult mResult;
   nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+  uint64_t mWindowId;
 };
 
 nsresult
-nsDOMCameraControl::Result(nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+nsDOMCameraControl::Result(nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
 {
-  nsCOMPtr<GetCameraResult> getCameraResult = new GetCameraResult(this, aResult, onSuccess, onError);
+  nsCOMPtr<GetCameraResult> getCameraResult = new GetCameraResult(this, aResult, onSuccess, onError, aWindowId);
   return NS_DispatchToMainThread(getCameraResult);
 }
+
+void
+nsDOMCameraControl::Shutdown()
+{
+  DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__);
+  mCameraControl->Shutdown();
+}
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -22,18 +22,20 @@ using namespace dom;
 // Main camera control.
 class nsDOMCameraControl : public nsICameraControl
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsDOMCameraControl)
   NS_DECL_NSICAMERACONTROL
 
-  nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
-  nsresult Result(nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
+  nsresult Result(nsresult aResult, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
+
+  void Shutdown();
 
 protected:
   virtual ~nsDOMCameraControl();
 
 private:
   nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
   nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
 
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -1,65 +1,79 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDebug.h"
-#include "nsIDocument.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/Services.h"
+#include "nsObserverService.h"
 #include "nsIPermissionManager.h"
 #include "DOMCameraControl.h"
 #include "DOMCameraManager.h"
 #include "nsDOMClassInfo.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
 using namespace mozilla;
+using namespace dom;
 
 DOMCI_DATA(CameraManager, nsIDOMCameraManager)
 
-NS_INTERFACE_MAP_BEGIN(nsDOMCameraManager)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMCameraManager)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMCameraManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCameraThread)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMCameraManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCameraThread)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCameraManager)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraManager)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_ADDREF(nsDOMCameraManager)
-NS_IMPL_RELEASE(nsDOMCameraManager)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraManager)
 
 /**
  * Global camera logging object
  *
  * Set the NSPR_LOG_MODULES environment variable to enable logging
  * in a debug build, e.g. NSPR_LOG_MODULES=Camera:5
  */
 PRLogModuleInfo* gCameraLog = PR_LOG_DEFINE("Camera");
 
 /**
  * nsDOMCameraManager::GetListOfCameras
  * is implementation-specific, and can be found in (e.g.)
  * GonkCameraManager.cpp and FallbackCameraManager.cpp.
  */
 
+WindowTable nsDOMCameraManager::sActiveWindows;
+bool nsDOMCameraManager::sActiveWindowsInitialized = false;
+
 nsDOMCameraManager::nsDOMCameraManager(uint64_t aWindowId)
   : mWindowId(aWindowId)
+  , mCameraThread(nullptr)
 {
   /* member initializers and constructor code */
   DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
 }
 
 nsDOMCameraManager::~nsDOMCameraManager()
 {
   /* destructor code */
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-}
-
-void
-nsDOMCameraManager::OnNavigation(uint64_t aWindowId)
-{
-  // TODO: see bug 779145.
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  obs->RemoveObserver(this, "xpcom-shutdown");
 }
 
 // static creator
 already_AddRefed<nsDOMCameraManager>
 nsDOMCameraManager::CheckPermissionAndCreateInstance(nsPIDOMWindow* aWindow)
 {
   nsCOMPtr<nsIPermissionManager> permMgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
@@ -67,18 +81,28 @@ nsDOMCameraManager::CheckPermissionAndCr
 
   uint32_t permission = nsIPermissionManager::DENY_ACTION;
   permMgr->TestPermissionFromWindow(aWindow, "camera", &permission);
   if (permission != nsIPermissionManager::ALLOW_ACTION) {
     NS_WARNING("No permission to access camera");
     return nullptr;
   }
 
+  // Initialize the shared active window tracker
+  if (!sActiveWindowsInitialized) {
+    sActiveWindows.Init();
+    sActiveWindowsInitialized = true;
+  }
+
   nsRefPtr<nsDOMCameraManager> cameraManager =
     new nsDOMCameraManager(aWindow->WindowID());
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  obs->AddObserver(cameraManager, "xpcom-shutdown", true);
+
   return cameraManager.forget();
 }
 
 /* [implicit_jscontext] void getCamera ([optional] in jsval aOptions, in nsICameraGetCameraCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
 nsDOMCameraManager::GetCamera(const JS::Value& aOptions, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
   NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
@@ -97,12 +121,89 @@ nsDOMCameraManager::GetCamera(const JS::
   if (!mCameraThread) {
     rv = NS_NewThread(getter_AddRefs(mCameraThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
 
   // Creating this object will trigger the onSuccess handler
-  nsCOMPtr<nsICameraControl> cameraControl = new nsDOMCameraControl(cameraId, mCameraThread, onSuccess, onError);
+  nsCOMPtr<nsDOMCameraControl> cameraControl = new nsDOMCameraControl(cameraId, mCameraThread, onSuccess, onError, mWindowId);
 
+  Register(cameraControl);
   return NS_OK;
 }
+
+void
+nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
+{
+  DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Put the camera control into the hash table
+  CameraControls* controls = sActiveWindows.Get(mWindowId);
+  if (!controls) {
+    controls = new CameraControls;
+    sActiveWindows.Put(mWindowId, controls);
+  }
+  controls->AppendElement(aDOMCameraControl);
+}
+
+void
+nsDOMCameraManager::Shutdown(uint64_t aWindowId)
+{
+  DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  CameraControls* controls = sActiveWindows.Get(aWindowId);
+  if (!controls) {
+    return;
+  }
+
+  PRUint32 length = controls->Length();
+  for (PRUint32 i = 0; i < length; i++) {
+    nsRefPtr<nsDOMCameraControl> cameraControl = controls->ElementAt(i);
+    cameraControl->Shutdown();
+  }
+  controls->Clear();
+
+  sActiveWindows.Remove(aWindowId);
+}
+
+void
+nsDOMCameraManager::XpComShutdown()
+{
+  DOM_CAMERA_LOGI(">>> XPCOM Shutdown\n");
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  obs->RemoveObserver(this, "xpcom-shutdown");
+
+  sActiveWindows.Clear();
+}
+
+nsresult
+nsDOMCameraManager::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
+{
+  if (strcmp(aTopic, "xpcom-shutdown") == 0) {
+    XpComShutdown();
+  }
+  return NS_OK;
+}
+
+void
+nsDOMCameraManager::OnNavigation(uint64_t aWindowId)
+{
+  DOM_CAMERA_LOGI(">>> OnNavigation event\n");
+  Shutdown(aWindowId);
+}
+
+bool
+nsDOMCameraManager::IsWindowStillActive(uint64_t aWindowId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sActiveWindowsInitialized) {
+    return false;
+  }
+
+  return !!sActiveWindows.Get(aWindowId);
+}
--- a/dom/camera/DOMCameraManager.h
+++ b/dom/camera/DOMCameraManager.h
@@ -5,43 +5,72 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_DOMCAMERAMANAGER_H
 #define DOM_CAMERA_DOMCAMERAMANAGER_H
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsIThread.h"
+#include "nsIObserver.h"
 #include "nsThreadUtils.h"
+#include "nsHashKeys.h"
+#include "nsWeakReference.h"
+#include "nsClassHashtable.h"
 #include "nsIDOMCameraManager.h"
+#include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 
 class nsPIDOMWindow;
 
-class nsDOMCameraManager MOZ_FINAL : public nsIDOMCameraManager
+namespace mozilla {
+class nsDOMCameraControl;
+}
+
+typedef nsTArray<nsRefPtr<mozilla::nsDOMCameraControl> > CameraControls;
+typedef nsClassHashtable<nsUint64HashKey, CameraControls> WindowTable;
+
+class nsDOMCameraManager MOZ_FINAL
+  : public nsIDOMCameraManager
+  , public nsIObserver
+  , public nsSupportsWeakReference
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMCameraManager, nsIObserver)
   NS_DECL_NSIDOMCAMERAMANAGER
+  NS_DECL_NSIOBSERVER
 
   static already_AddRefed<nsDOMCameraManager>
     CheckPermissionAndCreateInstance(nsPIDOMWindow* aWindow);
+  static bool IsWindowStillActive(uint64_t aWindowId);
 
+  void Register(mozilla::nsDOMCameraControl* aDOMCameraControl);
   void OnNavigation(uint64_t aWindowId);
 
+protected:
+  void XpComShutdown();
+  void Shutdown(uint64_t aWindowId);
+  ~nsDOMCameraManager();
+
 private:
   nsDOMCameraManager();
   nsDOMCameraManager(uint64_t aWindowId);
   nsDOMCameraManager(const nsDOMCameraManager&) MOZ_DELETE;
   nsDOMCameraManager& operator=(const nsDOMCameraManager&) MOZ_DELETE;
-  ~nsDOMCameraManager();
 
 protected:
   uint64_t mWindowId;
   nsCOMPtr<nsIThread> mCameraThread;
+  /**
+   * 'mActiveWindows' is only ever accessed while in the main thread,
+   * so it is not otherwise protected.
+   */
+  static WindowTable sActiveWindows;
+  static bool sActiveWindowsInitialized;
 };
 
 class GetCameraTask : public nsRunnable
 {
 public:
   GetCameraTask(uint32_t aCameraId, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsIThread* aCameraThread)
     : mCameraId(aCameraId)
     , mOnSuccessCb(onSuccess)
--- a/dom/camera/FallbackCameraControl.cpp
+++ b/dom/camera/FallbackCameraControl.cpp
@@ -9,17 +9,17 @@ using namespace mozilla;
 
 /**
  * Fallback camera control subclass.  Can be used as a template for the
  * definition of new camera support classes.
  */
 class nsFallbackCameraControl : public CameraControlImpl
 {
 public:
-  nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
   void SetParameter(uint32_t aKey, double aValue);
@@ -47,28 +47,28 @@ private:
 /**
  * Stub implementation of the DOM-facing camera control constructor.
  *
  * This should never get called--it exists to keep the linker happy; if
  * implemented, it should construct (e.g.) nsFallbackCameraControl and
  * store a reference in the 'mCameraControl' member (which is why it is
  * defined here).
  */
-nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
 {
 }
 
 /**
  * Stub implemetations of the fallback camera control.
  *
  * None of these should ever get called--they exist to keep the linker happy,
  * and may be used as templates for new camera support classes.
  */
-nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
-  : CameraControlImpl(aCameraId, aCameraThread)
+nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+  : CameraControlImpl(aCameraId, aCameraThread, aWindowId)
 {
 }
 
 nsFallbackCameraControl::~nsFallbackCameraControl()
 {
 }
 
 const char*
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -102,87 +102,89 @@ static const char* getKeyText(uint32_t a
     case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
       return CameraParameters::KEY_ZOOM_RATIOS;
     default:
       return nullptr;
   }
 }
 
 // nsDOMCameraControl implementation-specific constructor
-nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
   : mDOMCapabilities(nullptr)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 
   /**
    * nsDOMCameraControl is a cycle-collection participant, which means it is
    * not threadsafe--so we need to bump up its reference count here to make
    * sure that it exists long enough to be initialized.
    *
    * Once it is initialized, the GetCameraResult main-thread runnable will
    * decrement it again to make sure it can be cleaned up.
    *
    * nsGonkCameraControl MUST NOT hold a strong reference to this
    * nsDOMCameraControl or memory will leak!
    */
   NS_ADDREF_THIS();
-  mCameraControl = new nsGonkCameraControl(aCameraId, aCameraThread, this, onSuccess, onError);
+  mCameraControl = new nsGonkCameraControl(aCameraId, aCameraThread, this, onSuccess, onError, aWindowId);
 }
 
 // Gonk-specific CameraControl implementation.
 
 // Initialize nsGonkCameraControl instance--runs on camera thread.
 class InitGonkCameraControl : public nsRunnable
 {
 public:
-  InitGonkCameraControl(nsGonkCameraControl* aCameraControl, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
+  InitGonkCameraControl(nsGonkCameraControl* aCameraControl, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
     : mCameraControl(aCameraControl)
     , mDOMCameraControl(aDOMCameraControl)
     , mOnSuccessCb(onSuccess)
     , mOnErrorCb(onError)
+    , mWindowId(aWindowId)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   ~InitGonkCameraControl()
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   NS_IMETHOD Run()
   {
     nsresult rv = mCameraControl->Init();
-    return mDOMCameraControl->Result(rv, mOnSuccessCb, mOnErrorCb);
+    return mDOMCameraControl->Result(rv, mOnSuccessCb, mOnErrorCb, mWindowId);
   }
 
   nsRefPtr<nsGonkCameraControl> mCameraControl;
   // Raw pointer to DOM-facing camera control--it must NS_ADDREF itself for us
   nsDOMCameraControl* mDOMCameraControl;
   nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
   nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+  uint64_t mWindowId;
 };
 
 // Construct nsGonkCameraControl on the main thread.
-nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError)
-  : CameraControlImpl(aCameraId, aCameraThread)
+nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+  : CameraControlImpl(aCameraId, aCameraThread, aWindowId)
   , mHwHandle(0)
   , mExposureCompensationMin(0.0)
   , mExposureCompensationStep(0.0)
   , mDeferConfigUpdate(false)
   , mWidth(0)
   , mHeight(0)
   , mFormat(PREVIEW_FORMAT_UNKNOWN)
   , mDiscardedFrameCount(0)
 {
   // Constructor runs on the main thread...
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock");
 
   // ...but initialization is carried out on the camera thread.
-  nsCOMPtr<nsIRunnable> init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError);
+  nsCOMPtr<nsIRunnable> init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError, aWindowId);
   mCameraThread->Dispatch(init, NS_DISPATCH_NORMAL);
 }
 
 nsresult
 nsGonkCameraControl::Init()
 {
   mHwHandle = GonkCameraHardware::GetHandle(this, mCameraId);
   DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mHwHandle=%d)\n", mCameraId, this, mHwHandle);
@@ -514,17 +516,17 @@ nsGonkCameraControl::SetParameter(uint32
 
 nsresult
 nsGonkCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
 {
   SetPreviewSize(aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height);
 
   DOM_CAMERA_LOGI("config preview: wated %d x %d, got %d x %d (%d fps, format %d)\n", aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height, mWidth, mHeight, mFps, mFormat);
 
-  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, mWidth, mHeight, mFps, aGetPreviewStream->mOnSuccessCb);
+  nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = new GetPreviewStreamResult(this, mWidth, mHeight, mFps, aGetPreviewStream->mOnSuccessCb, mWindowId);
   return NS_DispatchToMainThread(getPreviewStreamResult);
 }
 
 nsresult
 nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview)
 {
   /**
    * If 'aStartPreview->mDOMPreview' is null, we are just restarting
@@ -582,17 +584,17 @@ nsGonkCameraControl::AutoFocusImpl(AutoF
     /**
      * We already have a callback, so someone has already
      * called autoFocus() -- cancel it.
      */
     mAutoFocusOnSuccessCb = nullptr;
     nsCOMPtr<nsICameraErrorCallback> ecb = mAutoFocusOnErrorCb;
     mAutoFocusOnErrorCb = nullptr;
     if (ecb) {
-      nsresult rv = NS_DispatchToMainThread(new CameraErrorResult(ecb, NS_LITERAL_STRING("CANCELLED")));
+      nsresult rv = NS_DispatchToMainThread(new CameraErrorResult(ecb, NS_LITERAL_STRING("CANCELLED"), mWindowId));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     GonkCameraHardware::CancelAutoFocus(mHwHandle);
   }
 
   mAutoFocusOnSuccessCb = aAutoFocus->mOnSuccessCb;
   mAutoFocusOnErrorCb = aAutoFocus->mOnErrorCb;
@@ -611,17 +613,17 @@ nsGonkCameraControl::TakePictureImpl(Tak
     /**
      * We already have a callback, so someone has already
      * called TakePicture() -- cancel it.
      */
     mTakePictureOnSuccessCb = nullptr;
     nsCOMPtr<nsICameraErrorCallback> ecb = mTakePictureOnErrorCb;
     mTakePictureOnErrorCb = nullptr;
     if (ecb) {
-      nsresult rv = NS_DispatchToMainThread(new CameraErrorResult(ecb, NS_LITERAL_STRING("CANCELLED")));
+      nsresult rv = NS_DispatchToMainThread(new CameraErrorResult(ecb, NS_LITERAL_STRING("CANCELLED"), mWindowId));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     GonkCameraHardware::CancelTakePicture(mHwHandle);
   }
 
   mTakePictureOnSuccessCb = aTakePicture->mOnSuccessCb;
   mTakePictureOnErrorCb = aTakePicture->mOnErrorCb;
@@ -704,44 +706,27 @@ nsGonkCameraControl::PullParametersImpl(
 
 nsresult
 nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
 {
   mStartRecordingOnSuccessCb = aStartRecording->mOnSuccessCb;
   mStartRecordingOnErrorCb = aStartRecording->mOnErrorCb;
 
   /**
-   * We need to pull in the base path from aStartRecording->mStorageArea
-   * once that feature lands.  See bug 795201.
-   *
-   * For now, we just assume /sdcard/Movies.
-   *
-   * Also, the camera app needs to provide the file extension '.3gp' for now.
+   * The camera app needs to provide the file extension '.3gp' for now.
    * See bug 795202.
    */
-#if 1
   nsCOMPtr<nsIFile> filename;
   aStartRecording->mStorageArea->GetRootDirectory(getter_AddRefs(filename));
   filename->Append(aStartRecording->mFilename);
 
   nsAutoCString pathname;
   filename->GetNativePath(pathname);
-#else
-  nsAutoCString pathname(NS_LITERAL_CSTRING("/sdcard/Movies/"));
-  nsAutoCString filename(NS_ConvertUTF16toUTF8(aStartRecording->mFilename));
+  DOM_CAMERA_LOGI("Video pathname is '%s'\n", pathname.get());
 
-  // Make sure that the file name doesn't contain any directory components.
-  if (strcmp(filename.get(), basename(filename.get())) != 0) {
-    DOM_CAMERA_LOGE("Video filename '%s' is not valid\n", filename.get());
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  pathname.Append(filename);
-#endif
-  DOM_CAMERA_LOGI("Video pathname is '%s'\n", pathname.get());
   int fd = open(pathname.get(), O_RDWR | O_CREAT, 0644);
   if (fd < 0) {
     DOM_CAMERA_LOGE("Couldn't create file '%s' with error (%d) %s\n", pathname.get(), errno, strerror(errno));
     return NS_ERROR_FAILURE;
   }
 
   if (SetupRecording(fd) != NS_OK) {
     DOM_CAMERA_LOGE("SetupRecording() failed\n");
@@ -750,17 +735,17 @@ nsGonkCameraControl::StartRecordingImpl(
   }
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     close(fd);
     return NS_ERROR_FAILURE;
   }
 
   // dispatch the callback
-  nsCOMPtr<nsIRunnable> startRecordingResult = new StartRecordingResult(mStartRecordingOnSuccessCb);
+  nsCOMPtr<nsIRunnable> startRecordingResult = new StartRecordingResult(mStartRecordingOnSuccessCb, mWindowId);
   nsresult rv = NS_DispatchToMainThread(startRecordingResult);
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
   }
   return NS_OK;
 }
 
 nsresult
@@ -783,17 +768,17 @@ nsGonkCameraControl::AutoFocusComplete(b
   PullParametersImpl();
 
   /**
    * If we make it here, regardless of the value of 'aSuccess', we
    * consider the autofocus _process_ to have succeeded.  It is up
    * to the onSuccess callback to determine how to handle the case
    * where the camera wasn't actually able to acquire focus.
    */
-  nsCOMPtr<nsIRunnable> autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb);
+  nsCOMPtr<nsIRunnable> autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb, mWindowId);
   /**
    * Remember to set these to null so that we don't hold any extra
    * references to our document's window.
    */
   mAutoFocusOnSuccessCb = nullptr;
   mAutoFocusOnErrorCb = nullptr;
   nsresult rv = NS_DispatchToMainThread(autoFocusResult);
   if (NS_FAILED(rv)) {
@@ -805,17 +790,17 @@ void
 nsGonkCameraControl::TakePictureComplete(uint8_t* aData, uint32_t aLength)
 {
   uint8_t* data = new uint8_t[aLength];
 
   memcpy(data, aData, aLength);
 
   // TODO: see bug 779144.
   nsIDOMBlob* blob = new nsDOMMemoryFile(static_cast<void*>(data), static_cast<uint64_t>(aLength), NS_LITERAL_STRING("image/jpeg"));
-  nsCOMPtr<nsIRunnable> takePictureResult = new TakePictureResult(blob, mTakePictureOnSuccessCb);
+  nsCOMPtr<nsIRunnable> takePictureResult = new TakePictureResult(blob, mTakePictureOnSuccessCb, mWindowId);
   /**
    * Remember to set these to null so that we don't hold any extra
    * references to our document's window.
    */
   mTakePictureOnSuccessCb = nullptr;
   mTakePictureOnErrorCb = nullptr;
   nsresult rv = NS_DispatchToMainThread(takePictureResult);
   if (NS_FAILED(rv)) {
@@ -1012,17 +997,17 @@ nsGonkCameraControl::GetPreviewStreamVid
   mVideoHeight = aGetPreviewStreamVideoMode->mOptions.height;
   DOM_CAMERA_LOGI("recording preview format: %d x %d (w x h) (rotated %d degrees)\n", mVideoWidth, mVideoHeight, mVideoRotation);
 
   // setup the video mode
   nsresult rv = SetupVideoMode();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // create and return new preview stream object
-  getPreviewStreamResult = new GetPreviewStreamResult(this, mVideoWidth, mVideoHeight, mVideoFrameRate, aGetPreviewStreamVideoMode->mOnSuccessCb);
+  getPreviewStreamResult = new GetPreviewStreamResult(this, mVideoWidth, mVideoHeight, mVideoFrameRate, aGetPreviewStreamVideoMode->mOnSuccessCb, mWindowId);
   rv = NS_DispatchToMainThread(getPreviewStreamResult);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!");
     return rv;
   }
 
   return NS_OK;
 }
@@ -1059,9 +1044,21 @@ GonkFrameBuilder(Image* aImage, void* aB
 void
 ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer)
 {
   if (!gc->ReceiveFrame(aBuffer, ImageFormat::GONK_IO_SURFACE, GonkFrameBuilder)) {
     aBuffer->Unlock();
   }
 }
 
+void
+OnShutter(nsGonkCameraControl* gc)
+{
+  gc->OnShutter();
+}
+
+void
+OnClosed(nsGonkCameraControl* gc)
+{
+  gc->OnClosed();
+}
+
 } // namespace mozilla
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -30,17 +30,17 @@ namespace mozilla {
 
 namespace layers {
 class GraphicBufferLocked;
 }
 
 class nsGonkCameraControl : public CameraControlImpl
 {
 public:
-  nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
   nsresult Init();
 
   const char* GetParameter(const char* aKey);
   const char* GetParameterConstChar(uint32_t aKey);
   double GetParameterDouble(uint32_t aKey);
   void GetParameter(uint32_t aKey, nsTArray<dom::CameraRegion>& aRegions);
   void SetParameter(const char* aKey, const char* aValue);
   void SetParameter(uint32_t aKey, const char* aValue);
@@ -115,12 +115,14 @@ private:
   nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
   nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE;
 };
 
 // camera driver callbacks
 void ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
 void AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
 void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
+void OnShutter(nsGonkCameraControl* gc);
+void OnClosed(nsGonkCameraControl* gc);
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_GONKCAMERACONTROL_H
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -129,17 +129,17 @@ GonkCameraHardware::NotifyCallback(int32
       } else {
         DOM_CAMERA_LOGW("Autofocus failed");
         bSuccess = false;
       }
       AutoFocusComplete(camera, bSuccess);
       break;
 
     case CAMERA_MSG_SHUTTER:
-      DOM_CAMERA_LOGW("Shutter event not handled yet\n");
+      OnShutter(camera);
       break;
 
     default:
       DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType);
       break;
   }
 }
 
@@ -193,16 +193,24 @@ GonkCameraHardware::Init()
   mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, GonkCameraHardware::DataCallbackTimestamp, (void*)sHwHandle);
   mInitialized = true;
 }
 
 GonkCameraHardware::~GonkCameraHardware()
 {
   DOM_CAMERA_LOGT( "%s:%d : this=%p\n", __func__, __LINE__, (void*)this );
   sHw = nullptr;
+
+  /**
+   * Trigger the OnClosed event; the upper layers can't do anything
+   * with the hardware layer once they receive this event.
+   */
+  if (mTarget) {
+    OnClosed(mTarget);
+  }
 }
 
 GonkCameraHardware* GonkCameraHardware::sHw         = nullptr;
 uint32_t            GonkCameraHardware::sHwHandle   = 0;
 
 void
 GonkCameraHardware::ReleaseHandle(uint32_t aHwHandle)
 {
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -32,27 +32,33 @@ public:
   virtual nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
 
   virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
   virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
   virtual nsresult Set(uint32_t aKey, double aValue) = 0;
   virtual nsresult Get(uint32_t aKey, double* aValue) = 0;
   virtual nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) = 0;
   virtual nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) = 0;
+  virtual nsresult Set(nsICameraShutterCallback* aOnShutter) = 0;
+  virtual nsresult Get(nsICameraShutterCallback** aOnShutter) = 0;
+  virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0;
+  virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0;
   virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
   virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
 
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
   virtual double GetParameterDouble(uint32_t aKey) = 0;
   virtual void GetParameter(uint32_t aKey, nsTArray<CameraRegion>& aRegions) = 0;
   virtual void SetParameter(const char* aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
   virtual void SetParameter(uint32_t aKey, double aValue) = 0;
   virtual void SetParameter(uint32_t aKey, const nsTArray<CameraRegion>& aRegions) = 0;
 
+  virtual void Shutdown() = 0;
+
 protected:
   virtual ~ICameraControl() { }
 };
 
 } // namespace mozilla
 
 #endif // DOM_CAMERA_ICAMERACONTROL_H
--- a/dom/camera/nsIDOMCameraManager.idl
+++ b/dom/camera/nsIDOMCameraManager.idl
@@ -179,27 +179,33 @@ interface nsICameraStartRecordingCallbac
 };
 
 [scriptable, function, uuid(fb80db71-e315-42f0-9ea9-dd3dd312ed70)]
 interface nsICameraShutterCallback : nsISupports
 {
     void handleEvent();
 };
 
+[scriptable, function, uuid(0ef0f01e-ce74-4741-9bba-54376adfb7a2)]
+interface nsICameraClosedCallback : nsISupports
+{
+    void handleEvent();
+};
+
 [scriptable, function, uuid(a302c6c9-3776-4d1d-a395-f4105d47c3d3)]
 interface nsICameraErrorCallback : nsISupports
 {
     void handleEvent(in DOMString error);
 };
 
 /*
     attributes here affect the preview, any pictures taken, and/or
     any video recorded by the camera.
 */
-[scriptable, uuid(469e0462-59e4-4ed5-afa9-aecd1256ee30)]
+[scriptable, uuid(0f206acd-196b-4bdf-8198-44c1a0cd1998)]
 interface nsICameraControl : nsISupports
 {
     readonly attribute nsICameraCapabilities capabilities;
 
     /* one of the vales chosen from capabilities.effects;
        default is "none" */
     attribute DOMString         effect;
 
@@ -280,16 +286,21 @@ interface nsICameraControl : nsISupports
     [implicit_jscontext]
     void setExposureCompensation([optional] in jsval compensation);
     readonly attribute double   exposureCompensation;
 
     /* the function to call on the camera's shutter event, to trigger
        a shutter sound and/or a visual shutter indicator. */
     attribute nsICameraShutterCallback onShutter;
 
+    /* the function to call when the camera hardware is closed
+       by the underlying framework, e.g. when another app makes a more
+       recent call to get the camera. */
+    attribute nsICameraClosedCallback onClosed;
+
     /* tell the camera to attempt to focus the image */
     void autoFocus(in nsICameraAutoFocusCallback onSuccess, [optional] in nsICameraErrorCallback onError);
 
     /* capture an image and return it as a blob to the 'onSuccess' callback;
        if the camera supports it, this may be invoked while the camera is
        already recording video.
 
        invoking this function will stop the preview stream, which must be
--- a/dom/icc/interfaces/SimToolKit.idl
+++ b/dom/icc/interfaces/SimToolKit.idl
@@ -455,8 +455,42 @@ dictionary MozStkResponse
    *
    * @see RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM
    *
    * true: Confirmed by User.
    * false: Rejected by User.
    */
   boolean hasConfirmed;
 };
+
+dictionary MozStkCallEvent
+{
+  /**
+   * The type of this event.
+   * It shall be one of following:
+   *     - nsIDOMMozIccManager.STK_EVENT_TYPE_MT_CALL,
+   *     - nsIDOMMozIccManager.STK_EVENT_TYPE_CALL_CONNECTED,
+   *     - nsIDOMMozIccManager.STK_EVENT_TYPE_CALL_DISCONNECTED.
+   */
+  unsigned short eventType;
+
+  /**
+   * Remote party number.
+   */
+  DOMString number;
+
+  /**
+   * This field is available in 'STK_EVENT_TYPE_CALL_CONNECTED' and
+   * 'STK_EVENT_TYPE_CALL_DISCONNECTED' events.
+   * For STK_EVENT_TYPE_CALL_DISCONNECTED event, setting this to true means the
+   * connection is answered by remote end, that is, this is an outgoing call.
+   * For STK_EVENT_TYPE_CALL_DISCONNECTED event, setting this to true indicates
+   * the connection is hung up by remote.
+   */
+  boolean isIssuedByRemote;
+
+  /**
+   * This field is available in Call Disconnected event to indicate the cause
+   * of disconnection. The cause string is passed to gaia through the error
+   * listener of nsIDOMCallEvent. Null if there's no error.
+   */
+  DOMString error;
+};
\ No newline at end of file
--- a/dom/icc/interfaces/nsIDOMIccManager.idl
+++ b/dom/icc/interfaces/nsIDOMIccManager.idl
@@ -219,16 +219,17 @@ interface nsIDOMMozIccManager : nsIDOMEv
 
   /**
    * Send "Event Download" Envelope command to ICC.
    * ICC will not respond with any data for this command.
    *
    * @param event
    *        one of events below:
    *        - MozStkLocationEvent
+   *        - MozStkCallEvent
    */
   void sendStkEventDownload(in jsval event);
 
   /**
    * The 'stkcommand' event is notified whenever STK Proactive Command is
    * issued from ICC.
    */
   [implicit_jscontext] attribute jsval onstkcommand;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -746,17 +746,17 @@ nsresult nsPluginHost::FindProxyForURL(c
   nsCOMPtr<nsIProtocolProxyService2> proxyService2;
   nsCOMPtr<nsIIOService> ioService;
 
   proxyService = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &res);
   if (NS_FAILED(res) || !proxyService)
     return res;
 
   proxyService2 = do_QueryInterface(proxyService, &res);
-  if (NS_FAILED(res) || !proxyService)
+  if (NS_FAILED(res) || !proxyService2)
     return res;
 
   ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &res);
   if (NS_FAILED(res) || !ioService)
     return res;
 
   // make an nsURI from the argument url
   res = ioService->NewURI(nsDependentCString(url), nullptr, nullptr, getter_AddRefs(uriIn));
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -530,27 +530,30 @@ const COMPREHENSIONTLV_FLAG_CR = 0x80;  
 
 // Tags for Comprehension TLV.
 const COMPREHENSIONTLV_TAG_COMMAND_DETAILS = 0x01;
 const COMPREHENSIONTLV_TAG_DEVICE_ID = 0x02;
 const COMPREHENSIONTLV_TAG_RESULT = 0x03;
 const COMPREHENSIONTLV_TAG_DURATION = 0x04;
 const COMPREHENSIONTLV_TAG_ALPHA_ID = 0x05;
 const COMPREHENSIONTLV_TAG_ADDRESS = 0x06;
+const COMPREHENSIONTLV_TAG_SUBADDRESS = 0x08;
 const COMPREHENSIONTLV_TAG_SMS_TPDU = 0x0b;
 const COMPREHENSIONTLV_TAG_TEXT_STRING = 0x0d;
 const COMPREHENSIONTLV_TAG_TONE = 0x0e;
 const COMPREHENSIONTLV_TAG_ITEM = 0x0f;
 const COMPREHENSIONTLV_TAG_ITEM_ID = 0x10;
 const COMPREHENSIONTLV_TAG_RESPONSE_LENGTH = 0x11;
 const COMPREHENSIONTLV_TAG_FILE_LIST = 0x12;
 const COMPREHENSIONTLV_TAG_LOCATION_INFO = 0x13;
 const COMPREHENSIONTLV_TAG_HELP_REQUEST = 0x15;
 const COMPREHENSIONTLV_TAG_DEFAULT_TEXT = 0x17;
+const COMPREHENSIONTLV_TAG_CAUSE = 0x1a;
 const COMPREHENSIONTLV_TAG_LOCATION_STATUS = 0x1b;
+const COMPREHENSIONTLV_TAG_TRANSACTION_ID = 0x1c;
 const COMPREHENSIONTLV_TAG_EVENT_LIST = 0x19;
 const COMPREHENSIONTLV_TAG_ICON_ID = 0x1e;
 const COMPREHENSIONTLV_TAG_ICON_ID_LIST = 0x1f;
 const COMPREHENSIONTLV_TAG_IMMEDIATE_RESPONSE = 0x2b;
 const COMPREHENSIONTLV_TAG_URL = 0x31;
 
 // Device identifiers, see TS 11.14, clause 12.7
 const STK_DEVICE_ID_KEYPAD = 0x01;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2389,46 +2389,75 @@ let RIL = {
           destinationId: STK_DEVICE_ID_SIM
         };
         command.locationStatus = command.event.locationStatus;
         // Location info should only be provided when locationStatus is normal.
         if (command.locationStatus == STK_SERVICE_STATE_NORMAL) {
           command.locationInfo = command.event.locationInfo;
         }
         break;
+      case STK_EVENT_TYPE_MT_CALL:
+        command.deviceId = {
+          sourceId: STK_DEVICE_ID_NETWORK,
+          destinationId: STK_DEVICE_ID_SIM
+        };
+        command.transactionId = 0;
+        command.address = command.eventData.number;
+        break;
+      case STK_EVENT_TYPE_CALL_DISCONNECTED:
+        command.cause = command.eventData.error;
+      case STK_EVENT_TYPE_CALL_CONNECTED:  // Fall through
+        command.deviceId = {
+          sourceId: (command.eventData.isIssuedByRemote ?
+                     STK_DEVICE_ID_NETWORK : STK_DEVICE_ID_ME),
+          destinationId: STK_DEVICE_ID_SIM
+        };
+        command.transactionId = 0;
+        break;
     }
     this.sendICCEnvelopeCommand(command);
   },
 
   /**
    * Send REQUEST_STK_SEND_ENVELOPE_COMMAND to ICC.
    *
    * @param tag
    * @patam deviceId
    * @param [optioanl] itemIdentifier
    * @param [optional] helpRequested
    * @param [optional] eventList
    * @param [optional] locationStatus
    * @param [optional] locationInfo
+   * @param [optional] address
+   * @param [optional] transactionId
+   * @param [optional] cause
    */
   sendICCEnvelopeCommand: function sendICCEnvelopeCommand(options) {
     if (DEBUG) {
       debug("Stk Envelope " + JSON.stringify(options));
     }
     let token = Buf.newParcel(REQUEST_STK_SEND_ENVELOPE_COMMAND);
     let berLen = TLV_DEVICE_ID_SIZE + /* Size of Device Identifier TLV */
                  (options.itemIdentifier ? TLV_ITEM_ID_SIZE : 0) +
                  (options.helpRequested ? TLV_HELP_REQUESTED_SIZE : 0) +
                  (options.eventList ? TLV_EVENT_LIST_SIZE : 0) +
                  (options.locationStatus ? TLV_LOCATION_STATUS_SIZE : 0) +
                  (options.locationInfo ?
                     (options.locationInfo.gsmCellId > 0xffff ?
                       TLV_LOCATION_INFO_UMTS_SIZE :
                       TLV_LOCATION_INFO_GSM_SIZE) :
-                    0);
+                    0) +
+                 (options.transactionId ? 3 : 0) +
+                 (options.address ?
+                  1 + // Length of tag.
+                  ComprehensionTlvHelper.getSizeOfLengthOctets(
+                    Math.ceil(options.address.length/2) + 1) + // Length of length field.
+                  Math.ceil(options.address.length/2) + 1 // address BCD + TON.
+                  : 0) +
+                 (options.cause ? 4 : 0);
     let size = (2 + berLen) * 2;
 
     Buf.writeUint32(size);
 
     // Write a BER-TLV
     GsmPDUHelper.writeHexOctet(options.tag);
     GsmPDUHelper.writeHexOctet(berLen);
 
@@ -2472,16 +2501,39 @@ let RIL = {
       GsmPDUHelper.writeHexOctet(options.locationStatus);
     }
 
     // Location Info
     if (options.locationInfo) {
       ComprehensionTlvHelper.writeLocationInfoTlv(options.locationInfo);
     }
 
+    // Transaction Id
+    if (options.transactionId) {
+      GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_TRANSACTION_ID |
+                                 COMPREHENSIONTLV_FLAG_CR);
+      GsmPDUHelper.writeHexOctet(1);
+      GsmPDUHelper.writeHexOctet(options.transactionId);
+    }
+
+    // Address
+    if (options.address) {
+      GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_ADDRESS |
+                                 COMPREHENSIONTLV_FLAG_CR);
+      ComprehensionTlvHelper.writeLength(
+        Math.ceil(options.address.length/2) + 1 // address BCD + TON
+      );
+      GsmPDUHelper.writeDiallingNumber(options.address);
+    }
+
+    // Cause of disconnection.
+    if (options.cause) {
+      ComprehensionTlvHelper.writeCauseTlv(options.cause);
+    }
+
     Buf.writeUint32(0);
     Buf.sendParcel();
   },
 
   /**
    * Check a given number against the list of emergency numbers provided by the RIL.
    *
    * @param number
@@ -6954,16 +7006,85 @@ let ComprehensionTlvHelper = {
       GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 8) & 0xff);
       GsmPDUHelper.writeHexOctet(loc.gsmCellId & 0xff);
     } else {
       // GSM, gsmCellId is 16 bits.
       GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 8) & 0xff);
       GsmPDUHelper.writeHexOctet(loc.gsmCellId & 0xff);
     }
   },
+
+  /**
+   * Given a geckoError string, this function translates it into cause value
+   * and write the value into buffer.
+   *
+   * @param geckoError Error string that is passed to gecko.
+   */
+  writeCauseTlv: function writeCauseTlv(geckoError) {
+    let cause = -1;
+    for (let errorNo in RIL_ERROR_TO_GECKO_ERROR) {
+      if (geckoError == RIL_ERROR_TO_GECKO_ERROR[errorNo]) {
+        cause = errorNo;
+        break;
+      }
+    }
+    cause = (cause == -1) ? ERROR_SUCCESS : cause;
+
+    GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_CAUSE |
+                               COMPREHENSIONTLV_FLAG_CR);
+    GsmPDUHelper.writeHexOctet(2);  // For single cause value.
+
+    // TS 04.08, clause 10.5.4.11: National standard code + user location.
+    GsmPDUHelper.writeHexOctet(0x60);
+
+    // TS 04.08, clause 10.5.4.11: ext bit = 1 + 7 bits for cause.
+    // +-----------------+----------------------------------+
+    // | Ext = 1 (1 bit) |          Cause (7 bits)          |
+    // +-----------------+----------------------------------+
+    GsmPDUHelper.writeHexOctet(0x80 | cause);
+  },
+
+  getSizeOfLengthOctets: function getSizeOfLengthOctets(length) {
+    if (length >= 0x10000) {
+      return 4; // 0x83, len_1, len_2, len_3
+    } else if (length >= 0x100) {
+      return 3; // 0x82, len_1, len_2
+    } else if (length >= 0x80) {
+      return 2; // 0x81, len
+    } else {
+      return 1; // len
+    }
+  },
+
+  writeLength: function writeLength(length) {
+    // TS 101.220 clause 7.1.2, Length Encoding.
+    // Length   |  Byte 1  | Byte 2 | Byte 3 | Byte 4 |
+    // 0 - 127  |  00 - 7f | N/A    | N/A    | N/A    |
+    // 128-255  |  81      | 80 - ff| N/A    | N/A    |
+    // 256-65535|  82      | 0100 - ffff     | N/A    |
+    // 65536-   |  83      |     010000 - ffffff      |
+    // 16777215
+    if (length < 0x80) {
+      GsmPDUHelper.writeHexOctet(length);
+    } else if (0x80 <= length && length < 0x100) {
+      GsmPDUHelper.writeHexOctet(0x81);
+      GsmPDUHelper.writeHexOctet(length);
+    } else if (0x100 <= length && length < 0x10000) {
+      GsmPDUHelper.writeHexOctet(0x82);
+      GsmPDUHelper.writeHexOctet((length >> 8) & 0xff);
+      GsmPDUHelper.writeHexOctet(length & 0xff);
+    } else if (0x10000 <= length && length < 0x1000000) {
+      GsmPDUHelper.writeHexOctet(0x83);
+      GsmPDUHelper.writeHexOctet((length >> 16) & 0xff);
+      GsmPDUHelper.writeHexOctet((length >> 8) & 0xff);
+      GsmPDUHelper.writeHexOctet(length & 0xff);
+    } else {
+      throw new Error("Invalid length value :" + length);
+    }
+  },
 };
 
 let BerTlvHelper = {
   /**
    * Decode Ber TLV.
    *
    * @param dataLen
    *        The length of data in bytes.
--- a/dom/system/gonk/tests/test_ril_worker_icc.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc.js
@@ -229,16 +229,37 @@ add_test(function test_write_location_in
   cellId = (pduHelper.readHexOctet() << 8) |
            (pduHelper.readHexOctet());
   do_check_eq(cellId, 65534);
 
   run_next_test();
 });
 
 /**
+ * Verify ComprehensionTlvHelper.writeErrorNumber
+ */
+add_test(function test_write_disconnecting_cause() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  tlvHelper.writeCauseTlv(RIL_ERROR_TO_GECKO_ERROR[ERROR_GENERIC_FAILURE]);
+  let tag = pduHelper.readHexOctet();
+  do_check_eq(tag, COMPREHENSIONTLV_TAG_CAUSE | COMPREHENSIONTLV_FLAG_CR);
+  let len = pduHelper.readHexOctet();
+  do_check_eq(len, 2);  // We have one cause.
+  let standard = pduHelper.readHexOctet();
+  do_check_eq(standard, 0x60);
+  let cause = pduHelper.readHexOctet();
+  do_check_eq(cause, 0x80 | ERROR_GENERIC_FAILURE);
+
+  run_next_test();
+});
+
+/**
  * Verify Proactive Command : Refresh
  */
 add_test(function test_stk_proactive_command_refresh() {
   let worker = newUint8Worker();
   let pduHelper = worker.GsmPDUHelper;
   let berHelper = worker.BerTlvHelper;
   let stkHelper = worker.StkProactiveCmdHelper;
 
@@ -336,8 +357,63 @@ add_test(function test_stk_proactive_com
   do_check_eq(tlv.value.commandQualifier, 0x00);
 
   tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
   do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
   do_check_eq(tlv.value.timeInterval, 0x14);
 
   run_next_test();
 });
+
+/**
+ * Verify ComprehensionTlvHelper.getSizeOfLengthOctets
+ */
+add_test(function test_get_size_of_length_octets() {
+  let worker = newUint8Worker();
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  let length = 0x70;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 1);
+
+  length = 0x80;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 2);
+
+  length = 0x180;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 3);
+
+  length = 0x18000;
+  do_check_eq(tlvHelper.getSizeOfLengthOctets(length), 4);
+
+  run_next_test();
+});
+
+/**
+ * Verify ComprehensionTlvHelper.writeLength
+ */
+add_test(function test_write_length() {
+  let worker = newUint8Worker();
+  let pduHelper = worker.GsmPDUHelper;
+  let tlvHelper = worker.ComprehensionTlvHelper;
+
+  let length = 0x70;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), length);
+
+  length = 0x80;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), 0x81);
+  do_check_eq(pduHelper.readHexOctet(), length);
+
+  length = 0x180;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), 0x82);
+  do_check_eq(pduHelper.readHexOctet(), (length >> 8) & 0xff);
+  do_check_eq(pduHelper.readHexOctet(), length & 0xff);
+
+  length = 0x18000;
+  tlvHelper.writeLength(length);
+  do_check_eq(pduHelper.readHexOctet(), 0x83);
+  do_check_eq(pduHelper.readHexOctet(), (length >> 16) & 0xff);
+  do_check_eq(pduHelper.readHexOctet(), (length >> 8) & 0xff);
+  do_check_eq(pduHelper.readHexOctet(), length & 0xff);
+
+  run_next_test();
+});
\ No newline at end of file
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -753,17 +753,17 @@ CompositorParent::TransformShadowTree(Ti
   bool wantNextFrame = false;
   Layer* layer = mLayerManager->GetPrimaryScrollableLayer();
   ShadowLayer* shadow = layer->AsShadowLayer();
   ContainerLayer* container = layer->AsContainerLayer();
   Layer* root = mLayerManager->GetRoot();
 
   // NB: we must sample animations *before* sampling pan/zoom
   // transforms.
-  wantNextFrame |= SampleAnimations(layer, mLastCompose);
+  wantNextFrame |= SampleAnimations(root, mLastCompose);
 
   const FrameMetrics& metrics = container->GetFrameMetrics();
   // We must apply the resolution scale before a pan/zoom transform, so we call
   // GetTransform here.
   const gfx3DMatrix& rootTransform = root->GetTransform();
   const gfx3DMatrix& currentTransform = layer->GetTransform();
 
   // FIXME/bug 775437: unify this interface with the ~native-fennec
--- a/ipc/ril/Ril.cpp
+++ b/ipc/ril/Ril.cpp
@@ -88,17 +88,21 @@ class RilReconnectTask : public Cancelab
     virtual void Run();
     virtual void Cancel() { mCanceled = true; }
 
     bool mCanceled;
 
 public:
     static void Enqueue(int aDelayMs = 0) {
         MessageLoopForIO* ioLoop = MessageLoopForIO::current();
-        MOZ_ASSERT(ioLoop && sClient->mIOLoop == ioLoop);
+        if (!ioLoop) {
+            NS_WARNING("No IOLoop to attach to, cancelling self!");
+            CancelIt();
+            return;
+        }
         if (sTask) {
             return;
         }
         sTask = new RilReconnectTask();
         if (aDelayMs) {
             ioLoop->PostDelayedTask(FROM_HERE, sTask, aDelayMs);
         } else {
             ioLoop->PostTask(FROM_HERE, sTask);
@@ -135,16 +139,20 @@ void RilReconnectTask::Run() {
     Enqueue(1000);
 }
 
 class RilWriteTask : public Task {
     virtual void Run();
 };
 
 void RilWriteTask::Run() {
+    if(sClient->mSocket.get() < 0) {
+        NS_WARNING("Trying to write to non-open socket!");
+        return;
+    }
     sClient->OnFileCanWriteWithoutBlocking(sClient->mSocket.rwget());
 }
 
 static void
 ConnectToRil(Monitor* aMonitor, bool* aSuccess)
 {
     MOZ_ASSERT(!sClient);
 
@@ -156,79 +164,82 @@ ConnectToRil(Monitor* aMonitor, bool* aS
         lock.Notify();
     }
     // aMonitor may have gone out of scope by now, don't touch it
 }
 
 bool
 RilClient::OpenSocket()
 {
+
+    ScopedClose skt;
 #if defined(MOZ_WIDGET_GONK)
     // Using a network socket to test basic functionality
     // before we see how this works on the phone.
     struct sockaddr_un addr;
     socklen_t alen;
     size_t namelen;
     int err;
     memset(&addr, 0, sizeof(addr));
     strcpy(addr.sun_path, RIL_SOCKET_NAME);
     addr.sun_family = AF_LOCAL;
-    mSocket.reset(socket(AF_LOCAL, SOCK_STREAM, 0));
+    skt.reset(socket(AF_LOCAL, SOCK_STREAM, 0));
     alen = strlen(RIL_SOCKET_NAME) + offsetof(struct sockaddr_un, sun_path) + 1;
 #else
     struct hostent *hp;
     struct sockaddr_in addr;
     socklen_t alen;
 
     hp = gethostbyname("localhost");
     if (hp == 0) return false;
 
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = hp->h_addrtype;
     addr.sin_port = htons(RIL_TEST_PORT);
     memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
-    mSocket.reset(socket(hp->h_addrtype, SOCK_STREAM, 0));
+    skt.reset(socket(hp->h_addrtype, SOCK_STREAM, 0));
     alen = sizeof(addr);
 #endif
 
-    if (mSocket.get() < 0) {
+    if (skt.get() < 0) {
         LOG("Cannot create socket for RIL!\n");
         return false;
     }
 
-    if (connect(mSocket.get(), (struct sockaddr *) &addr, alen) < 0) {
+    if (connect(skt.get(), (struct sockaddr *) &addr, alen) < 0) {
 #if defined(MOZ_WIDGET_GONK)
         LOG("Cannot open socket for RIL!\n");
 #endif
-        mSocket.dispose();
+        skt.dispose();
         return false;
     }
 
     // Set close-on-exec bit.
-    int flags = fcntl(mSocket.get(), F_GETFD);
+    int flags = fcntl(skt.get(), F_GETFD);
     if (-1 == flags) {
         return false;
     }
 
     flags |= FD_CLOEXEC;
-    if (-1 == fcntl(mSocket.get(), F_SETFD, flags)) {
+    if (-1 == fcntl(skt.get(), F_SETFD, flags)) {
         return false;
     }
 
     // Select non-blocking IO.
-    if (-1 == fcntl(mSocket.get(), F_SETFL, O_NONBLOCK)) {
+    if (-1 == fcntl(skt.get(), F_SETFL, O_NONBLOCK)) {
         return false;
     }
-    if (!mIOLoop->WatchFileDescriptor(mSocket.get(),
+    if (!mIOLoop->WatchFileDescriptor(skt.get(),
                                       true,
                                       MessageLoopForIO::WATCH_READ,
                                       &mReadWatcher,
                                       this)) {
         return false;
     }
+    mSocket = skt.forget();
     LOG("Socket open for RIL\n");
     return true;
 }
 
 void
 RilClient::OnFileCanReadWithoutBlocking(int fd)
 {
     // Keep reading data until either
@@ -256,17 +267,19 @@ RilClient::OnFileCanReadWithoutBlocking(
                     // else fall through to error handling on other errno's
                 }
                 LOG("Cannot read from network, error %d\n", ret);
                 // At this point, assume that we can't actually access
                 // the socket anymore, and start a reconnect loop.
                 mIncoming.forget();
                 mReadWatcher.StopWatchingFileDescriptor();
                 mWriteWatcher.StopWatchingFileDescriptor();
-                close(mSocket.get());
+                // ScopedClose will close our old socket on a reset.
+                // Setting to -1 means writes will fail with message.
+                mSocket.reset(-1);
                 RilReconnectTask::Enqueue();
                 return;
             }
             mIncoming->mSize = ret;
             sConsumer->MessageReceived(mIncoming.forget());
             if (ret < ssize_t(RilRawData::MAX_DATA_SIZE)) {
                 return;
             }
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -17,16 +17,21 @@
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::ion;
 
 namespace js {
 namespace ion {
 
+StringObject *
+MNewStringObject::templateObj() const {
+    return &templateObj_->asString();
+}
+
 CodeGenerator::CodeGenerator(MIRGenerator *gen, LIRGraph &graph)
   : CodeGeneratorSpecific(gen, graph)
 {
 }
 
 bool
 CodeGenerator::visitValueToInt32(LValueToInt32 *lir)
 {
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -5306,19 +5306,19 @@ class MNewStringObject :
 
     static MNewStringObject *New(MDefinition *input, HandleObject templateObj) {
         return new MNewStringObject(input, templateObj);
     }
 
     MDefinition *input() const {
         return getOperand(0);
     }
-    StringObject *templateObj() const {
-        return &templateObj_->asString();
-    }
+
+    StringObject *templateObj() const;
+
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
 // Node that represents that a script has begun executing. This comes at the
 // start of the function and is called once per function (including inline
 // ones)
--- a/toolkit/components/passwordmgr/test/test_prompt_async.html
+++ b/toolkit/components/passwordmgr/test/test_prompt_async.html
@@ -76,17 +76,17 @@
                                          .createInstance(Ci.nsILoginInfo);
                 login.init(host, null, realm, user, pass, "", "");
                 pwmgr.addLogin(login);
                 logins.push(login);
             }
 
             var mozproxy = "moz-proxy://" +
                            SpecialPowers.wrap(pi).host + ":" +
-	                   SpecialPowers.wrap(pi).port;
+                           SpecialPowers.wrap(pi).port;
 
             addLogin(mozproxy, "proxy_realm",
                      "proxy_user", "proxy_pass");
             addLogin(mozproxy, "proxy_realm2",
                      "proxy_user2", "proxy_pass2");
             addLogin(mozproxy, "proxy_realm3",
                      "proxy_user3", "proxy_pass3");
             addLogin(mozproxy, "proxy_realm4",
@@ -120,17 +120,17 @@
             SimpleTest.finish();
         }
 
 	var resolveCallback = {
 	QueryInterface : function (iid) {
 	const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports];
 
         if (!interfaces.some( function(v) { return iid.equals(v) } ))
-	  throw Components.results.NS_ERROR_NO_INTERFACE;
+          throw Components.results.NS_ERROR_NO_INTERFACE;
 	  return this;
 	},
 
 	onProxyAvailable : function (req, uri, pi, status) {
           initLogins(pi);
           doTest(testNum);
 	}
 	};
--- a/toolkit/content/tests/chrome/test_bug331215.xul
+++ b/toolkit/content/tests/chrome/test_bug331215.xul
@@ -24,20 +24,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 </pre>
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
 /** Test for Bug 331215 **/
-todo(false, "disabled - to be fixed in bug 443763");
-/*
-XXX disabled - to be fixed in bug 443763
+
 SimpleTest.waitForExplicitFinish();
 window.open("bug331215_window.xul", "331215test", 
             "chrome,width=600,height=600");
-*/
 
 ]]>
 </script>
 
 </window>
--- a/toolkit/mozapps/update/common/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -57,16 +57,18 @@
 #define WRITE_ERROR_SHARING_VIOLATION 36
 #define WRITE_ERROR_CALLBACK_APP 37
 #define INVALID_UPDATER_STATUS_CODE 38
 #define UNEXPECTED_BZIP_ERROR 39
 #define UNEXPECTED_MAR_ERROR 40
 #define UNEXPECTED_BSPATCH_ERROR 41
 #define UNEXPECTED_FILE_OPERATION_ERROR 42
 #define FILESYSTEM_MOUNT_READWRITE_ERROR 43
+#define FOTA_GENERAL_ERROR 44
+#define FOTA_UNKNOWN_ERROR 45
 
 // The following error codes are only used by updater.exe
 // when a fallback key exists and XPCShell tests are being run.
 #define FALLBACKKEY_UNKNOWN_ERROR 100
 #define FALLBACKKEY_REGPATH_ERROR 101
 #define FALLBACKKEY_NOKEY_ERROR 102
 #define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
 #define FALLBACKKEY_LAUNCH_ERROR 104
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -82,17 +82,17 @@ interface nsIUpdatePatch : nsISupports
  * the current application - this update may have several available patches
  * from which one must be selected to download and install, for example we
  * might select a binary difference patch first and attempt to apply that,
  * then if the application process fails fall back to downloading a complete
  * file-replace patch. This object also contains information about the update
  * that the front end and other application services can use to learn more
  * about what is going on.
  */
-[scriptable, uuid(2379e2e1-8eab-4084-8d8c-94ffeee56804)]
+[scriptable, uuid(b10bbf29-5a54-4e1e-aa64-c4e4e5819a52)]
 interface nsIUpdate : nsISupports
 {
   /**
    * The type of update:
    *   "major"  A major new version of the Application
    *   "minor"  A minor update to the Application (e.g. security update)
    */
   attribute AString type;
@@ -193,16 +193,22 @@ interface nsIUpdate : nsISupports
   /**
    * Whether or not the update is a security update or not. If this is true,
    * then we present more serious sounding user interface messages to the
    * user.
    */
   attribute boolean isSecurityUpdate;
 
   /**
+   * Whether or not the update being downloaded is an OS update. This is
+   * generally only possible in Gonk right now.
+   */
+  attribute boolean isOSUpdate;
+
+  /**
    * When the update was installed.
    */
   attribute long long installDate;
 
   /**
    * A message associated with this update, if any.
    */
   attribute AString statusText;
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -136,16 +136,18 @@ const WRITE_ERROR_ACCESS_DENIED        =
 const WRITE_ERROR_SHARING_VIOLATION    = 36;
 const WRITE_ERROR_CALLBACK_APP         = 37;
 const INVALID_UPDATER_STATUS_CODE      = 38;
 const UNEXPECTED_BZIP_ERROR            = 39;
 const UNEXPECTED_MAR_ERROR             = 40;
 const UNEXPECTED_BSPATCH_ERROR         = 41;
 const UNEXPECTED_FILE_OPERATION_ERROR  = 42;
 const FILESYSTEM_MOUNT_READWRITE_ERROR = 43;
+const FOTA_GENERAL_ERROR               = 44;
+const FOTA_UNKNOWN_ERROR               = 45;
 
 const CERT_ATTR_CHECK_FAILED_NO_UPDATE  = 100;
 const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 
 const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
 const DOWNLOAD_BACKGROUND_INTERVAL  = 600;    // seconds
 const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
@@ -953,17 +955,19 @@ function readStringFromFile(file) {
 }
 
 function handleUpdateFailure(update, errorCode) {
   update.errorCode = parseInt(errorCode);
   if (update.errorCode == WRITE_ERROR || 
       update.errorCode == WRITE_ERROR_ACCESS_DENIED ||
       update.errorCode == WRITE_ERROR_SHARING_VIOLATION ||
       update.errorCode == WRITE_ERROR_CALLBACK_APP ||
-      update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR) {
+      update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR ||
+      update.errorCode == FOTA_GENERAL_ERROR ||
+      update.errorCode == FOTA_UNKNOWN_ERROR) {
     Cc["@mozilla.org/updates/update-prompt;1"].
       createInstance(Ci.nsIUpdatePrompt).
       showUpdateError(update);
     writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
     return true;
   }
 
   if (update.errorCode == ELEVATION_CANCELED) {
@@ -1167,16 +1171,17 @@ UpdatePatch.prototype = {
  *          An <update> element to initialize this object with
  * @throws if the update contains no patches
  * @constructor
  */
 function Update(update) {
   this._properties = {};
   this._patches = [];
   this.isCompleteUpdate = false;
+  this.isOSUpdate = false;
   this.showPrompt = false;
   this.showSurvey = false;
   this.showNeverForVersion = false;
   this.channel = "default"
 
   // Null <update>, assume this is a message container and do no
   // further initialization
   if (!update)
@@ -1227,16 +1232,18 @@ function Update(update) {
         this.appVersion = attr.value;
     }
     else if (attr.name == "installDate" && attr.value)
       this.installDate = parseInt(attr.value);
     else if (attr.name == "isCompleteUpdate")
       this.isCompleteUpdate = attr.value == "true";
     else if (attr.name == "isSecurityUpdate")
       this.isSecurityUpdate = attr.value == "true";
+    else if (attr.name == "isOSUpdate")
+      this.isOSUpdate = attr.value == "true";
     else if (attr.name == "showNeverForVersion")
       this.showNeverForVersion = attr.value == "true";
     else if (attr.name == "showPrompt")
       this.showPrompt = attr.value == "true";
     else if (attr.name == "showSurvey")
       this.showSurvey = attr.value == "true";
     else if (attr.name == "version") {
       // Prevent version from replacing displayVersion if displayVersion is
@@ -1365,16 +1372,17 @@ Update.prototype = {
     update.setAttribute("appVersion", this.appVersion);
     update.setAttribute("buildID", this.buildID);
     update.setAttribute("channel", this.channel);
     update.setAttribute("displayVersion", this.displayVersion);
     // for backwards compatibility in case the user downgrades
     update.setAttribute("extensionVersion", this.appVersion);
     update.setAttribute("installDate", this.installDate);
     update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
+    update.setAttribute("isOSUpdate", this.isOSUpdate);
     update.setAttribute("name", this.name);
     update.setAttribute("serviceURL", this.serviceURL);
     update.setAttribute("showNeverForVersion", this.showNeverForVersion);
     update.setAttribute("showPrompt", this.showPrompt);
     update.setAttribute("showSurvey", this.showSurvey);
     update.setAttribute("type", this.type);
     // for backwards compatibility in case the user downgrades
     update.setAttribute("version", this.displayVersion);
@@ -1577,16 +1585,39 @@ UpdateService.prototype = {
       } else { // We get here even if we don't have an update object
         LOG("UpdateService:_postUpdateProcessing - patch found in applying " +
             "state for the second time");
         cleanupActiveUpdate();
       }
       return;
     }
 
+#ifdef MOZ_WIDGET_GONK
+    if (status == STATE_APPLIED && update && update.isOSUpdate) {
+      // In gonk, we need to check for OS update status after startup, since
+      // the recovery partition won't write to update.status for us
+      var recoveryService = Cc["@mozilla.org/recovery-service;1"].
+                            getService(Ci.nsIRecoveryService);
+
+      var fotaStatus = recoveryService.getFotaUpdateStatus();
+      switch (fotaStatus) {
+        case Ci.nsIRecoveryService.FOTA_UPDATE_SUCCESS:
+          status = STATE_SUCCEEDED;
+          break;
+        case Ci.nsIRecoveryService.FOTA_UPDATE_FAIL:
+          status = STATE_FAILED + ": " + FOTA_GENERAL_ERROR;
+          break;
+        case Ci.nsIRecoveryService.FOTA_UPDATE_UNKNOWN:
+        default:
+          status = STATE_FAILED + ": " + FOTA_UNKNOWN_ERROR;
+          break;
+      }
+    }
+#endif
+
     if (!update)
       update = new Update(null);
 
     var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
                    createInstance(Ci.nsIUpdatePrompt);
 
     update.state = status;
     this._sendStatusCodeTelemetryPing(status);
@@ -3472,17 +3503,19 @@ UpdatePrompt.prototype = {
       return;
 
     // In some cases, we want to just show a simple alert dialog:
     if (update.state == STATE_FAILED &&
         (update.errorCode == WRITE_ERROR ||
          update.errorCode == WRITE_ERROR_ACCESS_DENIED ||
          update.errorCode == WRITE_ERROR_SHARING_VIOLATION ||
          update.errorCode == WRITE_ERROR_CALLBACK_APP ||
-         update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR)) {
+         update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR ||
+         update.errorCode == FOTA_GENERAL_ERROR ||
+         update.errorCode == FOTA_UNKNOWN_ERROR)) {
       var title = gUpdateBundle.GetStringFromName("updaterIOErrorTitle");
       var text = gUpdateBundle.formatStringFromName("updaterIOErrorMsg",
                                                     [Services.appinfo.name,
                                                      Services.appinfo.name], 2);
       Services.ww.getNewPrompter(null).alert(title, text);
       return;
     }
 
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -267,16 +267,17 @@ private:
 
 static NS_tchar* gSourcePath;
 static NS_tchar gDestinationPath[MAXPATHLEN];
 static ArchiveReader gArchiveReader;
 static bool gSucceeded = false;
 static bool sBackgroundUpdate = false;
 static bool sReplaceRequest = false;
 static bool sUsingService = false;
+static bool sIsOSUpdate = false;
 
 #ifdef XP_WIN
 // The current working directory specified in the command line.
 static NS_tchar* gDestPath;
 static NS_tchar gCallbackRelPath[MAXPATHLEN];
 static NS_tchar gCallbackBackupPath[MAXPATHLEN];
 #endif
 
@@ -2087,17 +2088,17 @@ UpdateThreadFunc(void *param)
         }
 
         rv = gArchiveReader.VerifyProductInformation(MARStrings.MARChannelID,
                                                      MOZ_APP_VERSION);
       }
     }
 #endif
 
-    if (rv == OK && sBackgroundUpdate) {
+    if (rv == OK && sBackgroundUpdate && !sIsOSUpdate) {
       rv = CopyInstallDirToDestDir();
     }
 
     if (rv == OK) {
       rv = DoUpdate();
       gArchiveReader.Close();
     }
   }
@@ -2252,16 +2253,21 @@ int NS_main(int argc, NS_tchar **argv)
       sBackgroundUpdate = true;
     } else if (NS_tstrstr(argv[3], NS_T("/replace"))) {
       // We're processing a request to replace a version of the application
       // with an updated version applied in the background.
       sReplaceRequest = true;
     }
   }
 
+  if (getenv("MOZ_OS_UPDATE")) {
+    sIsOSUpdate = true;
+    putenv(const_cast<char*>("MOZ_OS_UPDATE="));
+  }
+
   if (sReplaceRequest) {
     // If we're attempting to replace the application, try to append to the
     // log generated when staging the background update.
     NS_tchar installDir[MAXPATHLEN];
     if (!GetInstallationDir(installDir)) {
       fprintf(stderr, "Could not get the installation directory\n");
       return 1;
     }
@@ -3380,16 +3386,20 @@ GetManifestContents(const NS_tchar *mani
   free(mbuf);
 
   return wrb;
 #endif
 }
 
 int AddPreCompleteActions(ActionList *list)
 {
+  if (sIsOSUpdate) {
+    return OK;
+  }
+
   NS_tchar *rb = GetManifestContents(NS_T("precomplete"));
   if (rb == NULL) {
     LOG(("AddPreCompleteActions: error getting contents of precomplete " \
          "manifest\n"));
     // Applications aren't required to have a precomplete manifest yet.
     return OK;
   }
 
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -4,17 +4,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdlib.h>
 #include <stdio.h>
 #include "nsUpdateDriver.h"
 #include "nsXULAppAPI.h"
 #include "nsAppRunner.h"
+#include "nsIWritablePropertyBag.h"
 #include "nsIFile.h"
+#include "nsIVariant.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "prproces.h"
 #include "prlog.h"
 #include "prenv.h"
 #include "nsVersionComparator.h"
 #include "nsXREDirProvider.h"
 #include "SpecialSystemDirectory.h"
@@ -552,16 +554,58 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIF
   LaunchChildMac(argc, argv);
   exit(0);
 #else
   PR_CreateProcessDetached(updaterPath.get(), argv, NULL, NULL);
   exit(0);
 #endif
 }
 
+#if defined(MOZ_WIDGET_GONK)
+static nsresult
+GetOSApplyToDir(nsACString& applyToDir)
+{
+  nsCOMPtr<nsIProperties> ds =
+    do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+  NS_ASSERTION(ds, "Can't get directory service");
+
+  nsCOMPtr<nsIFile> osApplyToDir;
+  nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile),
+                        getter_AddRefs(osApplyToDir));
+  NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the OS applyTo dir");
+
+  return osApplyToDir->GetNativePath(applyToDir);
+}
+
+static void
+SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir)
+{
+  nsresult rv;
+  nsCOMPtr<nsIWritablePropertyBag> updateProperties =
+    do_QueryInterface(update, &rv);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIWritableVariant> variant =
+    do_CreateInstance("@mozilla.org/variant;1", &rv);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  rv = variant->SetAsACString(osApplyToDir);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant);
+}
+#endif
+
 /**
  * Apply an update, possibly in the background.
  *
  * @param greDir the GRE dir
  * @param updateDir the update root dir
  * @param statusFile the update.status file
  * @param appDir the app dir
  * @param appArgc the number of args to the application
@@ -569,17 +613,18 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIF
  * @param restart if true, apply the update in the foreground and restart the
  *                application when done.  otherwise, apply the update in the
  *                background and don't restart the application.
  * @param outpid out parameter holding the handle to the updater application for
  *               background updates.
  */
 static void
 ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
-            nsIFile *appDir, int appArgc, char **appArgv, bool restart,
+            nsIFile *appDir, int appArgc, char **appArgv,
+            bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
             ProcessType *outpid)
 {
   nsresult rv;
 
   // Steps:
   //  - mark update as 'applying'
   //  - copy updater into update dir
   //  - run updater w/ appDir as the current working dir
@@ -667,19 +712,36 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
   }
 #if defined(XP_WIN)
   nsAutoString applyToDirW;
   rv = updatedDir->GetPath(applyToDirW);
 
   NS_ConvertUTF16toUTF8 applyToDir(applyToDirW);
 #else
   nsAutoCString applyToDir;
-  rv = updatedDir->GetNativePath(applyToDir);
+
+#if defined(MOZ_WIDGET_GONK)
+  if (isOSUpdate) {
+    if (!osApplyToDir) {
+      return;
+    }
+
+    rv = osApplyToDir->GetNativePath(applyToDir);
+  } else {
+#endif // defined(MOZ_WIDGET_GONK)
+
+    rv = updatedDir->GetNativePath(applyToDir);
+
+#if defined(MOZ_WIDGET_GONK)
+  }
+#endif // defined(MOZ_WIDGET_GONK)
+
+#endif // defined(XP_WIN)
 #endif
-#endif
+
   if (NS_FAILED(rv))
     return;
 
 #if defined(XP_WIN)
   nsAutoString updateDirPathW;
   rv = updateDir->GetPath(updateDirPathW);
 
   NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
@@ -737,16 +799,20 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
     argc = 4;
     argv[4] = NULL;
   }
 
   if (gSafeMode) {
     PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   }
 
+  if (isOSUpdate) {
+    PR_SetEnv("MOZ_OS_UPDATE=1");
+  }
+
   LOG(("spawning updater process [%s]\n", updaterPath.get()));
 
 #if defined(USE_EXECV)
   // Don't use execv for background updates.
   if (restart) {
     execv(updaterPath.get(), argv);
   } else {
     *outpid = PR_CreateProcess(updaterPath.get(), argv, NULL, NULL);
@@ -796,17 +862,18 @@ WaitForProcess(ProcessType pt)
     LOG(("Error while running the updater process, check update.log"));
   }
 #endif
 }
 
 nsresult
 ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
                int argc, char **argv, const char *appVersion,
-               bool restart, ProcessType *pid)
+               bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
+               ProcessType *pid)
 {
   nsresult rv;
 
   nsCOMPtr<nsIFile> updatesDir;
   rv = updRootDir->Clone(getter_AddRefs(updatesDir));
   if (NS_FAILED(rv))
     return rv;
 
@@ -860,17 +927,17 @@ ProcessUpdates(nsIFile *greDir, nsIFile 
     // Remove the update if the update application version file doesn't exist
     // or if the update's application version is less than the current
     // application version.
     if (!GetVersionFile(updatesDir, versionFile) ||
         IsOlderVersion(versionFile, appVersion)) {
       updatesDir->Remove(true);
     } else {
       ApplyUpdate(greDir, updatesDir, statusFile,
-                  appDir, argc, argv, restart, pid);
+                  appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid);
     }
     break;
   }
   case eAppliedUpdate:
   case eAppliedService:
     // An update was applied in the background, so we need to switch to using
     // it now.
     SwitchToUpdatedApp(greDir, updatesDir, statusFile,
@@ -880,16 +947,18 @@ ProcessUpdates(nsIFile *greDir, nsIFile 
     // We don't need to do any special processing here, we'll just continue to
     // startup the application.
     break;
   }
 
   return NS_OK;
 }
 
+
+
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsUpdateProcessor, nsIUpdateProcessor)
 
 nsUpdateProcessor::nsUpdateProcessor()
   : mUpdaterPID(0)
 {
 }
 
 NS_IMETHODIMP
@@ -975,35 +1044,59 @@ nsUpdateProcessor::ProcessUpdate(nsIUpda
   } else {
     MOZ_ASSERT(argc == 1); // see above
     const size_t length = binPath.Length();
     mInfo.mArgv[0] = new char[length + 1];
     strcpy(mInfo.mArgv[0], binPath.get());
   }
   mInfo.mAppVersion = appVersion;
 
+#if defined(MOZ_WIDGET_GONK)
+  bool isOSUpdate;
+  if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) &&
+      isOSUpdate) {
+    nsAutoCString osApplyToDir;
+
+    // This needs to be done on the main thread, so we pass it along in
+    // BackgroundThreadInfo
+    nsresult rv = GetOSApplyToDir(osApplyToDir);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the OS apply to dir");
+
+    SetOSApplyToDir(aUpdate, osApplyToDir);
+
+    mInfo.mIsOSUpdate = true;
+    rv = NS_NewNativeLocalFile(osApplyToDir, false,
+                               getter_AddRefs(mInfo.mOSApplyToDir));
+    NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create nsIFile for OS apply to dir");
+  }
+#endif
+
   mUpdate = aUpdate;
 
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
   return NS_NewThread(getter_AddRefs(mProcessWatcher),
                       NS_NewRunnableMethod(this, &nsUpdateProcessor::StartBackgroundUpdate));
 }
 
+
+
 void
 nsUpdateProcessor::StartBackgroundUpdate()
 {
   NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread");
 
   nsresult rv = ProcessUpdates(mInfo.mGREDir,
                                mInfo.mAppDir,
                                mInfo.mUpdateRoot,
                                mInfo.mArgc,
                                mInfo.mArgv,
                                mInfo.mAppVersion.get(),
                                false,
+                               mInfo.mIsOSUpdate,
+                               mInfo.mOSApplyToDir,
                                &mUpdaterPID);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   if (mUpdaterPID) {
     // Track the state of the background updater process
     rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
     NS_ENSURE_SUCCESS_VOID(rv);
   } else {
--- a/toolkit/xre/nsUpdateDriver.h
+++ b/toolkit/xre/nsUpdateDriver.h
@@ -48,16 +48,18 @@ class nsIFile;
  *
  * This function does not modify appDir.
  */
 NS_HIDDEN_(nsresult) ProcessUpdates(nsIFile *greDir, nsIFile *appDir,
                                     nsIFile *updRootDir,
                                     int argc, char **argv,
                                     const char *appVersion,
                                     bool restart = true,
+                                    bool isOSUpdate = false,
+                                    nsIFile *osApplyToDir = nullptr,
                                     ProcessType *pid = nullptr);
 
 #ifdef MOZ_UPDATER
 // The implementation of the update processor handles the task of loading the
 // updater application in the background for applying an update.
 // XXX ehsan this is living in this file in order to make use of the existing
 // stuff here, we might want to move it elsewhere in the future.
 class nsUpdateProcessor MOZ_FINAL : public nsIUpdateProcessor
@@ -67,31 +69,34 @@ public:
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIUPDATEPROCESSOR
 
 private:
   struct BackgroundUpdateInfo {
     BackgroundUpdateInfo()
       : mArgc(0),
-        mArgv(nullptr)
+        mArgv(nullptr),
+        mIsOSUpdate(false)
     {}
     ~BackgroundUpdateInfo() {
       for (int i = 0; i < mArgc; ++i) {
         delete[] mArgv[i];
       }
       delete[] mArgv;
     }
 
     nsCOMPtr<nsIFile> mGREDir;
     nsCOMPtr<nsIFile> mAppDir;
     nsCOMPtr<nsIFile> mUpdateRoot;
+    nsCOMPtr<nsIFile> mOSApplyToDir;
     int mArgc;
     char **mArgv;
     nsAutoCString mAppVersion;
+    bool mIsOSUpdate;
   };
 
 private:
   void StartBackgroundUpdate();
   void WaitForProcess();
   void UpdateDone();
   void ShutdownWatcherThread();
 
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -120,16 +120,23 @@
  * Windows: Documents and Settings\<User>\Local Settings\Application Data\
  *          <Vendor>\<Application>\<relative path to app dir from Program Files>
  * If appDir is not under the Program Files, directory service will fail.
  * Callers should fallback to appDir.
  */
 #define XRE_UPDATE_ROOT_DIR "UpdRootD"
 
 /**
+ * A directory service key which provides the directory where an OS update is
+*  applied.
+ * At present this is supported only in Gonk.
+ */
+#define XRE_OS_UPDATE_APPLY_TO_DIR "OSUpdApplyToD"
+
+/**
  * Platform flag values for XRE_main.
  *
  * XRE_MAIN_FLAG_USE_METRO - On Windows, use the winrt backend. Defaults
  * to win32 backend.
  */
 #define XRE_MAIN_FLAG_USE_METRO 0x01
 
 /**