Merge m-c to fx-team.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 05 Aug 2013 20:02:16 -0400
changeset 149511 a184b660b9082adb254505bd8e75c7908b90b0d9
parent 149510 cc0ced32bac5181f6429a60bd9a985a6ad9510e5 (current diff)
parent 149391 ff6bb97c0c74039997558be92cb131e9dca4ed2b (diff)
child 149512 4db723a74092edbceb3190def2e2c13665557299
push idunknown
push userunknown
push dateunknown
milestone26.0a1
Merge m-c to fx-team.
dom/tests/mochitest/webapps/bug_765063.xul
--- a/.hgtags
+++ b/.hgtags
@@ -89,8 +89,9 @@ 6fdf9985acfe6f939da584b2559464ab22264fe7
 fd72dbbd692012224145be1bf13df1d7675fd277 FIREFOX_AURORA_17_BASE
 2704e441363fe2a48e992dfac694482dfd82664a FIREFOX_AURORA_18_BASE
 cf8750abee06cde395c659f8ecd8ae019d7512e3 FIREFOX_AURORA_19_BASE
 5bb309998e7050c9ee80b0147de1e473f008e221 FIREFOX_AURORA_20_BASE
 cc37417e2c284aed960f98ffa479de4ccdd5c7c3 FIREFOX_AURORA_21_BASE
 1c070ab0f9db59f13423b9c1db60419f7a9098f9 FIREFOX_AURORA_22_BASE
 d7ce9089999719d5186595d160f25123a4e63e39 FIREFOX_AURORA_23_BASE
 8d3810543edccf4fbe458178b88dd4a6e420b010 FIREFOX_AURORA_24_BASE
+ad0ae007aa9e03cd74e9005cd6652e544139b3b5 FIREFOX_AURORA_25_BASE
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "5e7a8bbe525c0a43852770665ce9498fdb93ea81", 
+    "revision": "bfebfb81d21bd6a4fb30f11044e6531e8bfaea3a", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=25.0a1
+MOZ_APP_VERSION=26.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=1.2.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-25.0a1
+26.0a1
--- a/browser/modules/webappsUI.jsm
+++ b/browser/modules/webappsUI.jsm
@@ -29,29 +29,37 @@ this.webappsUI = {
   },
 
   observe: function webappsUI_observe(aSubject, aTopic, aData) {
     let data = JSON.parse(aData);
     data.mm = aSubject;
 
     switch(aTopic) {
       case "webapps-ask-install":
-        let [chromeWin, browser] = this._getBrowserForId(data.oid);
-        if (chromeWin)
-          this.doInstall(data, browser, chromeWin);
+        let win = this._getWindowForId(data.oid);
+        if (win && win.location.href == data.from) {
+          this.doInstall(data, win);
+        }
         break;
       case "webapps-launch":
         WebappOSUtils.launch(data);
         break;
       case "webapps-uninstall":
         WebappOSUtils.uninstall(data);
         break;
     }
   },
 
+  _getWindowForId: function(aId) {
+    let someWindow = Services.wm.getMostRecentWindow(null);
+    return someWindow && someWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIDOMWindowUtils)
+                                   .getOuterWindowWithId(aId);
+  },
+
   openURL: function(aUrl, aOrigin) {
     let browserEnumerator = Services.wm.getEnumerator("navigator:browser");
     let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
     // Check each browser instance for our URL
     let found = false;
     while (!found && browserEnumerator.hasMoreElements()) {
       let browserWin = browserEnumerator.getNext();
@@ -81,72 +89,64 @@ this.webappsUI = {
         let tab = browser.addTab(aUrl);
         browser.pinTab(tab);
         browser.selectedTab = tab;
         ss.setTabValue(tab, "appOrigin", aOrigin);
       }
     }
   },
 
-  _getBrowserForId: function(aId) {
-    let content = Services.wm.getOuterWindowWithId(aId);
-    if (content) {
-      let browser = content.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShell).chromeEventHandler;
-      let win = browser.ownerDocument.defaultView;
-      return [win, browser];
-    }
-
-    return [null, null];
-  },
-
-  doInstall: function(aData, aBrowser, aWindow) {
-    let bundle = aWindow.gNavigatorBundle;
+  doInstall: function(aData, aWindow) {
+    let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIDocShell)
+                         .chromeEventHandler;
+    let chromeWin = browser.ownerDocument.defaultView;
+    let bundle = chromeWin.gNavigatorBundle;
 
     let mainAction = {
       label: bundle.getString("webapps.install"),
       accessKey: bundle.getString("webapps.install.accesskey"),
       callback: function() {
         let app = WebappsInstaller.init(aData);
 
         if (app) {
           let localDir = null;
           if (app.appProfile) {
             localDir = app.appProfile.localDir;
           }
 
           DOMApplicationRegistry.confirmInstall(aData, false, localDir, null,
             function (aManifest) {
               if (WebappsInstaller.install(aData, aManifest)) {
-                installationSuccessNotification(aData, app, aWindow);
+                installationSuccessNotification(aData, app, chromeWin);
               }
             }
           );
         } else {
           DOMApplicationRegistry.denyInstall(aData);
         }
       }
     };
 
-    let requestingURI = aWindow.makeURI(aData.from);
+    let requestingURI = chromeWin.makeURI(aData.from);
     let jsonManifest = aData.isPackage ? aData.app.updateManifest : aData.app.manifest;
     let manifest = new ManifestHelper(jsonManifest, aData.app.origin);
 
     let host;
     try {
       host = requestingURI.host;
     } catch(e) {
       host = requestingURI.spec;
     }
 
     let message = bundle.getFormattedString("webapps.requestInstall",
                                             [manifest.name, host], 2);
 
-    aWindow.PopupNotifications.show(aBrowser, "webapps-install", message,
+    chromeWin.PopupNotifications.show(browser, "webapps-install", message,
                                     "webapps-notification-icon", mainAction);
 
   }
 }
 
 function installationSuccessNotification(aData, app, aWindow) {
   let launcher = {
     observe: function(aSubject, aTopic) {
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-25.0a1
+26.0a1
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -20,17 +20,17 @@
   The first argument to GK_ATOM is the C++ name of the atom
   The second argument it GK_ATOM is the string value of the atom
 */
 
 // OUTPUT_CLASS=nsGkAtoms
 // MACRO_NAME=GK_ATOM
 #ifdef small
 #undef small
-#endif 
+#endif
 
 //---------------------------------------------------------------------------
 // Generic atoms
 //---------------------------------------------------------------------------
 
 GK_ATOM(_empty, "")
 GK_ATOM(moz, "_moz")
 GK_ATOM(mozframetype, "mozframetype")
@@ -761,16 +761,18 @@ GK_ATOM(onpaint, "onpaint")
 GK_ATOM(onpairedstatuschanged, "onpairedstatuschanged")
 GK_ATOM(onpaste, "onpaste")
 GK_ATOM(onpopuphidden, "onpopuphidden")
 GK_ATOM(onpopuphiding, "onpopuphiding")
 GK_ATOM(onpopupshowing, "onpopupshowing")
 GK_ATOM(onpopupshown, "onpopupshown")
 GK_ATOM(onreadystatechange, "onreadystatechange")
 GK_ATOM(onreceived, "onreceived")
+GK_ATOM(onremoteheld, "onremoteheld")
+GK_ATOM(onremoteresumed, "onremoteresumed")
 GK_ATOM(onretrieving, "onretrieving")
 GK_ATOM(onRequest, "onRequest")
 GK_ATOM(onreset, "onreset")
 GK_ATOM(onresuming, "onresuming")
 GK_ATOM(onMozBeforeResize, "onMozBeforeResize")
 GK_ATOM(onresize, "onresize")
 GK_ATOM(onscostatuschanged, "onscostatuschanged")
 GK_ATOM(onscroll, "onscroll")
--- a/content/base/test/test_mutationobservers.html
+++ b/content/base/test/test_mutationobservers.html
@@ -482,18 +482,22 @@ function testModalDialog() {
       is(records[0].removedNodes.length, 2, "Should have got removedNodes");
       is(records[0].addedNodes.length, 1, "Should have got addedNodes");
       observer.disconnect();
       m = null;
       didHandleCallback = true;
     });
   m.observe(div, { childList: true });
   div.innerHTML = "<span><span>foo</span></span>";
-  window.showModalDialog("mutationobserver_dialog.html");
-  ok(didHandleCallback, "Should have called the callback while showing modal dialog!");
+  try {
+    window.showModalDialog("mutationobserver_dialog.html");
+    ok(didHandleCallback, "Should have called the callback while showing modal dialog!");
+  } catch(e) {
+    todo(false, "showModalDialog not implemented on this platform");
+  }
   then(testTakeRecords);
 }
 
 function testTakeRecords() {
   var s = "<span>1</span><span>2</span>";
   div.innerHTML = s;
   var takenRecords;
   m = new M(function(records, observer) {
--- a/content/media/encoder/TrackEncoder.cpp
+++ b/content/media/encoder/TrackEncoder.cpp
@@ -62,26 +62,34 @@ AudioTrackEncoder::NotifyQueuedTrackChan
   }
 }
 
 void
 AudioTrackEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
 {
   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
   LOG("[AudioTrackEncoder]: NotifyRemoved.");
+  NotifyEndOfStream();
+}
 
+void
+AudioTrackEncoder::NotifyEndOfStream()
+{
   // If source audio chunks are completely silent till the end of encoding,
   // initialize the encoder with default channel counts and sampling rate, and
   // append this many null data to the segment of track encoder.
-  if (!mInitialized && mSilentDuration > 0) {
+  if (!mCanceled && !mInitialized && mSilentDuration > 0) {
     Init(DEFAULT_CHANNELS, DEFAULT_SAMPLING_RATE);
     mRawSegment->AppendNullData(mSilentDuration);
     mSilentDuration = 0;
   }
-  NotifyEndOfStream();
+
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  mEndOfStream = true;
+  mReentrantMonitor.NotifyAll();
 }
 
 nsresult
 AudioTrackEncoder::AppendAudioSegment(MediaSegment* aSegment)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   AudioSegment* audio = static_cast<AudioSegment*>(aSegment);
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -125,22 +125,17 @@ protected:
    * method which is waiting for more data from mRawSegment.
    */
   nsresult AppendAudioSegment(MediaSegment* aSegment);
 
   /**
    * Notifies the audio encoder that we have reached the end of source stream,
    * and wakes up mReentrantMonitor if encoder is waiting for more track data.
    */
-  void NotifyEndOfStream()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    mEndOfStream = true;
-    mReentrantMonitor.NotifyAll();
-  }
+  void NotifyEndOfStream();
 
   /**
    * Interleaves the track data and stores the result into aOutput. Might need
    * to up-mix or down-mix the channel data if the channels number of this chunk
    * is different from mChannels. The channel data from aChunk might be modified
    * by up-mixing.
    */
   void InterleaveTrackData(AudioChunk& aChunk, int32_t aDuration,
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -1363,16 +1363,17 @@ BluetoothHfpManager::HandleCallStateChan
       break;
     case nsITelephonyProvider::CALL_STATE_CONNECTED:
       switch (prevCallState) {
         case nsITelephonyProvider::CALL_STATE_INCOMING:
         case nsITelephonyProvider::CALL_STATE_DISCONNECTED:
           // Incoming call, no break
           sStopSendingRingFlag = true;
           ConnectSco();
+        case nsITelephonyProvider::CALL_STATE_DIALING:
         case nsITelephonyProvider::CALL_STATE_ALERTING:
           // Outgoing call
           UpdateCIND(CINDType::CALL, CallState::IN_PROGRESS, aSend);
           UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, aSend);
           break;
         default:
           NS_WARNING("Not handling state changed");
       }
--- a/dom/bluetooth/BluetoothTelephonyListener.cpp
+++ b/dom/bluetooth/BluetoothTelephonyListener.cpp
@@ -59,16 +59,23 @@ TelephonyListener::EnumerateCallState(ui
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   hfp->HandleCallStateChanged(aCallIndex, aCallState, EmptyString(), aNumber,
                               aIsOutgoing, false);
   *aResult = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TelephonyListener::SupplementaryServiceNotification(int32_t aCallIndex,
+                                                    uint16_t aNotification)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TelephonyListener::NotifyError(int32_t aCallIndex,
                                const nsAString& aError)
 {
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
 
   if (aCallIndex > 0) {
     // In order to not miss any related call state transition.
     // It's possible that 3G network signal lost for unknown reason.
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -1,13 +1,14 @@
 /* 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 "base/basictypes.h"
+#include "mozilla/Assertions.h"
 #include "DOMCameraPreview.h"
 #include "CameraRecorderProfiles.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -15,24 +16,26 @@ using namespace mozilla::idl;
 
 CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
   : mCameraId(aCameraId)
   , mCameraThread(aCameraThread)
   , mWindowId(aWindowId)
   , mFileFormat()
   , mMaxMeteringAreas(0)
   , mMaxFocusAreas(0)
+  , mPreviewState(PREVIEW_STOPPED)
   , mDOMPreview(nullptr)
   , mAutoFocusOnSuccessCb(nullptr)
   , mAutoFocusOnErrorCb(nullptr)
   , mTakePictureOnSuccessCb(nullptr)
   , mTakePictureOnErrorCb(nullptr)
   , mOnShutterCb(nullptr)
   , mOnClosedCb(nullptr)
   , mOnRecorderStateChangeCb(nullptr)
+  , mOnPreviewStateChangeCb(nullptr)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
 
 CameraControlImpl::~CameraControlImpl()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 }
@@ -232,16 +235,30 @@ CameraControlImpl::Set(nsICameraRecorder
 
 nsresult
 CameraControlImpl::Get(nsICameraRecorderStateChange** aOnRecorderStateChange)
 {
   *aOnRecorderStateChange = mOnRecorderStateChangeCb;
   return NS_OK;
 }
 
+nsresult
+CameraControlImpl::Set(nsICameraPreviewStateChange* aOnPreviewStateChange)
+{
+  mOnPreviewStateChangeCb = new nsMainThreadPtrHolder<nsICameraPreviewStateChange>(aOnPreviewStateChange);
+  return NS_OK;
+}
+
+nsresult
+CameraControlImpl::Get(nsICameraPreviewStateChange** aOnPreviewStateChange)
+{
+  *aOnPreviewStateChange = mOnPreviewStateChangeCb;
+  return NS_OK;
+}
+
 already_AddRefed<RecorderProfileManager>
 CameraControlImpl::GetRecorderProfileManager()
 {
   return GetRecorderProfileManagerImpl();
 }
 
 void
 CameraControlImpl::Shutdown()
@@ -249,16 +266,17 @@ CameraControlImpl::Shutdown()
   DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
   mAutoFocusOnSuccessCb = nullptr;
   mAutoFocusOnErrorCb = nullptr;
   mTakePictureOnSuccessCb = nullptr;
   mTakePictureOnErrorCb = nullptr;
   mOnShutterCb = nullptr;
   mOnClosedCb = nullptr;
   mOnRecorderStateChangeCb = nullptr;
+  mOnPreviewStateChangeCb = nullptr;
 }
 
 void
 CameraControlImpl::OnShutterInternal()
 {
   DOM_CAMERA_LOGI("** SNAP **\n");
   if (mOnShutterCb.get()) {
     mOnShutterCb->HandleEvent();
@@ -322,16 +340,49 @@ CameraControlImpl::OnRecorderStateChange
 
   nsCOMPtr<nsIRunnable> onRecorderStateChange = new CameraRecorderStateChange(mOnRecorderStateChangeCb, aStateMsg, aStatus, aTrackNumber, mWindowId);
   nsresult rv = NS_DispatchToMainThread(onRecorderStateChange);
   if (NS_FAILED(rv)) {
     DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv);
   }
 }
 
+void
+CameraControlImpl::OnPreviewStateChange(PreviewState aNewState)
+{
+  if (aNewState == mPreviewState) {
+    DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
+    return;
+  }
+
+  nsString msg;
+  switch (aNewState) {
+    case PREVIEW_STOPPED:
+      msg = NS_LITERAL_STRING("stopped");
+      break;
+
+    case PREVIEW_STARTED:
+      msg = NS_LITERAL_STRING("started");
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("Preview state can only be PREVIEW_STOPPED or _STARTED!");
+  }
+
+  // const nsString& aStateMsg)
+  DOM_CAMERA_LOGI("OnPreviewStateChange: '%s'\n", NS_ConvertUTF16toUTF8(msg).get());
+  mPreviewState = aNewState;
+
+  nsCOMPtr<nsIRunnable> onPreviewStateChange = new CameraPreviewStateChange(mOnPreviewStateChangeCb, msg, mWindowId);
+  nsresult rv = NS_DispatchToMainThread(onPreviewStateChange);
+  if (NS_FAILED(rv)) {
+    DOM_CAMERA_LOGE("Failed to dispatch onPreviewStateChange event to main thread (%d)\n", rv);
+  }
+}
+
 nsresult
 CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
 {
   nsCOMPtr<nsIRunnable> getPreviewStreamTask = new GetPreviewStreamTask(this, aSize, onSuccess, onError);
   return mCameraThread->Dispatch(getPreviewStreamTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -66,16 +66,18 @@ public:
   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 Set(nsICameraRecorderStateChange* aOnRecorderStateChange);
   nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange);
+  nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange);
+  nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange);
 
   nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
   {
     return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
   }
 
   nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
   {
@@ -97,16 +99,22 @@ public:
   virtual nsresult PushParameters() = 0;
   virtual void Shutdown();
 
   bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
   void OnShutter();
   void OnClosed();
   void OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber);
 
+  enum PreviewState {
+    PREVIEW_STOPPED,
+    PREVIEW_STARTED
+  };
+  void OnPreviewStateChange(PreviewState aNewState);
+
   uint64_t GetWindowId()
   {
     return mWindowId;
   }
 
 protected:
   virtual ~CameraControlImpl();
 
@@ -127,16 +135,17 @@ protected:
   void OnClosedInternal();
 
   uint32_t            mCameraId;
   nsCOMPtr<nsIThread> mCameraThread;
   uint64_t            mWindowId;
   nsString            mFileFormat;
   uint32_t            mMaxMeteringAreas;
   uint32_t            mMaxFocusAreas;
+  PreviewState        mPreviewState;
 
   /**
    * 'mDOMPreview' is a raw pointer to the object that will receive incoming
    * preview frames.  This is guaranteed to be valid, or null.
    *
    * It is set by a call to StartPreview(), and set to null on StopPreview().
    * It is up to the caller to ensure that the object will not disappear
    * out from under this pointer--usually by calling NS_ADDREF().
@@ -145,16 +154,17 @@ protected:
 
   nsMainThreadPtrHandle<nsICameraAutoFocusCallback>   mAutoFocusOnSuccessCb;
   nsMainThreadPtrHandle<nsICameraErrorCallback>       mAutoFocusOnErrorCb;
   nsMainThreadPtrHandle<nsICameraTakePictureCallback> mTakePictureOnSuccessCb;
   nsMainThreadPtrHandle<nsICameraErrorCallback>       mTakePictureOnErrorCb;
   nsMainThreadPtrHandle<nsICameraShutterCallback>     mOnShutterCb;
   nsMainThreadPtrHandle<nsICameraClosedCallback>      mOnClosedCb;
   nsMainThreadPtrHandle<nsICameraRecorderStateChange> mOnRecorderStateChangeCb;
+  nsMainThreadPtrHandle<nsICameraPreviewStateChange>  mOnPreviewStateChangeCb;
 
 private:
   CameraControlImpl(const CameraControlImpl&) MOZ_DELETE;
   CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE;
 };
 
 // Error result runnable
 class CameraErrorResult : public nsRunnable
@@ -691,11 +701,37 @@ public:
 protected:
   nsMainThreadPtrHandle<nsICameraRecorderStateChange> mOnStateChangeCb;
   const nsString mStateMsg;
   int32_t mStatus;
   int32_t mTrackNumber;
   uint64_t mWindowId;
 };
 
+// Report that the preview stream state has changed.
+class CameraPreviewStateChange : public nsRunnable
+{
+public:
+  CameraPreviewStateChange(nsMainThreadPtrHandle<nsICameraPreviewStateChange> onStateChange, const nsString& aStateMsg, uint64_t aWindowId)
+    : mOnStateChangeCb(onStateChange)
+    , mStateMsg(aStateMsg)
+    , mWindowId(aWindowId)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
+      mOnStateChangeCb->HandleStateChange(mStateMsg);
+    }
+    return NS_OK;
+  }
+
+protected:
+  nsMainThreadPtrHandle<nsICameraPreviewStateChange> mOnStateChangeCb;
+  const nsString mStateMsg;
+  uint64_t mWindowId;
+};
+
 } // namespace mozilla
 
 #endif // DOM_CAMERA_CAMERACONTROLIMPL_H
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -234,16 +234,28 @@ nsDOMCameraControl::GetOnRecorderStateCh
   return mCameraControl->Get(aOnRecorderStateChange);
 }
 NS_IMETHODIMP
 nsDOMCameraControl::SetOnRecorderStateChange(nsICameraRecorderStateChange* aOnRecorderStateChange)
 {
   return mCameraControl->Set(aOnRecorderStateChange);
 }
 
+/* attribute nsICameraPreviewStateChange onPreviewStateChange; */
+NS_IMETHODIMP
+nsDOMCameraControl::GetOnPreviewStateChange(nsICameraPreviewStateChange** aOnPreviewStateChange)
+{
+  return mCameraControl->Get(aOnPreviewStateChange);
+}
+NS_IMETHODIMP
+nsDOMCameraControl::SetOnPreviewStateChange(nsICameraPreviewStateChange* aOnPreviewStateChange)
+{
+  return mCameraControl->Set(aOnPreviewStateChange);
+}
+
 /* [implicit_jscontext] void startRecording (in jsval aOptions, in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
 NS_IMETHODIMP
 nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
 {
   NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
   NS_ENSURE_TRUE(storageArea, NS_ERROR_INVALID_ARG);
 
   mozilla::idl::CameraStartRecordingOptions options;
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -671,16 +671,18 @@ nsGonkCameraControl::StartPreviewImpl(St
   if (mCameraHw->StartPreview() != OK) {
     DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__);
     return NS_ERROR_FAILURE;
   }
 
   if (aStartPreview->mDOMPreview) {
     mDOMPreview->Started();
   }
+
+  OnPreviewStateChange(PREVIEW_STARTED);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopPreviewInternal(bool aForced)
 {
   DOM_CAMERA_LOGI("%s: stopping preview (mDOMPreview=%p)\n", __func__, mDOMPreview);
 
@@ -689,16 +691,17 @@ nsGonkCameraControl::StopPreviewInternal
   if (mDOMPreview) {
     if (mCameraHw.get()) {
       mCameraHw->StopPreview();
     }
     mDOMPreview->Stopped(aForced);
     mDOMPreview = nullptr;
   }
 
+  OnPreviewStateChange(PREVIEW_STOPPED);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview)
 {
   return StopPreviewInternal();
 }
@@ -858,16 +861,20 @@ nsGonkCameraControl::TakePictureImpl(Tak
   }
 
   mDeferConfigUpdate = false;
   PushParameters();
 
   if (mCameraHw->TakePicture() != OK) {
     return NS_ERROR_FAILURE;
   }
+  
+  // In Gonk, taking a picture implicitly kills the preview stream,
+  // so we need to reflect that here.
+  OnPreviewStateChange(PREVIEW_STOPPED);
   return NS_OK;
 }
 
 nsresult
 nsGonkCameraControl::PushParametersImpl()
 {
   DOM_CAMERA_LOGI("Pushing camera parameters\n");
   RETURN_IF_NO_CAMERA_HW();
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -39,16 +39,18 @@ public:
   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 Set(nsICameraRecorderStateChange* aOnRecorderStateChange) = 0;
   virtual nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange) = 0;
+  virtual nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange) = 0;
+  virtual nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange) = 0;
   virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
   virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
   virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
   virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
   virtual uint32_t GetCameraId() = 0;
 
   virtual const char* GetParameter(const char* aKey) = 0;
   virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
--- a/dom/camera/nsIDOMCameraManager.idl
+++ b/dom/camera/nsIDOMCameraManager.idl
@@ -194,33 +194,39 @@ interface nsICameraClosedCallback : nsIS
 };
 
 [scriptable, function, uuid(550d675a-257d-4713-8b3d-0da53eba68fc)]
 interface nsICameraRecorderStateChange : nsISupports
 {
     void handleStateChange(in DOMString newState);
 };
 
+[scriptable, function, uuid(d1634592-43fd-4117-a2b2-419aec841cc4)]
+interface nsICameraPreviewStateChange : nsISupports
+{
+    void handleStateChange(in DOMString newState);
+};
+
 [scriptable, function, uuid(f84d607b-554c-413d-8810-cf848642765a)]
 interface nsICameraReleaseCallback : 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(c8e7418d-8913-4b66-bd9f-562fba627266)]
+[scriptable, uuid(74dc7f1f-c88f-4774-860b-44aef9de5dc8)]
 interface nsICameraControl : nsISupports
 {
     readonly attribute nsICameraCapabilities capabilities;
 
     /* one of the vales chosen from capabilities.effects;
        default is "none" */
     attribute DOMString         effect;
 
@@ -311,16 +317,22 @@ interface nsICameraControl : nsISupports
        recent call to get the camera. */
     attribute nsICameraClosedCallback onClosed;
 
     /* the function to call when the recorder changes state, either because
        the recording process encountered an error, or because one of the
        recording limits (see CameraStartRecordingOptions) was reached. */
     attribute nsICameraRecorderStateChange onRecorderStateChange;
 
+    /* the function to call when the preview stream is actually started and
+       stopped; this is usually used to enable and disable the camera UI,
+       since the low-level hardware often does not support taking pictures
+       unless the preview is running. */
+    attribute nsICameraPreviewStateChange onPreviewStateChange;
+
     /* 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/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -73,16 +73,17 @@ const RIL_IPC_MSG_NAMES = [
   "RIL:GetAvailableNetworks",
   "RIL:NetworkSelectionModeChanged",
   "RIL:SelectNetwork",
   "RIL:SelectNetworkAuto",
   "RIL:CallStateChanged",
   "RIL:VoicemailNotification",
   "RIL:VoicemailInfoChanged",
   "RIL:CallError",
+  "RIL:SuppSvcNotification",
   "RIL:CardLockResult",
   "RIL:CardLockRetryCount",
   "RIL:USSDReceived",
   "RIL:SendMMI:Return:OK",
   "RIL:SendMMI:Return:KO",
   "RIL:CancelMMI:Return:OK",
   "RIL:CancelMMI:Return:KO",
   "RIL:StkCommand",
@@ -1470,16 +1471,21 @@ RILContentHelper.prototype = {
       }
       case "RIL:CallError": {
         let data = msg.json.data;
         this._deliverEvent("_telephonyListeners",
                            "notifyError",
                            [data.callIndex, data.errorMsg]);
         break;
       }
+      case "RIL:SuppSvcNotification":
+        this._deliverEvent("_telephonyListeners",
+                           "supplementaryServiceNotification",
+                           [msg.json.callIndex, msg.json.notification]);
+        break;
       case "RIL:VoicemailNotification":
         this.handleVoicemailNotification(msg.json.data);
         break;
       case "RIL:VoicemailInfoChanged":
         this.updateInfo(msg.json.data, this.voicemailInfo);
         break;
       case "RIL:CardLockResult":
         if (msg.json.success) {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -204,16 +204,27 @@ function convertRILCallState(state) {
     case RIL.CALL_STATE_INCOMING:
     case RIL.CALL_STATE_WAITING:
       return nsITelephonyProvider.CALL_STATE_INCOMING;
     default:
       throw new Error("Unknown rilCallState: " + state);
   }
 }
 
+function convertRILSuppSvcNotification(notification) {
+  switch (notification) {
+    case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD:
+      return nsITelephonyProvider.NOTIFICATION_REMOTE_HELD;
+    case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED:
+      return nsITelephonyProvider.NOTIFICATION_REMOTE_RESUMED;
+    default:
+      throw new Error("Unknown rilSuppSvcNotification: " + notification);
+  }
+}
+
 /**
  * Fake nsIAudioManager implementation so that we can run the telephony
  * code in a non-Gonk build.
  */
 let FakeAudioManager = {
   microphoneMuted: false,
   masterVolume: 1.0,
   masterMuted: false,
@@ -934,16 +945,19 @@ RadioInterface.prototype = {
         break;
       case "enumerateCalls":
         // This one will handle its own notifications.
         this.handleEnumerateCalls(message);
         break;
       case "callError":
         this.handleCallError(message);
         break;
+      case "suppSvcNotification":
+        this.handleSuppSvcNotification(message);
+        break;
       case "iccOpenChannel":
         this.handleIccOpenChannel(message);
         break;
       case "iccCloseChannel":
         this.handleIccCloseChannel(message);
         break;
       case "iccExchangeAPDU":
         this.handleIccExchangeAPDU(message);
@@ -1806,16 +1820,24 @@ RadioInterface.prototype = {
    * Handle call error.
    */
   handleCallError: function handleCallError(message) {
     gMessageManager.sendTelephonyMessage("RIL:CallError",
                                          this.clientId, message);
   },
 
   /**
+   * Handle supplementary service notification.
+   */
+  handleSuppSvcNotification: function handleSuppSvcNotification(message) {
+    message.notification = convertRILSuppSvcNotification(message.notification);
+    this._sendTelephonyMessage("RIL:SuppSvcNotification", message);
+  },
+
+  /**
    * Handle WDP port push PDU. Constructor WDP bearer information and deliver
    * to WapPushManager.
    *
    * @param message
    *        A SMS message.
    */
   handleSmsWdpPortPush: function handleSmsWdpPortPush(message) {
     if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -2762,10 +2762,21 @@ this.MCC_TABLE_FOR_MNC_LENGTH_IS_3 = [
   "376",  // Turks and Caicos Islands
   "405",  // India
   "708",  // Honduras
   "722",  // Argentina
   "732",  // Colombia
   "750"   // Falkland Islands (Malvinas)
 ];
 
+// Supplementary service notifications, code2, as defined in 3GPP 27.007 7.17
+this.SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD = 2;
+this.SUPP_SVC_NOTIFICATION_CODE2_RETRIEVED = 3;
+
+this.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD    = "RemoteHeld";
+this.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED = "RemoteResumed";
+
+this.GECKO_SUPP_SVC_NOTIFICATION_FROM_CODE2 = {};
+GECKO_SUPP_SVC_NOTIFICATION_FROM_CODE2[SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD] = GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD;
+GECKO_SUPP_SVC_NOTIFICATION_FROM_CODE2[SUPP_SVC_NOTIFICATION_CODE2_RETRIEVED]   = GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED;
+
 // Allow this file to be imported via Components.utils.import().
 this.EXPORTED_SYMBOLS = Object.keys(this);
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -3843,16 +3843,61 @@ let RIL = {
         break;
       case DATACALL_ACTIVE_DOWN:
       case DATACALL_ACTIVE_UP:
         datacall.state = GECKO_NETWORK_STATE_CONNECTED;
         break;
     }
   },
 
+  _processSuppSvcNotification: function _processSuppSvcNotification(info) {
+    debug("handle supp svc notification: " + JSON.stringify(info));
+
+    let notification = null;
+    let callIndex = -1;
+
+    if (info.notificationType === 0) {
+      // MO intermediate result code. Refer to code1 defined in 3GPP 27.007
+      // 7.17.
+    } else if (info.notificationType === 1) {
+      // MT unsolicited result code. Refer to code2 defined in 3GPP 27.007 7.17.
+      switch (info.code) {
+        case SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD:
+        case SUPP_SVC_NOTIFICATION_CODE2_RETRIEVED:
+          notification = GECKO_SUPP_SVC_NOTIFICATION_FROM_CODE2[info.code];
+          break;
+        default:
+          // Notification type not supported.
+          return;
+      }
+
+      // Get the target call object for this notification.
+      let currentCallIndexes = Object.keys(this.currentCalls);
+      if (currentCallIndexes.length === 1) {
+        // Only one call exists. This should be the target.
+        callIndex = currentCallIndexes[0];
+      } else {
+        // Find the call in |currentCalls| by the given number.
+        if (info.number) {
+          for each (let currentCall in this.currentCalls) {
+            if (currentCall.number == info.number) {
+              callIndex = currentCall.callIndex;
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    let message = {rilMessageType: "suppSvcNotification",
+                   notification: notification,
+                   callIndex: callIndex};
+    this.sendChromeMessage(message);
+  },
+
   _processNetworks: function _processNetworks() {
     let strings = Buf.readStringList();
     let networks = [];
 
     for (let i = 0; i < strings.length; i += 4) {
       let network = {
         longName: strings[i],
         shortName: strings[i + 1],
@@ -6006,17 +6051,26 @@ RIL[UNSOLICITED_SIGNAL_STRENGTH] = funct
 };
 RIL[UNSOLICITED_DATA_CALL_LIST_CHANGED] = function UNSOLICITED_DATA_CALL_LIST_CHANGED(length) {
   if (RILQUIRKS_V5_LEGACY) {
     this.getDataCallList();
     return;
   }
   this[REQUEST_DATA_CALL_LIST](length, {rilRequestError: ERROR_SUCCESS});
 };
-RIL[UNSOLICITED_SUPP_SVC_NOTIFICATION] = null;
+RIL[UNSOLICITED_SUPP_SVC_NOTIFICATION] = function UNSOLICITED_SUPP_SVC_NOTIFICATION(length) {
+  let info = {};
+  info.notificationType = Buf.readUint32();
+  info.code = Buf.readUint32();
+  info.index = Buf.readUint32();
+  info.type = Buf.readUint32();
+  info.number = Buf.readString();
+
+  this._processSuppSvcNotification(info);
+};
 
 RIL[UNSOLICITED_STK_SESSION_END] = function UNSOLICITED_STK_SESSION_END() {
   this.sendChromeMessage({rilMessageType: "stksessionend"});
 };
 RIL[UNSOLICITED_STK_PROACTIVE_COMMAND] = function UNSOLICITED_STK_PROACTIVE_COMMAND() {
   this.processStkProactiveCommand();
 };
 RIL[UNSOLICITED_STK_EVENT_NOTIFY] = function UNSOLICITED_STK_EVENT_NOTIFY() {
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/tests/test_ril_worker_ssn.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
+
+function run_test() {
+  run_next_test();
+}
+
+function _getWorker() {
+  let _postedMessage;
+  let _worker = newWorker({
+    postRILMessage: function fakePostRILMessage(data) {
+    },
+    postMessage: function fakePostMessage(message) {
+      _postedMessage = message;
+    }
+  });
+  return {
+    get postedMessage() {
+      return _postedMessage;
+    },
+    get worker() {
+      return _worker;
+    }
+  };
+}
+
+add_test(function test_notification() {
+  let workerHelper = _getWorker();
+  let worker = workerHelper.worker;
+
+  function Call(callIndex, number) {
+    this.callIndex = callIndex;
+    this.number = number;
+  }
+
+  Call.prototype = {
+    state: CALL_STATE_DIALING,
+    //callIndex: 0,
+    toa: 0,
+    isMpty: false,
+    isMT: false,
+    als: 0,
+    isVoice: true,
+    isVoicePrivacy: false,
+    //number: null,
+    numberPresentation: 0,
+    name: null,
+    namePresentation: 0,
+    uusInfo: null
+  };
+
+  let oneCall = {
+    0: new Call(0, '00000')
+  };
+
+  let twoCalls = {
+    0: new Call(0, '00000'),
+    1: new Call(1, '11111')
+  };
+
+  function testNotification(calls, code, number, resultNotification,
+                            resultCallIndex) {
+
+    let testInfo = {calls: calls, code: code, number: number,
+                    resultNotification: resultNotification,
+                    resultCallIndex: resultCallIndex};
+    do_print('Test case info: ' + JSON.stringify(testInfo));
+
+    // Set current calls.
+    worker.RIL._processCalls(calls);
+
+    let notificationInfo = {
+      notificationType: 1,  // MT
+      code: code,
+      index: 0,
+      type: 0,
+      number: number
+    };
+
+    worker.RIL._processSuppSvcNotification(notificationInfo);
+
+    let postedMessage = workerHelper.postedMessage;
+    do_check_eq(postedMessage.rilMessageType, 'suppSvcNotification');
+    do_check_eq(postedMessage.notification, resultNotification);
+    do_check_eq(postedMessage.callIndex, resultCallIndex);
+
+    // Clear all existed calls.
+    worker.RIL._processCalls(null);
+  }
+
+  testNotification(oneCall, SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD, null,
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD, 0);
+
+  testNotification(oneCall, SUPP_SVC_NOTIFICATION_CODE2_RETRIEVED, null,
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED, 0);
+
+  testNotification(twoCalls, SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD, null,
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD, -1);
+
+  testNotification(twoCalls, SUPP_SVC_NOTIFICATION_CODE2_RETRIEVED, null,
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED, -1);
+
+  testNotification(twoCalls, SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD, '00000',
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD, 0);
+
+  testNotification(twoCalls, SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD, '11111',
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD, 1);
+
+  testNotification(twoCalls, SUPP_SVC_NOTIFICATION_CODE2_PUT_ON_HOLD, '22222',
+                   GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD, -1);
+
+  run_next_test();
+});
+
--- a/dom/system/gonk/tests/xpcshell.ini
+++ b/dom/system/gonk/tests/xpcshell.ini
@@ -7,8 +7,9 @@ tail =
 [test_ril_worker_sms.js]
 [test_ril_worker_mmi.js]
 [test_ril_worker_cf.js]
 [test_ril_worker_cellbroadcast.js]
 [test_ril_worker_ruim.js]
 [test_ril_worker_cw.js]
 [test_ril_worker_clir.js]
 [test_ril_worker_clip.js]
+[test_ril_worker_ssn.js]
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -391,16 +391,18 @@ Telephony::StopTone()
   nsresult rv = mProvider->StopTone();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMPL_EVENT_HANDLER(Telephony, incoming)
 NS_IMPL_EVENT_HANDLER(Telephony, callschanged)
+NS_IMPL_EVENT_HANDLER(Telephony, remoteheld)
+NS_IMPL_EVENT_HANDLER(Telephony, remoteresumed)
 
 // EventTarget
 
 void
 Telephony::EventListenerAdded(nsIAtom* aType)
 {
   if (aType == nsGkAtoms::oncallschanged) {
     // Fire oncallschanged on the next tick if the calls array is ready.
@@ -521,16 +523,48 @@ Telephony::EnumerateCallState(uint32_t a
 
   NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!");
 
   *aContinue = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+Telephony::SupplementaryServiceNotification(int32_t aCallIndex,
+                                            uint16_t aNotification)
+{
+  nsRefPtr<TelephonyCall> associatedCall;
+  if (!mCalls.IsEmpty() && aCallIndex != -1) {
+    for (uint32_t index = 0; index < mCalls.Length(); index++) {
+      nsRefPtr<TelephonyCall>& call = mCalls[index];
+      if (call->CallIndex() == uint32_t(aCallIndex)) {
+        associatedCall = call;
+        break;
+      }
+    }
+  }
+
+  nsresult rv;
+  switch (aNotification) {
+    case nsITelephonyProvider::NOTIFICATION_REMOTE_HELD:
+      rv = DispatchCallEvent(NS_LITERAL_STRING("remoteheld"), associatedCall);
+      break;
+    case nsITelephonyProvider::NOTIFICATION_REMOTE_RESUMED:
+      rv = DispatchCallEvent(NS_LITERAL_STRING("remoteresumed"), associatedCall);
+      break;
+    default:
+      NS_ERROR("Got a bad notification!");
+      return NS_ERROR_UNEXPECTED;
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 Telephony::NotifyError(int32_t aCallIndex,
                        const nsAString& aError)
 {
   nsRefPtr<TelephonyCall> callToNotify;
   if (!mCalls.IsEmpty()) {
     // The connection is not established yet. Get the latest dialing call object.
     if (aCallIndex == -1) {
       nsRefPtr<TelephonyCall>& lastCall = mCalls[mCalls.Length() - 1];
@@ -563,19 +597,23 @@ Telephony::NotifyError(int32_t aCallInde
 
   return NS_OK;
 }
 
 nsresult
 Telephony::DispatchCallEvent(const nsAString& aType,
                              nsIDOMTelephonyCall* aCall)
 {
-  // We will notify enumeration being completed by firing oncallschanged.
-  // We only ever have a null call with that event type.
-  MOZ_ASSERT(aCall || aType.EqualsLiteral("callschanged"));
+  // The call may be null in following cases:
+  //   1. callschanged when notifying enumeration being completed
+  //   2. remoteheld/remoteresumed.
+  MOZ_ASSERT(aCall ||
+             aType.EqualsLiteral("callschanged") ||
+             aType.EqualsLiteral("remoteheld") ||
+             aType.EqualsLiteral("remtoeresumed"));
 
   nsCOMPtr<nsIDOMEvent> event;
   NS_NewDOMCallEvent(getter_AddRefs(event), this, nullptr, nullptr);
   NS_ASSERTION(event, "This should never fail!");
 
   nsCOMPtr<nsIDOMCallEvent> callEvent = do_QueryInterface(event);
   MOZ_ASSERT(callEvent);
   nsresult rv = callEvent->InitCallEvent(aType, false, false, aCall);
--- a/dom/telephony/nsIDOMTelephony.idl
+++ b/dom/telephony/nsIDOMTelephony.idl
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMEventListener;
 interface nsIDOMTelephonyCall;
 
-[scriptable, builtinclass, uuid(dd9f3957-b0fe-4d3d-b738-79782f76f05d)]
+[scriptable, builtinclass, uuid(5ad8bf8b-958c-447b-9e1a-e6cf598b680f)]
 interface nsIDOMTelephony : nsIDOMEventTarget
 {
   nsIDOMTelephonyCall dial(in DOMString number);
   nsIDOMTelephonyCall dialEmergency(in DOMString number);
 
   attribute boolean muted;
   attribute boolean speakerEnabled;
 
@@ -25,9 +25,11 @@ interface nsIDOMTelephony : nsIDOMEventT
   // Array of all calls that are currently connected.
   readonly attribute jsval calls;
 
   void startTone(in DOMString tone);
   void stopTone();
 
   [implicit_jscontext] attribute jsval onincoming;
   [implicit_jscontext] attribute jsval oncallschanged;
+  [implicit_jscontext] attribute jsval onremoteheld;
+  [implicit_jscontext] attribute jsval onremoteresumed;
 };
--- a/dom/telephony/nsITelephonyProvider.idl
+++ b/dom/telephony/nsITelephonyProvider.idl
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(37fde795-7ff4-40fd-925b-3ebc07ba0cd1)]
+[scriptable, uuid(a1e9fdd9-7901-4a0f-8b6b-6ee0fa8f9d81)]
 interface nsITelephonyListener : nsISupports
 {
   /**
    * Notified when a telephony call changes state.
    *
    * @param callIndex
    *        Call identifier assigned by the RIL.
    * @param callState
@@ -57,46 +57,60 @@ interface nsITelephonyListener : nsISupp
   boolean enumerateCallState(in unsigned long callIndex,
                              in unsigned short callState,
                              in AString number,
                              in boolean isActive,
                              in boolean isOutgoing,
                              in boolean isEmergency);
 
   /**
+   * Notify when RIL receives supplementary service notification.
+   *
+   * @param callIndex
+   *        Call identifier assigned by the RIL. -1 if not specified
+   * @param notification
+   *        One of the nsITelephonyProvider::NOTIFICATION_* values.
+   */
+  void supplementaryServiceNotification(in long callIndex,
+                                        in unsigned short notification);
+
+  /**
    * Called when RIL error occurs.
    *
    * @param callIndex
    *        Call identifier assigned by the RIL. -1 if no connection
    * @param error
    *        Error from RIL.
    */
   void notifyError(in long callIndex,
                    in AString error);
 };
 
 /**
  * XPCOM component (in the content process) that provides the telephony
  * information.
  */
-[scriptable, uuid(099e1d81-f247-4ebb-89d8-cd89dd5f6ed4)]
+[scriptable, uuid(eddb7ff6-29af-4973-83de-1159365c7d7d)]
 interface nsITelephonyProvider : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
   const unsigned short CALL_STATE_CONNECTING = 3;
   const unsigned short CALL_STATE_CONNECTED = 4;
   const unsigned short CALL_STATE_HOLDING = 5;
   const unsigned short CALL_STATE_HELD = 6;
   const unsigned short CALL_STATE_RESUMING = 7;
   const unsigned short CALL_STATE_DISCONNECTING = 8;
   const unsigned short CALL_STATE_DISCONNECTED = 9;
   const unsigned short CALL_STATE_INCOMING = 10;
 
+  const unsigned short NOTIFICATION_REMOTE_HELD = 0;
+  const unsigned short NOTIFICATION_REMOTE_RESUMED = 1;
+
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'telephony' permission is allowed to register.
    */
   void registerTelephonyMsg(in nsITelephonyListener listener);
   void unregisterTelephonyMsg(in nsITelephonyListener listener);
 
--- a/dom/tests/mochitest/webapps/Makefile.in
+++ b/dom/tests/mochitest/webapps/Makefile.in
@@ -6,21 +6,22 @@ DEPTH	 = @DEPTH@
 topsrcdir	 = @top_srcdir@
 srcdir	= @srcdir@
 VPATH	 = @srcdir@
 relativesrcdir	= @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_CHROME_FILES = \
-    bug_765063.xul \
+    install_and_redirect_helper.xul \
     cross_origin.html \
     head.js \
     test_bug_765063.xul \
     test_bug_795164.xul \
+    test_bug_771294.xul \
     test_cross_origin.xul \
     test_install_app.xul \
     test_list_api.xul \
     test_install_errors.xul \
     test_install_utf8.xul \
     test_install_receipts.xul \
     test_getNotInstalled.xul \
     test_launch_paths.xul \
rename from dom/tests/mochitest/webapps/bug_765063.xul
rename to dom/tests/mochitest/webapps/install_and_redirect_helper.xul
--- a/dom/tests/mochitest/webapps/bug_765063.xul
+++ b/dom/tests/mochitest/webapps/install_and_redirect_helper.xul
@@ -1,13 +1,12 @@
 <?xml version="1.0"?>
 
 <!-- Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/ -->
 
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="Mozilla Bug 765063 Helper">
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script>
     var url = "http://test/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
     navigator.mozApps.install(url, null);
     document.location = "about:blank";
   </script>
 </window>
--- a/dom/tests/mochitest/webapps/test_bug_765063.xul
+++ b/dom/tests/mochitest/webapps/test_bug_765063.xul
@@ -17,42 +17,39 @@
   </body>
 
 <script> 
 
 SimpleTest.waitForExplicitFinish();
 
 makeAllAppsLaunchable();
 
-var url = "http://test/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
+var mmListener = {
+  receiveMessage: function(aMessage) {
+    ppmm.removeMessageListener("Webapps:Install", mmListener);
 
-// Observe app installation and confirm that the install origin didn't change.
-var observer = {
-  observe: function observe(subject, topic, data) {
-    Services.obs.removeObserver(observer, "webapps-sync-install");
-    is(JSON.parse(data).installOrigin, "http://test",
-       "the install origin didn't change");
+    var msg = aMessage.json;
+    var ioService = Components.classes["@mozilla.org/network/io-service;1"]
+                                      .getService(Components.interfaces.nsIIOService);
+    var uri = ioService.newURI(msg.from, null, null);
+    is(uri.prePath, "http://test", "the install origin didn't change");
 
-    navigator.mozApps.mgmt.getAll().onsuccess = function onGetAll() {
-      var app = [a for (a of this.result) if (a.manifestURL == url)][0];
-      navigator.mozApps.mgmt.uninstall(app).onsuccess = function onUninstall() {
-        SimpleTest.finish();
-      }
-    }
+    SimpleTest.finish();
   }
 };
 
-Components.utils.import("resource://gre/modules/Services.jsm");
-Services.obs.addObserver(observer, "webapps-sync-install", false);
+var ppmm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
+                             .getService(Components.interfaces.nsIMessageBroadcaster);
+ppmm.addMessageListener("Webapps:Install", mmListener);
 
 // We call this here, even though the app is installed by the helper page,
 // because the helper page redirect would cause its install listener to unload
 // before it can confirm the install.
 confirmNextInstall();
 
 </script> 
 
   <!-- Load a page that initiates an app installation and then immediately
      - redirects to a page at a different origin.  We can't do this directly
      - inside this test page, because that would cause the test to hang. -->
-  <iframe src="http://test/chrome/dom/tests/mochitest/webapps/bug_765063.xul"/>
+  <iframe src="http://test/chrome/dom/tests/mochitest/webapps/install_and_redirect_helper.xul"/>
 
 </window>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webapps/test_bug_771294.xul
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Mozilla Bug 771294">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="head.js"/>
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=771294"
+     target="_blank">Mozilla Bug 771294</a>
+  </body>
+
+<script>
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+SimpleTest.waitForExplicitFinish();
+
+makeAllAppsLaunchable();
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PopupNotifications.jsm");
+
+let blocked = true;
+
+window.top.QueryInterface(Ci.nsIInterfaceRequestor)
+          .getInterface(Ci.nsIWebNavigation)
+          .QueryInterface(Ci.nsIDocShell)
+          .chromeEventHandler.ownerDocument.defaultView
+          .PopupNotifications.panel
+          .addEventListener("popupshowing", function() {
+  blocked = false;
+}, false);
+
+Services.obs.addObserver(
+  function observeInstalling() {
+    Services.obs.removeObserver(observeInstalling, "webapps-ask-install");
+    // Spin the event loop before running the test to give the registry time
+    // to process the install request and (hopefully not) show the doorhanger.
+    setTimeout(function verify() {
+      ok(blocked, "Install panel was blocked after immediate redirect");
+      SimpleTest.finish();
+    }, 0);
+  },
+  "webapps-ask-install",
+  false
+);
+
+</script>
+
+  <!-- Load a page that initiates an app installation and then immediately
+     - redirects to a page at a different origin.  We can't do this directly
+     - inside this test page, because that would cause the test to hang. -->
+  <iframe src="http://test/chrome/dom/tests/mochitest/webapps/install_and_redirect_helper.xul"/>
+
+</window>
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -1345,17 +1345,18 @@ var WifiManager = (function() {
 
   manager.disconnect = disconnectCommand;
   manager.reconnect = reconnectCommand;
   manager.reassociate = reassociateCommand;
 
   var networkConfigurationFields = [
     "ssid", "bssid", "psk", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
     "wep_tx_keyidx", "priority", "key_mgmt", "scan_ssid", "disabled",
-    "identity", "password", "auth_alg", "phase1", "phase2", "eap"
+    "identity", "password", "auth_alg", "phase1", "phase2", "eap", "pin",
+    "pcsc"
   ];
 
   manager.getNetworkConfiguration = function(config, callback) {
     var netId = config.netId;
     var done = 0;
     for (var n = 0; n < networkConfigurationFields.length; ++n) {
       let fieldName = networkConfigurationFields[n];
       getNetworkVariableCommand(netId, fieldName, function(value) {
@@ -1719,16 +1720,17 @@ Network.api = {
 
   password: "rw",
   keyManagement: "rw",
   psk: "rw",
   identity: "rw",
   wep: "rw",
   hidden: "rw",
   eap: "rw",
+  pin: "rw",
   phase1: "rw",
   phase2: "rw"
 };
 
 // Note: We never use ScanResult.prototype, so the fact that it's unrelated to
 // Network.prototype is OK.
 function ScanResult(ssid, bssid, flags, signal) {
   Network.call(this, ssid, getKeyManagement(flags), undefined,
@@ -1982,16 +1984,21 @@ function WifiWorker() {
     checkAssign("psk", true);
     checkAssign("identity", false);
     checkAssign("password", true);
     if (wep && net.wep && net.wep != '*') {
       configured.wep_key0 = net.wep_key0 = isWepHexKey(net.wep) ? net.wep : quote(net.wep);
       configured.auth_alg = net.auth_alg = "OPEN SHARED";
     }
 
+    if ("pin" in net) {
+      net.pin = quote(net.pin);
+      net.pcsc = quote("");
+    }
+
     if ("phase1" in net)
       net.phase1 = quote(net.phase1);
 
     if ("phase2" in net)
       net.phase2 = quote(net.phase2);
 
     return net;
   };
--- a/js/src/config/milestone.txt
+++ b/js/src/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-25.0a1
+26.0a1
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOZ_APP_BASENAME=Fennec
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=25.0a1
+MOZ_APP_VERSION=26.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=1
 
--- a/services/sync/Makefile.in
+++ b/services/sync/Makefile.in
@@ -5,17 +5,17 @@
 DEPTH     := @DEPTH@
 topsrcdir := @top_srcdir@
 srcdir    := @srcdir@
 VPATH     := @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # Definitions used by constants.js.
-weave_version := 1.27.0
+weave_version := 1.28.0
 weave_id      := {340c2bbc-ce74-4362-90b5-7c26312808ef}
 
 # Preprocess files.
 SYNC_PP := modules/constants.js
 SYNC_PP_FLAGS := \
  -Dweave_version=$(weave_version) \
  -Dweave_id=$(weave_id)
 SYNC_PP_PATH = $(FINAL_TARGET)/modules/services-sync
--- a/testing/mochitest/android.json
+++ b/testing/mochitest/android.json
@@ -18,17 +18,16 @@
  "content/base/test/test_CSP_frameancestors.html": "",
  "content/base/test/test_CSP_inlinescript.html": "",
  "content/base/test/test_csp_redirects.html": "TIMED_OUT",
  "content/base/test/test_fileapi_slice.html": "bug 775227",
  "content/base/test/test_mozfiledataurl.html": "TIMED_OUT",
  "content/base/test/test_mixed_content_blocker.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_mixed_content_blocker_bug803225.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_mixed_content_blocker_frameNavigation.html": "TIMED_OUT, SSL_REQUIRED",
- "content/base/test/test_mutationobservers.html": "",
  "content/base/test/test_plugin_freezing.html": "CLICK_TO_PLAY",
  "content/base/test/test_object.html": "",
  "content/base/test/test_range_bounds.html": "",
  "content/base/test/test_reentrant_flush.html": "RANDOM",
  "content/base/test/test_sync_xhr_timer.xhtml": "RANDOM",
  "content/base/test/test_websocket.html": "",
  "content/base/test/test_websocket_basic.html": "",
  "content/base/test/test_websocket_hello.html": "",
--- a/testing/mochitest/androidx86.json
+++ b/testing/mochitest/androidx86.json
@@ -16,17 +16,16 @@
  "content/base/test/test_bug505783.html": "TIMED_OUT",
  "content/base/test/test_copypaste.html": "",
  "content/base/test/test_csp_redirects.html": "TIMED_OUT",
  "content/base/test/test_fileapi_slice.html": "bug 775227",
  "content/base/test/test_mozfiledataurl.html": "TIMED_OUT",
  "content/base/test/test_mixed_content_blocker.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_mixed_content_blocker_bug803225.html": "TIMED_OUT, SSL_REQUIRED",
  "content/base/test/test_mixed_content_blocker_frameNavigation.html": "TIMED_OUT, SSL_REQUIRED",
- "content/base/test/test_mutationobservers.html": "",
  "content/base/test/test_plugin_freezing.html": "CLICK_TO_PLAY",
  "content/base/test/test_object.html": "",
  "content/base/test/test_range_bounds.html": "",
  "content/base/test/test_reentrant_flush.html": "RANDOM",
  "content/base/test/test_sync_xhr_timer.xhtml": "RANDOM",
  "content/base/test/test_websocket.html": "",
  "content/base/test/test_websocket_basic.html": "",
  "content/base/test/test_websocket_hello.html": "",
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -77,17 +77,16 @@
     "content/base/test/test_bug338583.html":"",
     "content/base/test/test_bug372086.html":"",
     "content/base/test/test_bug466080.html":"",
     "content/base/test/test_bug475156.html":"",
     "content/base/test/test_bug590870.html":"",
     "content/base/test/test_bug666604.html":"",
     "content/base/test/test_bug675121.html":"",
     "content/base/test/test_classList.html":"",
-    "content/base/test/test_mutationobservers.html":"",
     "content/base/test/test_title.html":"",
     "content/canvas/test/crossorigin/test_video_crossorigin.html":"",
     "content/events/test/test_bug422132.html":"",
     "content/events/test/test_bug426082.html":"",
     "content/events/test/test_bug534833.html":"",
     "content/events/test/test_bug603008.html":"",
     "content/events/test/test_bug659071.html":"",
     "content/events/test/test_bug667919-1.html":"",
--- a/toolkit/webapps/WebappsInstaller.jsm
+++ b/toolkit/webapps/WebappsInstaller.jsm
@@ -418,18 +418,18 @@ WinNativeApp.prototype = {
       parentKey.open(parentKey.ROOT_KEY_CURRENT_USER,
                      "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                      parentKey.ACCESS_WRITE);
       uninstallKey = parentKey.createChild("Uninstall", parentKey.ACCESS_WRITE)
       subKey = uninstallKey.createChild(this.uninstallSubkeyStr, uninstallKey.ACCESS_WRITE);
 
       subKey.writeStringValue("DisplayName", this.appName);
 
-      subKey.writeStringValue("UninstallString", this.uninstallerFile.path);
-      subKey.writeStringValue("InstallLocation", this.installDir.path);
+      subKey.writeStringValue("UninstallString", '"' + this.uninstallerFile.path + '"');
+      subKey.writeStringValue("InstallLocation", '"' + this.installDir.path + '"');
       subKey.writeStringValue("AppFilename", this.appNameAsFilename);
 
       if(this.iconFile) {
         subKey.writeStringValue("DisplayIcon", this.iconFile.path);
       }
 
       subKey.writeIntValue("NoModify", 1);
       subKey.writeIntValue("NoRepair", 1);
--- a/webapprt/gtk2/webapprt.cpp
+++ b/webapprt/gtk2/webapprt.cpp
@@ -66,16 +66,18 @@ void SetAllocatedString(const char *&str
 }
 
 // Function to open a dialog box displaying the message provided
 void ErrorDialog(const char* message)
 {
   gtk_init(pargc, pargv);
 
   GtkWidget* dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message);
+  gtk_window_set_title(GTK_WINDOW(dialog), "Error launching webapp");
+  gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), false);
   gtk_dialog_run(GTK_DIALOG(dialog));
   gtk_widget_destroy(dialog);
 }
 
 // Function to get the parent dir of a file
 void GetDirFromPath(char* parentDir, char* filePath)
 {
   char* base = strrchr(filePath, '/');
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -16,17 +16,17 @@ namespace mozilla {
 /**
  * A module implements one or more XPCOM components. This structure is used
  * for both binary and script modules, but the registration members
  * (cids/contractids/categoryentries) are unused for modules which are loaded
  * via a module loader.
  */
 struct Module
 {
-  static const unsigned int kVersion = 25;
+  static const unsigned int kVersion = 26;
 
   struct CIDEntry;
 
   typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)
     (const Module& module, const CIDEntry& entry);
 
   typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
                                          const nsIID& aIID,