Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 05 May 2016 14:58:24 -0400
changeset 296386 e4e6e8589b76d09f1fa43186cbca61bdac430254
parent 296385 eacfa3e222b31ad989e8d6689808b4fe1b7e67c7 (current diff)
parent 296234 0177462aac74605f426ab9c92e39bb467b7ce2d1 (diff)
child 296387 5fe67c44f537155ac01049024e1c9548ece1a012
push id76311
push usercbook@mozilla.com
push dateFri, 06 May 2016 12:26:12 +0000
treeherdermozilla-inbound@84a3e5716801 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
StreamTracks.cpp
StreamTracks.h
b2g/components/InterAppCommUIGlue.js
dom/apps/InterAppComm.cpp
dom/apps/InterAppComm.h
dom/apps/InterAppComm.manifest
dom/apps/InterAppCommService.js
dom/apps/InterAppCommService.jsm
dom/apps/InterAppConnection.js
dom/apps/InterAppMessagePort.js
dom/apps/tests/b2g_chrome.ini
dom/apps/tests/iac/README.txt
dom/apps/tests/iac/makezips.sh
dom/apps/tests/iac/publisher/index.html
dom/apps/tests/iac/publisher/manifest.webapp
dom/apps/tests/iac/publisher/publisher.list
dom/apps/tests/iac/publisher/publisher.zip
dom/apps/tests/iac/publisher/test.js
dom/apps/tests/iac/publisher/update.webapp
dom/apps/tests/iac/publisher/update.webapp^headers^
dom/apps/tests/iac/subscriber/index.html
dom/apps/tests/iac/subscriber/manifest.webapp
dom/apps/tests/iac/subscriber/subscriber.list
dom/apps/tests/iac/subscriber/subscriber.zip
dom/apps/tests/iac/subscriber/test.js
dom/apps/tests/iac/subscriber/update.webapp
dom/apps/tests/iac/subscriber/update.webapp^headers^
dom/apps/tests/test_iac.html
dom/apps/tests/unit/test_inter_app_comm_service.js
dom/engineeringmode/EngineeringMode.manifest
dom/engineeringmode/EngineeringModeAPI.js
dom/engineeringmode/EngineeringModeService.js
dom/engineeringmode/moz.build
dom/engineeringmode/nsIEngineeringMode.idl
dom/interfaces/apps/nsIInterAppCommService.idl
dom/interfaces/apps/nsIInterAppCommUIGlue.idl
dom/media/StreamTracks.cpp
dom/media/StreamTracks.h
dom/webidl/EngineeringMode.webidl
dom/webidl/InterAppConnection.webidl
dom/webidl/InterAppConnectionRequest.webidl
dom/webidl/InterAppMessagePort.webidl
dom/webidl/MozInterAppMessageEvent.webidl
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/cross-origin-http/audio-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/cross-origin-http/img-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/cross-origin-http/video-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/same-host-http/audio-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/same-host-http/img-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/same-host-http/video-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
toolkit/components/telemetry/Histograms.json
toolkit/crashreporter/google-breakpad/src/client/linux/Makefile.in
toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in
deleted file mode 100644
--- a/StreamTracks.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "StreamBuffer.h"
-#include "mozilla/Logging.h"
-#include <algorithm>
-
-namespace mozilla {
-
-extern LazyLogModule gMediaStreamGraphLog;
-#define STREAM_LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg)
-
-#ifdef DEBUG
-void
-StreamBuffer::DumpTrackInfo() const
-{
-  STREAM_LOG(LogLevel::Info, ("DumpTracks: mTracksKnownTime %lld", mTracksKnownTime));
-  for (uint32_t i = 0; i < mTracks.Length(); ++i) {
-    Track* track = mTracks[i];
-    if (track->IsEnded()) {
-      STREAM_LOG(LogLevel::Info, ("Track[%d] %d: ended", i, track->GetID()));
-    } else {
-      STREAM_LOG(LogLevel::Info, ("Track[%d] %d: %lld", i, track->GetID(),
-                                 track->GetEnd()));
-    }
-  }
-}
-#endif
-
-StreamTime
-StreamBuffer::GetEnd() const
-{
-  StreamTime t = mTracksKnownTime;
-  for (uint32_t i = 0; i < mTracks.Length(); ++i) {
-    Track* track = mTracks[i];
-    if (!track->IsEnded()) {
-      t = std::min(t, track->GetEnd());
-    }
-  }
-  return t;
-}
-
-StreamTime
-StreamBuffer::GetAllTracksEnd() const
-{
-  if (mTracksKnownTime < STREAM_TIME_MAX) {
-    // A track might be added.
-    return STREAM_TIME_MAX;
-  }
-  StreamTime t = 0;
-  for (uint32_t i = 0; i < mTracks.Length(); ++i) {
-    Track* track = mTracks[i];
-    if (!track->IsEnded()) {
-      return STREAM_TIME_MAX;
-    }
-    t = std::max(t, track->GetEnd());
-  }
-  return t;
-}
-
-StreamBuffer::Track*
-StreamBuffer::FindTrack(TrackID aID)
-{
-  if (aID == TRACK_NONE || mTracks.IsEmpty()) {
-    return nullptr;
-  }
-
-  // The tracks are sorted by ID. We can use a binary search.
-
-  uint32_t left = 0, right = mTracks.Length() - 1;
-  while (left <= right) {
-    uint32_t middle = (left + right) / 2;
-    if (mTracks[middle]->GetID() == aID) {
-      return mTracks[middle];
-    }
-
-    if (mTracks[middle]->GetID() > aID) {
-      if (middle == 0) {
-        break;
-      }
-
-      right = middle - 1;
-    } else {
-      left = middle + 1;
-    }
-  }
-
-  return nullptr;
-}
-
-void
-StreamBuffer::ForgetUpTo(StreamTime aTime)
-{
-  // Only prune if there is a reasonable chunk (50ms @ 48kHz) to forget, so we
-  // don't spend too much time pruning segments.
-  const StreamTime minChunkSize = 2400;
-  if (aTime < mForgottenTime + minChunkSize) {
-    return;
-  }
-  mForgottenTime = aTime;
-
-  for (uint32_t i = 0; i < mTracks.Length(); ++i) {
-    Track* track = mTracks[i];
-    if (track->IsEnded() && track->GetEnd() <= aTime) {
-      mTracks.RemoveElementAt(i);
-      mTracksDirty = true;
-      --i;
-      continue;
-    }
-    StreamTime forgetTo = std::min(track->GetEnd() - 1, aTime);
-    track->ForgetUpTo(forgetTo);
-  }
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/StreamTracks.h
+++ /dev/null
@@ -1,343 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef MOZILLA_STREAMBUFFER_H_
-#define MOZILLA_STREAMBUFFER_H_
-
-#include "MediaSegment.h"
-#include "nsAutoPtr.h"
-
-namespace mozilla {
-
-/**
- * Unique ID for track within a StreamBuffer. Tracks from different
- * StreamBuffers may have the same ID; this matters when appending StreamBuffers,
- * since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
- */
-typedef int32_t TrackID;
-const TrackID TRACK_NONE = 0;
-const TrackID TRACK_INVALID = -1;
-const TrackID TRACK_ANY = -2;
-
-inline bool IsTrackIDExplicit(const TrackID& aId) {
-  return aId > TRACK_NONE;
-}
-
-inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
-                                            TrackRate aInRate,
-                                            TrackTicks aTicks)
-{
-  NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
-  NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
-  NS_WARN_IF_FALSE(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks"); // bug 957691
-  return (aTicks * aOutRate) / aInRate;
-}
-inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate,
-                                          TrackRate aInRate, TrackTicks aTicks)
-{
-  NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
-  NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
-  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
-  return (aTicks * aOutRate + aInRate - 1) / aInRate;
-}
-
-/**
- * This object contains the decoded data for a stream's tracks.
- * A StreamBuffer can be appended to. Logically a StreamBuffer only gets longer,
- * but we also have the ability to "forget" data before a certain time that
- * we know won't be used again. (We prune a whole number of seconds internally.)
- *
- * StreamBuffers should only be used from one thread at a time.
- *
- * A StreamBuffer has a set of tracks that can be of arbitrary types ---
- * the data for each track is a MediaSegment. The set of tracks can vary
- * over the timeline of the StreamBuffer.
- */
-class StreamBuffer
-{
-public:
-  /**
-   * Every track has a start time --- when it started in the StreamBuffer.
-   * It has an end flag; when false, no end point is known; when true,
-   * the track ends when the data we have for the track runs out.
-   * Tracks have a unique ID assigned at creation. This allows us to identify
-   * the same track across StreamBuffers. A StreamBuffer should never have
-   * two tracks with the same ID (even if they don't overlap in time).
-   * TODO Tracks can also be enabled and disabled over time.
-   * Takes ownership of aSegment.
-   */
-  class Track final
-  {
-    Track(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
-      : mStart(aStart),
-        mSegment(aSegment),
-        mID(aID),
-        mEnded(false)
-    {
-      MOZ_COUNT_CTOR(Track);
-
-      NS_ASSERTION(aID > TRACK_NONE, "Bad track ID");
-      NS_ASSERTION(0 <= aStart && aStart <= aSegment->GetDuration(), "Bad start position");
-    }
-
-  public:
-    ~Track()
-    {
-      MOZ_COUNT_DTOR(Track);
-    }
-
-    template <class T> T* Get() const
-    {
-      if (mSegment->GetType() == T::StaticType()) {
-        return static_cast<T*>(mSegment.get());
-      }
-      return nullptr;
-    }
-
-    MediaSegment* GetSegment() const { return mSegment; }
-    TrackID GetID() const { return mID; }
-    bool IsEnded() const { return mEnded; }
-    StreamTime GetStart() const { return mStart; }
-    StreamTime GetEnd() const { return mSegment->GetDuration(); }
-    MediaSegment::Type GetType() const { return mSegment->GetType(); }
-
-    void SetEnded() { mEnded = true; }
-    void AppendFrom(Track* aTrack)
-    {
-      NS_ASSERTION(!mEnded, "Can't append to ended track");
-      NS_ASSERTION(aTrack->mID == mID, "IDs must match");
-      NS_ASSERTION(aTrack->mStart == 0, "Source track must start at zero");
-      NS_ASSERTION(aTrack->mSegment->GetType() == GetType(), "Track types must match");
-
-      mSegment->AppendFrom(aTrack->mSegment);
-      mEnded = aTrack->mEnded;
-    }
-    MediaSegment* RemoveSegment()
-    {
-      return mSegment.forget();
-    }
-    void ForgetUpTo(StreamTime aTime)
-    {
-      mSegment->ForgetUpTo(aTime);
-    }
-    void FlushAfter(StreamTime aNewEnd)
-    {
-      // Forget everything after a given endpoint
-      // a specified amount
-      mSegment->FlushAfter(aNewEnd);
-    }
-
-    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-    {
-      size_t amount = aMallocSizeOf(this);
-      if (mSegment) {
-        amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
-      }
-      return amount;
-    }
-
-  private:
-    friend class StreamBuffer;
-
-    // Start offset is in ticks at rate mRate
-    StreamTime mStart;
-    // The segment data starts at the start of the owning StreamBuffer, i.e.,
-    // there's mStart silence/no video at the beginning.
-    nsAutoPtr<MediaSegment> mSegment;
-    // Unique ID
-    TrackID mID;
-    // True when the track ends with the data in mSegment
-    bool mEnded;
-  };
-
-  class MOZ_STACK_CLASS CompareTracksByID final
-  {
-  public:
-    bool Equals(Track* aA, Track* aB) const {
-      return aA->GetID() == aB->GetID();
-    }
-    bool LessThan(Track* aA, Track* aB) const {
-      return aA->GetID() < aB->GetID();
-    }
-  };
-
-  StreamBuffer()
-    : mGraphRate(0)
-    , mTracksKnownTime(0)
-    , mForgottenTime(0)
-    , mTracksDirty(false)
-#ifdef DEBUG
-    , mGraphRateIsSet(false)
-#endif
-  {
-    MOZ_COUNT_CTOR(StreamBuffer);
-  }
-  ~StreamBuffer()
-  {
-    MOZ_COUNT_DTOR(StreamBuffer);
-  }
-
-  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    size_t amount = 0;
-    amount += mTracks.ShallowSizeOfExcludingThis(aMallocSizeOf);
-    for (size_t i = 0; i < mTracks.Length(); i++) {
-      amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
-    }
-    return amount;
-  }
-
-  /**
-   * Initialize the graph rate for use in calculating StreamTimes from track
-   * ticks.  Called when a MediaStream's graph pointer is initialized.
-   */
-  void InitGraphRate(TrackRate aGraphRate)
-  {
-    mGraphRate = aGraphRate;
-#if DEBUG
-    MOZ_ASSERT(!mGraphRateIsSet);
-    mGraphRateIsSet = true;
-#endif
-  }
-
-  TrackRate GraphRate() const
-  {
-    MOZ_ASSERT(mGraphRateIsSet);
-    return mGraphRate;
-  }
-
-  /**
-   * Takes ownership of aSegment. Don't do this while iterating, or while
-   * holding a Track reference.
-   * aSegment must have aStart worth of null data.
-   */
-  Track& AddTrack(TrackID aID, StreamTime aStart, MediaSegment* aSegment)
-  {
-    NS_ASSERTION(!FindTrack(aID), "Track with this ID already exists");
-
-    Track* track = new Track(aID, aStart, aSegment);
-    mTracks.InsertElementSorted(track, CompareTracksByID());
-    mTracksDirty = true;
-
-    if (mTracksKnownTime == STREAM_TIME_MAX) {
-      // There exists code like
-      // http://mxr.mozilla.org/mozilla-central/source/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp?rev=96b197deb91e&mark=1292-1297#1292
-      NS_WARNING("Adding track to StreamBuffer that should have no more tracks");
-    } else {
-      NS_ASSERTION(mTracksKnownTime <= aStart, "Start time too early");
-    }
-    return *track;
-  }
-
-  void AdvanceKnownTracksTime(StreamTime aKnownTime)
-  {
-    NS_ASSERTION(aKnownTime >= mTracksKnownTime, "Can't move tracks-known time earlier");
-    mTracksKnownTime = aKnownTime;
-  }
-
-  /**
-   * The end time for the StreamBuffer is the latest time for which we have
-   * data for all tracks that haven't ended by that time.
-   */
-  StreamTime GetEnd() const;
-
-  /**
-   * Returns the earliest time >= 0 at which all tracks have ended
-   * and all their data has been played out and no new tracks can be added,
-   * or STREAM_TIME_MAX if there is no such time.
-   */
-  StreamTime GetAllTracksEnd() const;
-
-#ifdef DEBUG
-  void DumpTrackInfo() const;
-#endif
-
-  Track* FindTrack(TrackID aID);
-
-  class MOZ_STACK_CLASS TrackIter final
-  {
-  public:
-    /**
-     * Iterate through the tracks of aBuffer in order of ID.
-     */
-    explicit TrackIter(const StreamBuffer& aBuffer) :
-      mBuffer(&aBuffer.mTracks), mIndex(0), mMatchType(false) {}
-    /**
-     * Iterate through the tracks of aBuffer with type aType, in order of ID.
-     */
-    TrackIter(const StreamBuffer& aBuffer, MediaSegment::Type aType) :
-      mBuffer(&aBuffer.mTracks), mIndex(0), mType(aType), mMatchType(true) { FindMatch(); }
-    bool IsEnded() { return mIndex >= mBuffer->Length(); }
-    void Next()
-    {
-      ++mIndex;
-      FindMatch();
-    }
-    Track* get() { return mBuffer->ElementAt(mIndex); }
-    Track& operator*() { return *mBuffer->ElementAt(mIndex); }
-    Track* operator->() { return mBuffer->ElementAt(mIndex); }
-  private:
-    void FindMatch()
-    {
-      if (!mMatchType)
-        return;
-      while (mIndex < mBuffer->Length() &&
-             mBuffer->ElementAt(mIndex)->GetType() != mType) {
-        ++mIndex;
-      }
-    }
-
-    const nsTArray<nsAutoPtr<Track> >* mBuffer;
-    uint32_t mIndex;
-    MediaSegment::Type mType;
-    bool mMatchType;
-  };
-  friend class TrackIter;
-
-  /**
-   * Forget stream data before aTime; they will no longer be needed.
-   * Also can forget entire tracks that have ended at or before aTime.
-   * Can't be used to forget beyond GetEnd().
-   */
-  void ForgetUpTo(StreamTime aTime);
-  /**
-   * Returns the latest time passed to ForgetUpTo.
-   */
-  StreamTime GetForgottenDuration()
-  {
-    return mForgottenTime;
-  }
-
-  bool GetAndResetTracksDirty()
-  {
-    if (!mTracksDirty) {
-      return false;
-    }
-
-    mTracksDirty = false;
-    return true;
-  }
-
-protected:
-  TrackRate mGraphRate; // StreamTime per second
-  // Any new tracks added will start at or after this time. In other words, the track
-  // list is complete and correct for all times less than this time.
-  StreamTime mTracksKnownTime;
-  StreamTime mForgottenTime;
-
-private:
-  // All known tracks for this StreamBuffer
-  nsTArray<nsAutoPtr<Track>> mTracks;
-  bool mTracksDirty;
-
-#ifdef DEBUG
-  bool mGraphRateIsSet;
-#endif
-};
-
-} // namespace mozilla
-
-#endif /* MOZILLA_STREAMBUFFER_H_ */
-
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -880,19 +880,16 @@ pref("disk_space_watcher.enabled", true)
 // SNTP preferences.
 pref("network.sntp.maxRetryCount", 10);
 pref("network.sntp.refreshPeriod", 86400); // In seconds.
 pref("network.sntp.pools", // Servers separated by ';'.
      "0.pool.ntp.org;1.pool.ntp.org;2.pool.ntp.org;3.pool.ntp.org");
 pref("network.sntp.port", 123);
 pref("network.sntp.timeout", 30); // In seconds.
 
-// DOM Inter-App Communication API.
-pref("dom.inter-app-communication-api.enabled", true);
-
 // Allow ADB to run for this many hours before disabling
 // (only applies when marionette is disabled)
 // 0 disables the timer.
 pref("b2g.adb.timeout-hours", 12);
 
 // InputMethod so we can do soft keyboards
 pref("dom.mozInputMethod.enabled", true);
 
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -22,20 +22,16 @@ component {9181eb7c-6f87-11e1-90b1-4f59d
 contract @mozilla.org/b2g/directory-provider;1 {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}
 category xpcom-directory-providers b2g-directory-provider @mozilla.org/b2g/directory-provider;1
 #endif
 
 # ActivitiesGlue.js
 component {3a54788b-48cc-4ab4-93d6-0d6a8ef74f8e} ActivitiesGlue.js
 contract @mozilla.org/dom/activities/ui-glue;1 {3a54788b-48cc-4ab4-93d6-0d6a8ef74f8e}
 
-# InterAppCommUIGlue.js
-component {879ee66c-e246-11e3-9910-74d02b97e723} InterAppCommUIGlue.js
-contract @mozilla.org/dom/apps/inter-app-comm-ui-glue;1 {879ee66c-e246-11e3-9910-74d02b97e723}
-
 # SystemMessageGlue.js
 component {2846f034-e614-11e3-93cd-74d02b97e723} SystemMessageGlue.js
 contract @mozilla.org/dom/messages/system-message-glue;1 {2846f034-e614-11e3-93cd-74d02b97e723}
 
 # ProcessGlobal.js
 component {1a94c87a-5ece-4d11-91e1-d29c29f21b28} ProcessGlobal.js
 contract @mozilla.org/b2g-process-global;1 {1a94c87a-5ece-4d11-91e1-d29c29f21b28}
 category app-startup ProcessGlobal service,@mozilla.org/b2g-process-global;1
deleted file mode 100644
--- a/b2g/components/InterAppCommUIGlue.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict"
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
-                                  "resource://gre/modules/SystemAppProxy.jsm");
-
-const DEBUG = false;
-function debug(aMsg) {
-  dump("-- InterAppCommUIGlue: " + Date.now() + ": " + aMsg + "\n");
-}
-
-function InterAppCommUIGlue() {
-  // This matrix is to store the callerID (a random UUID) / deferral binding.
-  // An example of the object literal is shown below:
-  //
-  // {
-  //   "callerID1" : deferred1,
-  //   "callerID2" : deferred2
-  // }
-  this._deferreds = {};
-
-  // Listen to the result of selected apps from front-end.
-  SystemAppProxy.addEventListener ("mozIACContentEvent", function (aEvent) {
-    let detail = aEvent.detail;
-    if (detail.type != "inter-app-comm-permission") {
-      return;
-    }
-
-    if (DEBUG) {
-      debug("mozIACContentEvent: " + JSON.stringify(detail));
-    }
-
-    let callerID = detail.chromeEventID;
-    let deferred = this._deferreds[callerID];
-    if (!deferred) {
-      if (DEBUG) {
-        debug("Error! Cannot find the deferred for callerID: " + callerID);
-      }
-      return;
-    }
-
-    delete this._deferreds[callerID];
-    deferred.resolve({ callerID: callerID,
-                       keyword: detail.keyword,
-                       manifestURL: detail.manifestURL,
-                       selectedApps: detail.peers });
-  }.bind(this));
-}
-
-InterAppCommUIGlue.prototype = {
-  selectApps: function(aCallerID, aPubAppManifestURL, aKeyword, aAppsToSelect) {
-    let deferred = Promise.defer();
-    this._deferreds[aCallerID] = deferred;
-
-    SystemAppProxy._sendCustomEvent("mozIACChromeEvent",
-                                    { type: "inter-app-comm-permission",
-                                      chromeEventID: aCallerID,
-                                      manifestURL: aPubAppManifestURL,
-                                      keyword: aKeyword,
-                                      peers: aAppsToSelect });
-
-    // TODO Bug 897169 Simulate the return of the app-selected result by
-    // the prompt, which always allows the connection. This dummy codes
-    // will be removed when the UX/UI for the prompt is ready.
-    SystemAppProxy._sendCustomEvent("mozIACContentEvent",
-                                    { type: "inter-app-comm-permission",
-                                      chromeEventID: aCallerID,
-                                      manifestURL: aPubAppManifestURL,
-                                      keyword: aKeyword,
-                                      peers: aAppsToSelect });
-
-    return deferred.promise;
-  },
-
-  classID: Components.ID("{879ee66c-e246-11e3-9910-74d02b97e723}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommUIGlue])
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommUIGlue]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -12,17 +12,16 @@ EXTRA_COMPONENTS += [
     'B2GAboutRedirector.js',
     'B2GAppMigrator.js',
     'B2GPresentationDevicePrompt.js',
     'BootstrapCommandLine.js',
     'ContentPermissionPrompt.js',
     'FilePicker.js',
     'FxAccountsUIGlue.js',
     'HelperAppDialog.js',
-    'InterAppCommUIGlue.js',
     'KillSwitch.js',
     'MailtoProtocolHandler.js',
     'MobileIdentityUIGlue.js',
     'OMAContentHandler.js',
     'PaymentGlue.js',
     'PaymentProviderStrategy.js',
     'PresentationRequestUIGlue.js',
     'ProcessGlobal.js',
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -211,17 +211,16 @@
 @RESPATH@/components/dom_voicemail.xpt
 #ifdef MOZ_WEBSPEECH
 @RESPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @RESPATH@/components/dom_xbl.xpt
 @RESPATH@/components/dom_xpath.xpt
 @RESPATH@/components/dom_xul.xpt
 @RESPATH@/components/dom_time.xpt
-@RESPATH@/components/dom_engineeringmode.xpt
 @RESPATH@/components/dom_presentation.xpt
 @RESPATH@/components/downloads.xpt
 @RESPATH@/components/editor.xpt
 @RESPATH@/components/embed_base.xpt
 @RESPATH@/components/extensions.xpt
 @RESPATH@/components/exthandler.xpt
 @RESPATH@/components/exthelper.xpt
 @RESPATH@/components/fastfind.xpt
@@ -622,21 +621,16 @@
 @RESPATH@/components/Push.js
 @RESPATH@/components/Push.manifest
 #ifdef MOZ_SIMPLEPUSH
 @RESPATH@/components/PushServiceLauncher.js
 #else
 @RESPATH@/components/PushComponents.js
 #endif
 
-@RESPATH@/components/InterAppComm.manifest
-@RESPATH@/components/InterAppCommService.js
-@RESPATH@/components/InterAppConnection.js
-@RESPATH@/components/InterAppMessagePort.js
-
 @RESPATH@/components/nsDOMIdentity.js
 @RESPATH@/components/nsIDService.js
 @RESPATH@/components/Identity.manifest
 
 @RESPATH@/components/SystemMessageInternal.js
 @RESPATH@/components/SystemMessageManager.js
 @RESPATH@/components/SystemMessageCache.js
 @RESPATH@/components/SystemMessageManager.manifest
@@ -659,20 +653,16 @@
 
 ; InputMethod API
 @RESPATH@/components/MozKeyboard.js
 @RESPATH@/components/InputMethod.manifest
 #ifdef MOZ_B2G
 @RESPATH@/components/inputmethod.xpt
 #endif
 
-@RESPATH@/components/EngineeringMode.manifest
-@RESPATH@/components/EngineeringModeAPI.js
-@RESPATH@/components/EngineeringModeService.js
-
 @RESPATH@/components/SystemUpdate.manifest
 @RESPATH@/components/SystemUpdateManager.js
 
 #ifdef MOZ_DEBUG
 @RESPATH@/components/TestInterfaceJS.js
 @RESPATH@/components/TestInterfaceJS.manifest
 @RESPATH@/components/TestInterfaceJSMaplike.js
 #endif
@@ -910,17 +900,16 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/components/RecoveryService.js
 @RESPATH@/components/MailtoProtocolHandler.js
 @RESPATH@/components/SmsProtocolHandler.js
 @RESPATH@/components/TelProtocolHandler.js
 @RESPATH@/components/B2GAboutRedirector.js
 @RESPATH@/components/FilePicker.js
 @RESPATH@/components/HelperAppDialog.js
 @RESPATH@/components/DownloadsUI.js
-@RESPATH@/components/InterAppCommUIGlue.js
 @RESPATH@/components/SystemMessageGlue.js
 @RESPATH@/components/B2GAppMigrator.js
 @RESPATH@/components/B2GPresentationDevicePrompt.js
 @RESPATH@/components/PresentationRequestUIGlue.js
 @RESPATH@/components/KillSwitch.js
 
 #ifndef MOZ_WIDGET_GONK
 @RESPATH@/components/SimulatorScreen.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -811,20 +811,16 @@ function _loadURIWithFlags(browser, uri,
   }
   let flags = params.flags || 0;
   let referrer = params.referrerURI;
   let referrerPolicy = ('referrerPolicy' in params ? params.referrerPolicy :
                         Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT);
   let charset = params.charset;
   let postData = params.postData;
 
-  if (!(flags & browser.webNavigation.LOAD_FLAGS_FROM_EXTERNAL)) {
-    browser.userTypedClear++;
-  }
-
   let wasRemote = browser.isRemoteBrowser;
 
   let process = browser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
                                         : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
   let mustChangeProcess = gMultiProcessBrowser &&
                           !E10SUtils.canLoadURIInProcess(uri, process);
   if ((!wasRemote && !mustChangeProcess) ||
       (wasRemote && mustChangeProcess)) {
@@ -862,19 +858,16 @@ function _loadURIWithFlags(browser, uri,
     } else {
       throw e;
     }
   } finally {
     if ((!wasRemote && !mustChangeProcess) ||
         (wasRemote && mustChangeProcess)) {
       browser.inLoadURI = false;
     }
-    if (browser.userTypedClear) {
-      browser.userTypedClear--;
-    }
   }
 }
 
 // Starts a new load in the browser first switching the browser to the correct
 // process
 function LoadInOtherProcess(browser, loadOptions, historyIndex = -1) {
   let tab = gBrowser.getTabForBrowser(browser);
   SessionStore.navigateAndRestore(tab, loadOptions, historyIndex);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -613,16 +613,22 @@
             onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
               if (!aRequest)
                 return;
 
               var oldBlank = this.mBlank;
 
               const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
               const nsIChannel = Components.interfaces.nsIChannel;
+              let location, originalLocation;
+              try {
+                aRequest.QueryInterface(nsIChannel)
+                location = aRequest.URI;
+                originalLocation = aRequest.originalURI;
+              } catch (ex) {}
 
               if (aStateFlags & nsIWebProgressListener.STATE_START) {
                 this.mRequestCount++;
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
                 const NS_ERROR_UNKNOWN_HOST = 2152398878;
                 if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
                   // to prevent bug 235825: wait for the request handled
@@ -631,27 +637,31 @@
                 }
                 // since we (try to) only handle STATE_STOP of the last request,
                 // the count of open requests should now be 0
                 this.mRequestCount = 0;
               }
 
               if (aStateFlags & nsIWebProgressListener.STATE_START &&
                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
-                // It's okay to clear what the user typed when we start
-                // loading a document. If the user types, this counter gets
-                // set to zero, if the document load ends without an
-                // onLocationChange, this counter gets decremented
-                // (so we keep it while switching tabs after failed loads)
-                // We need to add 2 because loadURIWithFlags may have
-                // cancelled a pending load which would have cleared
-                // its anchor scroll detection temporary increment.
                 if (aWebProgress.isTopLevel) {
-                  this.mBrowser.userTypedClear += 2;
-
+                  // Need to use originalLocation rather than location because things
+                  // like about:home and about:privatebrowsing arrive with nsIRequest
+                  // pointing to their resolved jar: or file: URIs.
+                  if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
+                        originalLocation != "about:blank" &&
+                        this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
+                    // This will trigger clearing the location bar. Don't do it if
+                    // we loaded off a blank browser and this is an initial page load
+                    // (e.g. about:privatebrowsing, about:newtab, etc.) so we avoid
+                    // clearing the location bar in case the user is typing in it.
+                    // loading about:blank shouldn't trigger this, either, because its
+                    // loads are "special".
+                    this.mBrowser.urlbarChangeTracker.startedLoad();
+                  }
                   // If the browser is loading it must not be crashed anymore
                   this.mTab.removeAttribute("crashed");
                 }
 
                 if (this._shouldShowProgress(aRequest)) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
 
@@ -671,47 +681,39 @@
                   this.mTab.removeAttribute("busy");
                   this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
                   if (!this.mTab.selected)
                     this.mTab.setAttribute("unread", "true");
                 }
                 this.mTab.removeAttribute("progress");
 
                 if (aWebProgress.isTopLevel) {
-                  if (!Components.isSuccessCode(aStatus) &&
-                      !isTabEmpty(this.mTab)) {
+                  let isSuccessful = Components.isSuccessCode(aStatus);
+                  if (!isSuccessful && !isTabEmpty(this.mTab)) {
                     // Restore the current document's location in case the
                     // request was stopped (possibly from a content script)
                     // before the location changed.
 
                     this.mBrowser.userTypedValue = null;
 
                     let inLoadURI = this.mBrowser.inLoadURI;
                     if (this.mTab.selected && gURLBar && !inLoadURI) {
                       URLBarSetURI();
                     }
-                  } else {
-                    // The document is done loading, we no longer want the
-                    // value cleared.
-
-                    if (this.mBrowser.userTypedClear > 1)
-                      this.mBrowser.userTypedClear -= 2;
-                    else if (this.mBrowser.userTypedClear > 0)
-                      this.mBrowser.userTypedClear--;
+                  } else if (isSuccessful) {
+                    this.mBrowser.urlbarChangeTracker.finishedLoad();
                   }
 
                   if (!this.mBrowser.mIconURL)
                     this.mTabBrowser.useDefaultIcon(this.mTab);
                 }
 
                 if (this.mBlank)
                   this.mBlank = false;
 
-                var location = aRequest.QueryInterface(nsIChannel).URI;
-
                 // For keyword URIs clear the user typed value since they will be changed into real URIs
                 if (location.scheme == "keyword")
                   this.mBrowser.userTypedValue = null;
 
                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.connecting"))
                   this.mTabBrowser.setTabTitle(this.mTab);
 
                 if (this.mTab.selected)
@@ -746,28 +748,27 @@
                                         aFlags) {
               // OnLocationChange is called for both the top-level content
               // and the subframes.
               let topLevel = aWebProgress.isTopLevel;
 
               if (topLevel) {
                 let isSameDocument =
                   !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
-                // If userTypedClear > 0, the document loaded correctly and we should be
-                // clearing the user typed value. We also need to clear the typed value
+                // We need to clear the typed value
                 // if the document failed to load, to make sure the urlbar reflects the
                 // failed URI (particularly for SSL errors). However, don't clear the value
                 // if the error page's URI is about:blank, because that causes complete
                 // loss of urlbar contents for invalid URI errors (see bug 867957).
                 // Another reason to clear the userTypedValue is if this was an anchor
                 // navigation initiated by the user.
-                if (this.mBrowser.userTypedClear > 0 ||
+                if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
                     ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
                      aLocation.spec != "about:blank") ||
-                     (isSameDocument && this.mBrowser.inLoadURI)) {
+                    (isSameDocument && this.mBrowser.inLoadURI)) {
                   this.mBrowser.userTypedValue = null;
                 }
 
                 // If the browser was playing audio, we should remove the playing state.
                 if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
                   this.mTab.removeAttribute("soundplaying");
                   this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
                 }
@@ -1579,28 +1580,36 @@
             let filter = this._tabFilters.get(tab);
             let listener = this._tabListeners.get(tab);
             aBrowser.webProgress.removeProgressListener(filter);
             filter.removeProgressListener(listener);
 
             // We'll be creating a new listener, so destroy the old one.
             listener.destroy();
 
+            let oldUserTypedValue = aBrowser.userTypedValue;
+            let hadStartedLoad = aBrowser.didStartLoadSinceLastUserTyping();
+
             // Make sure the browser is destroyed so it unregisters from observer notifications
             aBrowser.destroy();
 
             // Make sure to restore the original droppedLinkHandler.
             let droppedLinkHandler = aBrowser.droppedLinkHandler;
 
             // Change the "remote" attribute.
             let parent = aBrowser.parentNode;
             parent.removeChild(aBrowser);
             aBrowser.setAttribute("remote", aShouldBeRemote ? "true" : "false");
             parent.appendChild(aBrowser);
 
+            aBrowser.userTypedValue = oldUserTypedValue;
+            if (hadStartedLoad) {
+              aBrowser.urlbarChangeTracker.startedLoad();
+            }
+
             aBrowser.droppedLinkHandler = droppedLinkHandler;
 
             // Switching a browser's remoteness will create a new frameLoader.
             // As frameLoaders start out with an active docShell we have to
             // deactivate it if this is not the selected tab's browser or the
             // browser window is minimized.
             aBrowser.docShellIsActive = (aBrowser == this.selectedBrowser &&
                                          window.windowState != window.STATE_MINIMIZED);
@@ -4152,20 +4161,16 @@
                   offset *= -1;
                 this.tabContainer.advanceSelectedTab(offset, true);
                 aEvent.preventDefault();
             }
           }
         ]]></body>
       </method>
 
-      <property name="userTypedClear"
-                onget="return this.mCurrentBrowser.userTypedClear;"
-                onset="return this.mCurrentBrowser.userTypedClear = val;"/>
-
       <property name="userTypedValue"
                 onget="return this.mCurrentBrowser.userTypedValue;"
                 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
 
       <method name="createTooltip">
         <parameter name="event"/>
         <body><![CDATA[
           event.stopPropagation();
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -1,29 +1,32 @@
 [DEFAULT]
-skip-if = buildapp == 'mulet'
+skip-if = (buildapp == 'mulet') || (os == 'linux') # Bug 1243103, Bug 1243398, etc.
 support-files =
   head.js
 
+[browser_newtab_1188015.js]
 [browser_newtab_background_captures.js]
 [browser_newtab_block.js]
 [browser_newtab_bug721442.js]
 [browser_newtab_bug722273.js]
-skip-if = true # Bug 1119906
 [browser_newtab_bug723102.js]
 [browser_newtab_bug723121.js]
 [browser_newtab_bug725996.js]
 [browser_newtab_bug734043.js]
 [browser_newtab_bug735987.js]
 [browser_newtab_bug752841.js]
 [browser_newtab_bug765628.js]
 [browser_newtab_bug876313.js]
 [browser_newtab_bug991111.js]
 [browser_newtab_bug991210.js]
 [browser_newtab_bug998387.js]
+[browser_newtab_bug1145428.js]
+[browser_newtab_bug1178586.js]
+[browser_newtab_bug1194895.js]
 [browser_newtab_disable.js]
 [browser_newtab_drag_drop.js]
 [browser_newtab_drag_drop_ext.js]
 [browser_newtab_drop_preview.js]
 [browser_newtab_enhanced.js]
 [browser_newtab_focus.js]
 [browser_newtab_perwindow_private_browsing.js]
 [browser_newtab_reportLinkAction.js]
@@ -38,12 +41,8 @@ support-files =
   searchEngine2xLogo.xml
   searchEngine1x2xLogo.xml
   ../general/searchSuggestionEngine.xml
   ../general/searchSuggestionEngine.sjs
 [browser_newtab_sponsored_icon_click.js]
 [browser_newtab_undo.js]
 [browser_newtab_unpin.js]
 [browser_newtab_update.js]
-[browser_newtab_bug1145428.js]
-[browser_newtab_bug1178586.js]
-[browser_newtab_bug1194895.js]
-[browser_newtab_1188015.js]
--- a/browser/base/content/test/urlbar/browser.ini
+++ b/browser/base/content/test/urlbar/browser.ini
@@ -48,16 +48,17 @@ support-files =
   authenticate.sjs
 [browser_urlbarDecode.js]
 [browser_urlbarDelete.js]
 [browser_urlbarEnter.js]
 [browser_urlbarEnterAfterMouseOver.js]
 skip-if = os == "linux" # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_urlbarHashChangeProxyState.js]
 [browser_urlbarKeepStateAcrossTabSwitches.js]
+[browser_urlbarPrivateBrowsingWindowChange.js]
 [browser_urlbarRevert.js]
 [browser_urlbarSearchSingleWordNotification.js]
 [browser_urlbarSearchSuggestions.js]
 support-files =
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
 [browser_urlbarSearchSuggestionsNotification.js]
 support-files =
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/urlbar/browser_urlbarPrivateBrowsingWindowChange.js
@@ -0,0 +1,43 @@
+"use strict";
+
+/**
+ * Test that when opening a private browsing window and typing in it before about:privatebrowsing
+ * loads, we don't clear the URL bar.
+ */
+add_task(function*() {
+  let urlbarTestValue = "Mary had a little lamb";
+  let win = OpenBrowserWindow({private: true});
+  let delayedStartupFinished = TestUtils.topicObserved("browser-delayed-startup-finished",
+                                                       subject => subject == win);
+  yield BrowserTestUtils.waitForEvent(win, "load");
+  let urlbar = win.document.getElementById("urlbar");
+  urlbar.value = urlbarTestValue;
+  // Need this so the autocomplete controller attaches:
+  let focusEv = new FocusEvent("focus", {});
+  urlbar.dispatchEvent(focusEv);
+  // And so we know input happened:
+  let inputEv = new InputEvent("input", {data: "", view: win, bubbles: true});
+  urlbar.onInput(inputEv);
+  // Check it worked:
+  is(urlbar.value, urlbarTestValue, "URL bar value should be there");
+  is(win.gBrowser.selectedBrowser.userTypedValue, urlbarTestValue, "browser object should know the url bar value");
+
+  let continueTest;
+  let continuePromise = new Promise(resolve => continueTest = resolve);
+  let wpl = {
+    onLocationChange(aWebProgress, aRequest, aLocation) {
+      if (aLocation && aLocation.spec == "about:privatebrowsing") {
+        continueTest();
+      }
+    },
+  };
+  win.gBrowser.addProgressListener(wpl);
+
+  yield continuePromise;
+  is(urlbar.value, urlbarTestValue,
+     "URL bar value should be the same once about:privatebrowsing has loaded");
+  is(win.gBrowser.selectedBrowser.userTypedValue, urlbarTestValue,
+     "browser object should still know url bar value once about:privatebrowsing has loaded");
+  win.gBrowser.removeProgressListener(wpl);
+  yield BrowserTestUtils.closeWindow(win);
+});
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -6,8 +6,9 @@ support-files =
   serviceworker.html
   worker.js
 
 [browser_usercontext.js]
 [browser_usercontextid_tabdrop.js]
 [browser_windowName.js]
 [browser_windowOpen.js]
 [browser_serviceworkers.js]
+[browser_broadcastchannel.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js
@@ -0,0 +1,76 @@
+let { classes: Cc, interfaces: Ci } = Components;
+
+const BASE_ORIGIN = "http://example.com";
+const URI = BASE_ORIGIN +
+  "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function* openTabInUserContext(uri, userContextId) {
+  // open the tab in the correct userContextId
+  let tab = gBrowser.addTab(uri, {userContextId});
+
+  // select tab and make sure its browser is focused
+  gBrowser.selectedTab = tab;
+  tab.ownerDocument.defaultView.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return {tab, browser};
+}
+
+add_task(function* setup() {
+  // make sure userContext is enabled.
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["privacy.userContext.enabled", true]
+    ]}, resolve);
+  });
+});
+
+add_task(function* test() {
+  let receiver = yield* openTabInUserContext(URI, 2);
+
+  let channelName = "contextualidentity-broadcastchannel";
+
+  // reflect the received message on title
+  let receiveMsg = ContentTask.spawn(receiver.browser, channelName,
+      function (name) {
+        return new Promise(resolve => {
+          content.window.bc = new content.window.BroadcastChannel(name);
+          content.window.bc.onmessage = function (e) {
+            content.document.title += e.data;
+            resolve();
+          }
+        });
+      });
+
+  let sender1 = yield* openTabInUserContext(URI, 1);
+  let sender2 = yield* openTabInUserContext(URI, 2);
+  sender1.message = "Message from user context #1";
+  sender2.message = "Message from user context #2";
+
+  // send a message from a tab in different user context first
+  // then send a message from a tab in the same user context
+  for (let sender of [sender1, sender2]) {
+    yield ContentTask.spawn(
+        sender.browser,
+        { name: channelName, message: sender.message },
+        function (opts) {
+          let bc = new content.window.BroadcastChannel(opts.name);
+          bc.postMessage(opts.message);
+        });
+  }
+
+  // make sure we have received a message
+  yield receiveMsg;
+
+  // Since sender1 sends before sender2, if the title is exactly
+  // sender2's message, sender1's message must've been blocked
+  is(receiver.browser.contentDocument.title, sender2.message,
+      "should only receive messages from the same user context");
+
+  gBrowser.removeTab(sender1.tab);
+  gBrowser.removeTab(sender2.tab);
+  gBrowser.removeTab(receiver.tab);
+});
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -770,20 +770,22 @@ var SessionStoreInternal = {
           }
         }
         break;
       case "SessionStore:restoreHistoryComplete":
         // Notify the tabbrowser that the tab chrome has been restored.
         let tabData = TabState.collect(tab);
 
         // wall-paper fix for bug 439675: make sure that the URL to be loaded
-        // is always visible in the address bar
+        // is always visible in the address bar if no other value is present
         let activePageData = tabData.entries[tabData.index - 1] || null;
         let uri = activePageData ? activePageData.url || null : null;
-        browser.userTypedValue = uri;
+        if (!browser.userTypedValue) {
+          browser.userTypedValue = uri;
+        }
 
         // If the page has a title, set it.
         if (activePageData) {
           if (activePageData.title) {
             tab.label = activePageData.title;
             tab.crop = "end";
           } else if (activePageData.url != "about:blank") {
             tab.label = activePageData.url;
@@ -813,21 +815,18 @@ var SessionStoreInternal = {
           // previously pending tab. Mark the tab as no longer pending.
           this.markTabAsRestoring(tab);
         } else {
           // If the user was typing into the URL bar when we crashed, but hadn't hit
           // enter yet, then we just need to write that value to the URL bar without
           // loading anything. This must happen after the load, as the load will clear
           // userTypedValue.
           let tabData = TabState.collect(tab);
-          if (tabData.userTypedValue && !tabData.userTypedClear) {
+          if (tabData.userTypedValue && !tabData.userTypedClear && !browser.userTypedValue) {
             browser.userTypedValue = tabData.userTypedValue;
-            if (data.didStartLoad) {
-              browser.userTypedClear++;
-            }
             win.URLBarSetURI();
           }
 
           // Remove state we don't need any longer.
           TabStateCache.update(browser, {
             userTypedValue: null, userTypedClear: null
           });
         }
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -124,22 +124,27 @@ var TabStateInternal = {
 
     // Store the tab icon.
     if (!("image" in tabData)) {
       let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
       tabData.image = tabbrowser.getIcon(tab);
     }
 
     // If there is a userTypedValue set, then either the user has typed something
-    // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
-    // is used to indicate whether the tab was in some sort of loading state with
-    // userTypedValue.
+    // in the URL bar, or a new tab was opened with a URI to load.
+    // If so, we also track whether we were still in the process of loading something.
     if (!("userTypedValue" in tabData) && browser.userTypedValue) {
       tabData.userTypedValue = browser.userTypedValue;
-      tabData.userTypedClear = browser.userTypedClear;
+      // We always used to keep track of the loading state as an integer, where
+      // '0' indicated the user had typed since the last load (or no load was
+      // ongoing), and any positive value indicated we had started a load since
+      // the last time the user typed in the URL bar. Mimic this to keep the
+      // session store representation in sync, even though we now represent this
+      // more explicitly:
+      tabData.userTypedClear = browser.didStartLoadSinceLastUserTyping() ? 1 : 0;
     }
 
     return tabData;
   },
 
   /**
    * Copy data for the given |browser| from the cache to |tabData|.
    *
--- a/browser/components/sessionstore/test/browser_522545.js
+++ b/browser/components/sessionstore/test/browser_522545.js
@@ -23,18 +23,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "about:blank",
          "No history entries still sets currentURI to about:blank");
       is(browser.userTypedValue, "example.com",
          "userTypedValue was correctly restored");
-      is(browser.userTypedClear, 0,
-         "userTypeClear restored as expected");
+      ok(!browser.didStartLoadSinceLastUserTyping(),
+         "We still know that no load is ongoing");
       is(gURLBar.value, "example.com",
          "Address bar's value correctly restored");
       // Change tabs to make sure address bar value gets updated
       gBrowser.selectedTab = gBrowser.tabContainer.getItemAtIndex(0);
       is(gURLBar.value, "about:mozilla",
          "Address bar's value correctly updated");
       runNextTest();
     });
@@ -55,18 +55,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.getBrowserAtIndex(1);
       is(browser.currentURI.spec, "about:blank",
          "No history entries still sets currentURI to about:blank");
       is(browser.userTypedValue, "example.org",
          "userTypedValue was correctly restored");
-      is(browser.userTypedClear, 0,
-         "userTypeClear restored as expected");
+      ok(!browser.didStartLoadSinceLastUserTyping(),
+         "We still know that no load is ongoing");
       is(gURLBar.value, "about:mozilla",
          "Address bar's value correctly restored");
       // Change tabs to make sure address bar value gets updated
       gBrowser.selectedTab = gBrowser.tabContainer.getItemAtIndex(1);
       is(gURLBar.value, "example.org",
          "Address bar's value correctly updated");
       runNextTest();
     });
@@ -88,18 +88,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "about:config",
          "browser.currentURI set to current entry in SH");
       is(browser.userTypedValue, "example.com",
          "userTypedValue was correctly restored");
-      is(browser.userTypedClear, 0,
-         "userTypeClear restored as expected");
+      ok(!browser.didStartLoadSinceLastUserTyping(),
+         "We still know that no load is ongoing");
       is(gURLBar.value, "example.com",
          "Address bar's value correctly restored to userTypedValue");
       runNextTest();
     });
   }
 
   // This tests the following use case:
   // User is in a tab with session history, presses back at some point, then
@@ -117,18 +117,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "about:mozilla",
          "browser.currentURI set to current entry in SH");
       is(browser.userTypedValue, "example.org",
          "userTypedValue was correctly restored");
-      is(browser.userTypedClear, 0,
-         "userTypeClear restored as expected");
+      ok(!browser.didStartLoadSinceLastUserTyping(),
+         "We still know that no load is ongoing");
       is(gURLBar.value, "example.org",
          "Address bar's value correctly restored to userTypedValue");
       runNextTest();
     });
   }
 
   // This test simulates lots of tabs opening at once and then quitting/crashing.
   function test_getBrowserState_lotsOfTabsOpening() {
@@ -154,26 +154,23 @@ function test() {
     function firstLocationChange() {
       let state = JSON.parse(ss.getBrowserState());
       let hasUTV = state.windows[0].tabs.some(function(aTab) {
         return aTab.userTypedValue && aTab.userTypedClear && !aTab.entries.length;
       });
 
       ok(hasUTV, "At least one tab has a userTypedValue with userTypedClear with no loaded URL");
 
-      BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(firstLoad);
+      BrowserTestUtils.waitForMessage(gBrowser.selectedBrowser.messageManager, "SessionStore:update").then(firstLoad);
     }
 
     function firstLoad() {
-      let state = JSON.parse(ss.getBrowserState());
-      let hasSH = state.windows[0].tabs.some(function(aTab) {
-        return !("userTypedValue" in aTab) && aTab.entries[0].url;
-      });
-
-      ok(hasSH, "At least one tab has its entry in SH");
+      let state = JSON.parse(ss.getTabState(gBrowser.selectedTab));
+      let hasSH = !("userTypedValue" in state) && state.entries[0].url;
+      ok(hasSH, "The selected tab has its entry in SH");
 
       runNextTest();
     }
 
     gBrowser.loadTabs(uris);
   }
 
   // This simulates setting a userTypedValue and ensures that just typing in the
@@ -184,28 +181,29 @@ function test() {
         tabs: [{ entries: [] }]
       }]
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       // Make sure this tab isn't loading and state is clear before we test.
       is(browser.userTypedValue, null, "userTypedValue is empty to start");
-      is(browser.userTypedClear, 0, "userTypedClear is 0 to start");
+      ok(!browser.didStartLoadSinceLastUserTyping(),
+         "Initially, no load should be ongoing");
 
       let inputText = "example.org";
       gURLBar.focus();
       gURLBar.value = inputText.slice(0, -1);
       EventUtils.synthesizeKey(inputText.slice(-1) , {});
 
       executeSoon(function () {
         is(browser.userTypedValue, "example.org",
            "userTypedValue was set when changing URLBar value");
-        is(browser.userTypedClear, 0,
-           "userTypedClear was not changed when changing URLBar value");
+        ok(!browser.didStartLoadSinceLastUserTyping(),
+           "No load started since changing URLBar value");
 
         // Now make sure ss gets these values too
         let newState = JSON.parse(ss.getBrowserState());
         is(newState.windows[0].tabs[0].userTypedValue, "example.org",
            "sessionstore got correct userTypedValue");
         is(newState.windows[0].tabs[0].userTypedClear, 0,
            "sessionstore got correct userTypedClear");
         runNextTest();
@@ -226,18 +224,18 @@ function test() {
     };
 
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "http://example.com/",
          "userTypedClear=2 caused userTypedValue to be loaded");
       is(browser.userTypedValue, null,
          "userTypedValue was null after loading a URI");
-      is(browser.userTypedClear, 0,
-         "userTypeClear reset to 0");
+      ok(!browser.didStartLoadSinceLastUserTyping(),
+         "We should have reset the load state when the tab loaded");
       is(gURLBar.textValue, gURLBar.trimValue("http://example.com/"),
          "Address bar's value set after loading URI");
       runNextTest();
     });
   }
 
 
   let tests = [test_newTabFocused, test_newTabNotFocused,
@@ -247,17 +245,25 @@ function test() {
   let originalState = JSON.parse(ss.getBrowserState());
   let state = {
     windows: [{
       tabs: [{ entries: [{ url: "about:blank" }] }]
     }]
   };
   function runNextTest() {
     if (tests.length) {
-      waitForBrowserState(state, tests.shift());
+      waitForBrowserState(state, function() {
+        gBrowser.selectedBrowser.userTypedValue = null;
+        URLBarSetURI();
+        (tests.shift())();
+      });
     } else {
-      waitForBrowserState(originalState, finish);
+      waitForBrowserState(originalState, function() {
+        gBrowser.selectedBrowser.userTypedValue = null;
+        URLBarSetURI();
+        finish();
+      });
     }
   }
 
   // Run the tests!
   runNextTest();
 }
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -530,23 +530,16 @@
 @RESPATH@/components/RemoteWebNavigation.js
 
 @RESPATH@/components/SlowScriptDebug.manifest
 @RESPATH@/components/SlowScriptDebug.js
 
 @RESPATH@/components/TVSimulatorService.js
 @RESPATH@/components/TVSimulatorService.manifest
 
-#ifndef RELEASE_BUILD
-@RESPATH@/components/InterAppComm.manifest
-@RESPATH@/components/InterAppCommService.js
-@RESPATH@/components/InterAppConnection.js
-@RESPATH@/components/InterAppMessagePort.js
-#endif
-
 #ifdef MOZ_ACTIVITIES
 @RESPATH@/components/SystemMessageCache.js
 @RESPATH@/components/SystemMessageInternal.js
 @RESPATH@/components/SystemMessageManager.js
 @RESPATH@/components/SystemMessageManager.manifest
 
 @RESPATH@/components/Activities.manifest
 @RESPATH@/components/ActivityProxy.js
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -146,16 +146,17 @@ this.TabCrashHandler = {
     let browser = message.target.browser;
 
     let childID = this.browserMap.get(browser.permanentKey);
     let dumpID = this.childMap.get(childID);
     if (!dumpID)
       return
 
     if (!message.data.sendReport) {
+      Services.telemetry.getHistogramById("FX_CONTENT_CRASH_NOT_SUBMITTED").add(1);
       this.prefs.setBoolPref("sendReport", false);
       return;
     }
 
     let {
       includeURL,
       comments,
       email,
@@ -241,49 +242,70 @@ this.TabCrashHandler = {
     this.pageListener.sendAsyncMessage("UpdateCount", {
       count: this._crashedTabCount,
     });
 
     let browser = message.target.browser;
 
     let dumpID = this.getDumpID(browser);
     if (!dumpID) {
+      // Make sure to only count once even if there are multiple windows
+      // that will all show about:tabcrashed.
+      if (this._crashedTabCount == 1) {
+        Services.telemetry.getHistogramById("FX_CONTENT_CRASH_DUMP_UNAVAILABLE").add(1);
+      }
+
       message.target.sendAsyncMessage("SetCrashReportAvailable", {
         hasReport: false,
       });
       return;
     }
 
     let sendReport = this.prefs.getBoolPref("sendReport");
     let includeURL = this.prefs.getBoolPref("includeURL");
     let emailMe = this.prefs.getBoolPref("emailMe");
 
     let data = { hasReport: true, sendReport, includeURL, emailMe };
     if (emailMe) {
       data.email = this.prefs.getCharPref("email", "");
     }
 
+    // Make sure to only count once even if there are multiple windows
+    // that will all show about:tabcrashed.
+    if (this._crashedTabCount == 1) {
+      Services.telemetry.getHistogramById("FX_CONTENT_CRASH_PRESENTED").add(1);
+    }
+
     message.target.sendAsyncMessage("SetCrashReportAvailable", data);
   },
 
-  onAboutTabCrashedUnload: function() {
+  onAboutTabCrashedUnload(message) {
     if (!this._crashedTabCount) {
       Cu.reportError("Can not decrement crashed tab count to below 0");
       return;
     }
     this._crashedTabCount--;
 
     // Broadcast to all about:tabcrashed pages a count of
     // how many about:tabcrashed pages exist, so that they
     // can decide whether or not to display the "Restore All
     // Crashed Tabs" button.
     this.pageListener.sendAsyncMessage("UpdateCount", {
       count: this._crashedTabCount,
     });
-  },
+
+    let browser = message.target.browser;
+    let childID = this.browserMap.get(browser.permanentKey);
+
+    // Make sure to only count once even if there are multiple windows
+    // that will all show about:tabcrashed.
+    if (this._crashedTabCount == 0 && childID) {
+      Services.telemetry.getHistogramById("FX_CONTENT_CRASH_NOT_SUBMITTED").add(1);
+    }
+},
 
   /**
    * For some <xul:browser>, return a crash report dump ID for that browser
    * if we have been informed of one. Otherwise, return null.
    *
    * @param browser (<xul:browser)
    *        The browser to try to get the dump ID for
    * @returns dumpID (String)
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -810,21 +810,21 @@ menuitem:not([type]):not(.menuitem-toolt
 #restore-button {
   list-style-image: url("chrome://global/skin/icons/Restore.gif");
 }
 #close-button {
   list-style-image: url("chrome://global/skin/icons/Close.gif");
 }
 
 /* Location bar */
-#nav-bar {
+#main-window {
   --urlbar-border-color: ThreeDShadow;
 }
 
-#nav-bar:-moz-lwtheme {
+#main-window:-moz-lwtheme {
   --urlbar-border-color: rgba(0,0,0,.3);
 }
 
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   padding: 0;
   border: 1px solid var(--urlbar-border-color);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1091,37 +1091,37 @@ toolbar[brighttext] .toolbarbutton-1 > .
   #restore-button:-moz-locale-dir(rtl),
   #close-button:-moz-locale-dir(rtl) {
     transform: scaleX(-1);
   }
 }
 
 /* ::::: Location Bar ::::: */
 
-#nav-bar {
+#main-window {
   --urlbar-border-color: ThreeDShadow;
   --urlbar-border-color-hover: var(--urlbar-border-color);
 }
 
-#nav-bar:-moz-lwtheme {
+#main-window:-moz-lwtheme {
   --urlbar-border-color: var(--toolbarbutton-hover-bordercolor);
 }
 
 @media (-moz-windows-default-theme) {
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7),
          (-moz-os-version: windows-win8) {
-    #nav-bar:not(:-moz-lwtheme) {
+    #main-window:not(:-moz-lwtheme) {
       --urlbar-border-color: hsla(210,54%,20%,.25) hsla(210,54%,20%,.27) hsla(210,54%,20%,.3);
       --urlbar-border-color-hover: hsla(210,54%,20%,.35) hsla(210,54%,20%,.37) hsla(210,54%,20%,.4);
     }
   }
 
   @media (-moz-os-version: windows-win10) {
-    #nav-bar:not(:-moz-lwtheme) {
+    #main-window:not(:-moz-lwtheme) {
       --urlbar-border-color: hsl(0,0%,90%);
       --urlbar-border-color-hover: hsl(0,0%,80%);
     }
   }
 }
 
 #urlbar,
 .searchbar-textbox {
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -884,17 +884,17 @@ crate_src_libdep = $(call mk_global_crat
 	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(HOST_CMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(HOST_CMMOBJS):
 	$(REPORT_BUILD_VERBOSE)
 	$(ELOG) $(HOST_CXX) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CXXFLAGS) $(HOST_CMMFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
 $(COBJS):
 	$(REPORT_BUILD_VERBOSE)
-	$(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 # DEFINES and ACDEFINES are needed here to enable conditional compilation of Q_OBJECTs:
 # 'moc' only knows about #defines it gets on the command line (-D...), not in
 # included headers like mozilla-config.h
 $(filter moc_%.cpp,$(CPPSRCS)): moc_%.cpp: %.h
 	$(REPORT_BUILD_VERBOSE)
 	$(ELOG) $(MOC) $(DEFINES) $(ACDEFINES) $< $(OUTOPTION)$@
 
@@ -923,46 +923,46 @@ ifdef MOZ_RUST
 
 $(RS_STATICLIB_CRATE_OBJ):
 	$(REPORT_BUILD)
 	$(RUSTC) $(RUSTFLAGS) --crate-type staticlib $(RLIB_EXTERN_CRATE_OPTIONS) --emit dep-info=$(MDDEPDIR)/$(call mk_global_crate_libname,$(RS_STATICLIB_CRATE_SRC)).pp,link=$@ $(RS_STATICLIB_CRATE_SRC)
 endif
 
 $(SOBJS):
 	$(REPORT_BUILD)
-	$(AS) -o $@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) $(TARGET_LOCAL_INCLUDES) -c $<
+	$(AS) -o $@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) -c $<
 
 $(CPPOBJS):
 	$(REPORT_BUILD_VERBOSE)
 	$(call BUILDSTATUS,OBJECT_FILE $@)
-	$(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(CMMOBJS):
 	$(REPORT_BUILD_VERBOSE)
-	$(ELOG) $(CCC) -o $@ -c $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(ELOG) $(CCC) -o $@ -c $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(CMOBJS):
 	$(REPORT_BUILD_VERBOSE)
-	$(ELOG) $(CC) -o $@ -c $(COMPILE_CFLAGS) $(COMPILE_CMFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(ELOG) $(CC) -o $@ -c $(COMPILE_CFLAGS) $(COMPILE_CMFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(filter %.s,$(CPPSRCS:%.cpp=%.s)): %.s: %.cpp $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
-	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(filter %.s,$(CPPSRCS:%.cc=%.s)): %.s: %.cc $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
-	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(filter %.s,$(CPPSRCS:%.cxx=%.s)): %.s: %.cpp $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
-	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(filter %.s,$(CSRCS:%.c=%.s)): %.s: %.c $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
-	$(CC) -S $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CC) -S $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 ifneq (,$(filter %.i,$(MAKECMDGOALS)))
 # Call as $(call _group_srcs,extension,$(SRCS)) - this will create a list
 # of the full sources, as well as the $(notdir) version. So:
 #   foo.cpp sub/bar.cpp
 # becomes:
 #   foo.cpp sub/bar.cpp bar.cpp
 #
@@ -980,37 +980,37 @@ VPATH += $(addprefix $(srcdir)/,$(sort $
 
 # Make preprocessed files PHONY so they are always executed, since they are
 # manual targets and we don't necessarily write to $@.
 .PHONY: $(_PREPROCESSED_CPP_FILES) $(_PREPROCESSED_CC_FILES) $(_PREPROCESSED_CXX_FILES) $(_PREPROCESSED_C_FILES) $(_PREPROCESSED_CMM_FILES)
 
 $(_PREPROCESSED_CPP_FILES): %.i: %.cpp $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
 	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
-	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(_PREPROCESSED_CC_FILES): %.i: %.cc $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
 	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
-	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(_PREPROCESSED_CXX_FILES): %.i: %.cxx $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
 	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
-	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(_PREPROCESSED_C_FILES): %.i: %.c $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
 	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
-	$(CC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 $(_PREPROCESSED_CMM_FILES): %.i: %.mm $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD_VERBOSE)
 	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
-	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
+	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $($(notdir $<)_FLAGS) $(_VPATH_SRCS)
 
 # Default to pre-processing the actual unified file. This can be overridden
 # at the command-line to pre-process only the individual source file.
 PP_UNIFIED ?= 1
 
 # PP_REINVOKE gets set on the sub-make to prevent us from going in an
 # infinite loop if the filename doesn't exist in the unified source files.
 ifndef PP_REINVOKE
--- a/db/sqlite3/src/moz.build
+++ b/db/sqlite3/src/moz.build
@@ -44,18 +44,20 @@ for var in ('SQLITE_SECURE_DELETE', 'SQL
 
 DEFINES['SQLITE_DEFAULT_PAGE_SIZE'] = 32768
 DEFINES['SQLITE_MAX_DEFAULT_PAGE_SIZE'] = 32768
 DEFINES['SQLITE_MAX_SCHEMA_RETRY'] = 25
 
 # -DSQLITE_WIN32_GETVERSIONEX=0 avoids using deprecated functions.
 # SQLite will just assume we are running on NT kinds of Windows. That's fine
 # because we don't support Win9x.
+# -DSQLITE_ALLOW_URI_AUTHORITY=1 enables uri authorities. See bug 879133.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DEFINES['SQLITE_WIN32_GETVERSIONEX'] = 0
+    DEFINES['SQLITE_ALLOW_URI_AUTHORITY'] = 1
 
 # -DSQLITE_ENABLE_LOCKING_STYLE=1 to help with AFP folders
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 1
 
 # sqlite defaults this to on on __APPLE_ but it breaks on newer iOS SDKs
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
     DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 0
--- a/devtools/server/tests/unit/test_memory_footprint.js
+++ b/devtools/server/tests/unit/test_memory_footprint.js
@@ -35,21 +35,21 @@ function init_server() {
 function add_browser_actors() {
   DebuggerServer.addBrowserActors();
   check_footprint("DebuggerServer.addBrowserActors()", 2000);
 }
 
 function connect_client() {
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect().then(function onConnect() {
-    check_footprint("DebuggerClient.connect()", 2000);
+    check_footprint("DebuggerClient.connect()", 2600);
   });
 }
 
 function list_tabs() {
   gClient.listTabs(function onListTabs(aResponse) {
-    check_footprint("DebuggerClient.listTabs()", 2600);
+    check_footprint("DebuggerClient.listTabs()", 3200);
   });
 }
 
 function close_client() {
   gClient.close(run_next_test);
 }
index 4a1cf83ac7bb2d3670f6903be34b383abd3bc510..cb3c7ffd2a53c249dbcab8cc851a0c4f4354a053
GIT binary patch
literal 895
zc$^FHW@Zs#U}E54IFdTgi7D`)iW>t1Lo5(;0dZztN@|5(MoDhY=0Km5{(38QJVH-s
zoIJbn@utSWm7SVpW~wt)EmxMA{W6eJ;hN%e%75jS$3X!FK6)qhgETyK^#VO~G%h^e
zbdmiuLuQ92Ljc@1htydfi|+>{tN_{~#mK<G52SMw^D@&?i%ay%Q<D-43PSrFxegf!
zxPG^FeQ|4BMApQ4hxaadd4t8rC`F0u(r#<d39ml=|32gAd#{dLp4?wmUU3JA2yL`m
zQpOl`Fu~+r?ZU#<6^G7najsm%e{|33_TXP#Mz2!Jj_H2LD&6w^Q17R{8B5Y-jxUV3
zWA^yMqUgxePj1XFKk{(;*X4!jGDgW;o{D!FT->x{;R3aV$wyYc&bzn$)a^|yiV@B!
z|GqWMP0UnDdbh1)^Q~3<5Kl1xJ#~B1UnkZF5jqY)o+}XZfPyeNKTp@t$U?6mFa0&o
zhvMf?Cp}6^NO<t|O93a-r5=Sd2U%3qr?B5=4zii2*QY3ReRBOA`vV6KoH=nqp>TUS
zbHJQ~m#>~rog3^Vj1+u=lm9yT9lIc654DLKW|Nr-@fPt0>D-W%2$qzbGd;4xn4xj`
z>iMUwmba*Z?EytUFj{~<l2D8e;sE;($wx+}W+01zR0R-o1$Z+u$uZ+jNFV@=Wri({
z3@FJ8Ss%6}fzX$SnLuEgaRogLd~0-OM2!e!Yq13-Lg!jWlz@e3#2q#W_lh#1hE#wz
RD;r3b6$q7pO5B-1JOGY~8lV6G
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -129,16 +129,17 @@
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIWebBrowserChrome3.h"
 #include "nsITabChild.h"
 #include "nsISiteSecurityService.h"
 #include "nsStructuredCloneContainer.h"
 #include "nsIStructuredCloneContainer.h"
 #ifdef MOZ_PLACES
 #include "nsIFaviconService.h"
+#include "mozIPlacesPendingOperation.h"
 #include "mozIAsyncFavicons.h"
 #endif
 #include "nsINetworkPredictor.h"
 
 // Editor-related
 #include "nsIEditingSession.h"
 
 #include "nsPIDOMWindow.h"
@@ -9407,21 +9408,22 @@ public:
     // Continue only if there is an associated favicon.
     if (!aFaviconURI) {
       return NS_OK;
     }
 
     MOZ_ASSERT(aDataLen == 0,
                "We weren't expecting the callback to deliver data.");
 
+    nsCOMPtr<mozIPlacesPendingOperation> po;
     return mSvc->SetAndFetchFaviconForPage(
       mNewURI, aFaviconURI, false,
       mInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE :
                            nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
-      nullptr, mLoadingPrincipal);
+      nullptr, mLoadingPrincipal, getter_AddRefs(po));
   }
 
 private:
   ~nsCopyFaviconCallback() {}
 
   nsCOMPtr<mozIAsyncFavicons> mSvc;
   nsCOMPtr<nsIURI> mNewURI;
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -1378,20 +1378,18 @@ nsSHistory::RemoveEntries(nsTArray<uint6
   bool didRemove = false;
   while (index > minIndex) {
     if (index != mIndex) {
       didRemove = RemoveDuplicate(index, index < mIndex) || didRemove;
     }
     --index;
   }
   if (didRemove && mRootDocShell) {
-    nsCOMPtr<nsIRunnable> ev =
-      NS_NewRunnableMethod(static_cast<nsDocShell*>(mRootDocShell),
-                           &nsDocShell::FireDummyOnLocationChange);
-    NS_DispatchToCurrentThread(ev);
+    NS_DispatchToCurrentThread(NewRunnableMethod(static_cast<nsDocShell*>(mRootDocShell),
+                                                 &nsDocShell::FireDummyOnLocationChange));
   }
 }
 
 void
 nsSHistory::RemoveDynEntries(int32_t aOldIndex, int32_t aNewIndex)
 {
   // Search for the entries which are in the current index,
   // but not in the new one.
--- a/dom/activities/ActivitiesService.jsm
+++ b/dom/activities/ActivitiesService.jsm
@@ -252,24 +252,16 @@ var Activities = {
     * - finds a list of matching activities.
     * - calls the UI glue to get the user choice.
     * - fire an system message of type "activity" to this app, sending the
     *   activity data as a payload.
     */
   startActivity: function activities_startActivity(aMsg) {
     debug("StartActivity: " + JSON.stringify(aMsg));
 
-    // The caller app will be killed by |assertAppHasStatus| if it doesn't
-    // fit our permission requirement.
-    let callerApp = this.callers[aMsg.id].mm;
-    if (aMsg.options.name === 'internal-system-engineering-mode' &&
-        !callerApp.assertAppHasStatus(Ci.nsIPrincipal.APP_STATUS_CERTIFIED)) {
-      return;
-    }
-
     let self = this;
     let successCb = function successCb(aResults) {
       debug(JSON.stringify(aResults));
 
       function getActivityChoice(aResultType, aResult) {
         switch(aResultType) {
           case Ci.nsIActivityUIGlueCallback.NATIVE_ACTIVITY: {
             self.callers[aMsg.id].mm.sendAsyncMessage("Activity:FireSuccess", {
@@ -379,23 +371,16 @@ var Activities = {
     };
 
     let errorCb = function errorCb(aError) {
       // Something unexpected happened. Should we send an error back?
       debug("Error in startActivity: " + aError + "\n");
     };
 
     let matchFunc = function matchFunc(aResult) {
-      let calleeApp = DOMApplicationRegistry.getAppByManifestURL(aResult.manifest);
-      // Only allow certified apps to handle this special activity
-      if (aMsg.options.name === 'internal-system-engineering-mode' &&
-          calleeApp.appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-        return false;
-      }
-
       // If the activity is in the developer mode activity list, only let the
       // system app be a provider.
       let isSystemApp = false;
       let isDevModeActivity = false;
       try {
         isSystemApp =
           aResult.manifest == Services.prefs.getCharPref("b2g.system_manifest_url");
         isDevModeActivity =
--- a/dom/activities/ActivityProxy.js
+++ b/dom/activities/ActivityProxy.js
@@ -57,24 +57,16 @@ ActivityProxy.prototype = {
     // Only let certified apps enumerate providers for this filter.
     if (aOptions.getFilterResults === true &&
         principal.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
       Services.DOMRequest.fireErrorAsync(this.activity, "SecurityError");
       Services.obs.notifyObservers(null, "Activity:Error", null);
       return;
     }
 
-    // Only let certified app to initiate this activitiy.
-    if (aOptions.name === 'internal-system-engineering-mode' &&
-        principal.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-      Services.DOMRequest.fireErrorAsync(this.activity, "SecurityError");
-      Services.obs.notifyObservers(null, "Activity:Error", null);
-      return;
-    }
-
     // Check the activities that are restricted to be used in dev mode.
     let devMode = false;
     let isDevModeActivity = false;
     try {
       devMode = Services.prefs.getBoolPref("dom.apps.developer_mode");
       isDevModeActivity =
         Services.prefs.getCharPref("dom.activities.developer_mode_only")
                       .split(",").indexOf(aOptions.name) !== -1;
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1231,17 +1231,17 @@ void
 Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
 {
   CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
 
   if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
     DoFinishNotificationImmediately();
   } else if (!mFinishNotificationTask.IsPending()) {
     RefPtr<nsRunnableMethod<Animation>> runnable =
-      NS_NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
+      NewRunnableMethod(this, &Animation::DoFinishNotificationImmediately);
     runtime->DispatchToMicroTask(runnable);
     mFinishNotificationTask = runnable;
   }
 }
 
 void
 Animation::ResetFinishedPromise()
 {
deleted file mode 100644
--- a/dom/apps/InterAppComm.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=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 "InterAppComm.h"
-#include "nsContentUtils.h"
-#include "nsPIDOMWindow.h"
-#include "nsJSPrincipals.h"
-#include "mozilla/Preferences.h"
-#include "AccessCheck.h"
-
-using namespace mozilla::dom;
-
-/* static */ bool
-InterAppComm::EnabledForScope(JSContext* /* unused */,
-                              JS::Handle<JSObject*> /* unused */)
-{
-  // Disable the constructors if they're disabled by the preference for sure.
-  if (!Preferences::GetBool("dom.inter-app-communication-api.enabled", false)) {
-    return false;
-  }
-
-  // Only expose the constructors to the chrome codes for Gecko internal uses.
-  // The content pages shouldn't be aware of the constructors.
-  return nsContentUtils::ThreadsafeIsCallerChrome();
-}
deleted file mode 100644
--- a/dom/apps/InterAppComm.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=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/. */
-
-#ifndef mozilla_dom_apps_InterAppComm_h
-#define mozilla_dom_apps_InterAppComm_h
-
-#include "mozilla/dom/MozInterAppMessageEvent.h"
-
-// Forward declarations.
-struct JSContext;
-class JSObject;
-
-namespace mozilla {
-namespace dom {
-
-class InterAppComm
-{
-public:
-  static bool EnabledForScope(JSContext* /* unused */,
-                              JS::Handle<JSObject*> /* unused */);
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_apps_InterAppComm_h
deleted file mode 100644
--- a/dom/apps/InterAppComm.manifest
+++ /dev/null
@@ -1,16 +0,0 @@
-component {9dbfa904-0718-11e3-8e77-0721a45514b8} InterAppConnection.js
-contract @mozilla.org/dom/inter-app-connection;1 {9dbfa904-0718-11e3-8e77-0721a45514b8}
-
-component {6a77e9e0-0645-11e3-b90b-73bb7c78e06a} InterAppConnection.js
-contract @mozilla.org/dom/inter-app-connection-request;1 {6a77e9e0-0645-11e3-b90b-73bb7c78e06a}
-
-component {c66e0f8c-e3cb-11e2-9e85-43ef6244b884} InterAppMessagePort.js
-contract @mozilla.org/dom/inter-app-message-port;1 {c66e0f8c-e3cb-11e2-9e85-43ef6244b884}
-
-component {3dd15ce6-e7be-11e2-82bc-77967e7a63e6} InterAppCommService.js
-contract @mozilla.org/inter-app-communication-service;1 {3dd15ce6-e7be-11e2-82bc-77967e7a63e6}
-category profile-after-change InterAppCommService @mozilla.org/inter-app-communication-service;1
-
-component {d7c7a466-f91d-11e2-812a-6fab12ece58e} InterAppConnection.js
-contract @mozilla.org/dom/system-messages/wrapper/connection;1 {d7c7a466-f91d-11e2-812a-6fab12ece58e}
-
deleted file mode 100644
--- a/dom/apps/InterAppCommService.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/InterAppCommService.jsm");
-
-const DEBUG = false;
-function debug(aMsg) {
-  dump("-- InterAppCommServiceProxy: " + Date.now() + ": " + aMsg + "\n");
-}
-
-function InterAppCommServiceProxy() {
-}
-
-InterAppCommServiceProxy.prototype = {
-  registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
-                               aDescription, aRules) {
-    InterAppCommService.
-      registerConnection(aKeyword, aHandlerPageURI, aManifestURI,
-                         aDescription, aRules);
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    if (aTopic != "profile-after-change") {
-      if (DEBUG) {
-        debug("Should receive 'profile-after-change' only.");
-      }
-      return;
-    }
-  },
-
-  classID: Components.ID("{3dd15ce6-e7be-11e2-82bc-77967e7a63e6}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService,
-                                         Ci.nsIObserver])
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommServiceProxy]);
deleted file mode 100644
--- a/dom/apps/InterAppCommService.jsm
+++ /dev/null
@@ -1,1126 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-this.EXPORTED_SYMBOLS = ["InterAppCommService"];
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const DEBUG = false;
-function debug(aMsg) {
-  dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
-}
-
-XPCOMUtils.defineLazyServiceGetter(this, "appsService",
-                                   "@mozilla.org/AppsService;1",
-                                   "nsIAppsService");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
-                                   "@mozilla.org/uuid-generator;1",
-                                   "nsIUUIDGenerator");
-
-XPCOMUtils.defineLazyServiceGetter(this, "messenger",
-                                   "@mozilla.org/system-message-internal;1",
-                                   "nsISystemMessagesInternal");
-
-const kMessages =["Webapps:Connect",
-                  "Webapps:GetConnections",
-                  "InterAppConnection:Cancel",
-                  "InterAppMessagePort:PostMessage",
-                  "InterAppMessagePort:Register",
-                  "InterAppMessagePort:Unregister",
-                  "child-process-shutdown"];
-
-/**
- * This module contains helpers for Inter-App Communication API [1] related
- * purposes, which plays the role of the central service receiving messages
- * from and interacting with the content processes.
- *
- * [1] https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
- */
-
-this.InterAppCommService = {
-  init: function() {
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-    Services.obs.addObserver(this, "webapps-clear-data", false);
-
-    kMessages.forEach(function(aMsg) {
-      ppmm.addMessageListener(aMsg, this);
-    }, this);
-
-    // This matrix is used for saving the inter-app connection info registered in
-    // the app manifest. The object literal is defined as below:
-    //
-    // {
-    //   "keyword1": {
-    //     "subAppManifestURL1": {
-    //       /* subscribed info */
-    //     },
-    //     "subAppManifestURL2": {
-    //       /* subscribed info */
-    //     },
-    //     ...
-    //   },
-    //   "keyword2": {
-    //     "subAppManifestURL3": {
-    //       /* subscribed info */
-    //     },
-    //     ...
-    //   },
-    //   ...
-    // }
-    //
-    // For example:
-    //
-    // {
-    //   "foo": {
-    //     "app://subApp1.gaiamobile.org/manifest.webapp": {
-    //       pageURL: "app://subApp1.gaiamobile.org/handler.html",
-    //       description: "blah blah",
-    //       rules: { ... }
-    //     },
-    //     "app://subApp2.gaiamobile.org/manifest.webapp": {
-    //       pageURL: "app://subApp2.gaiamobile.org/handler.html",
-    //       description: "blah blah",
-    //       rules: { ... }
-    //     }
-    //   },
-    //   "bar": {
-    //     "app://subApp3.gaiamobile.org/manifest.webapp": {
-    //       pageURL: "app://subApp3.gaiamobile.org/handler.html",
-    //       description: "blah blah",
-    //       rules: { ... }
-    //     }
-    //   }
-    // }
-    //
-    // TODO Bug 908999 - Update registered connections when app gets uninstalled.
-    this._registeredConnections = {};
-
-    // This matrix is used for saving the permitted connections, which allows
-    // the messaging between publishers and subscribers. The object literal is
-    // defined as below:
-    //
-    // {
-    //   "keyword1": {
-    //     "pubAppManifestURL1": [
-    //       "subAppManifestURL1",
-    //       "subAppManifestURL2",
-    //       ...
-    //     ],
-    //     "pubAppManifestURL2": [
-    //       "subAppManifestURL3",
-    //       "subAppManifestURL4",
-    //       ...
-    //     ],
-    //     ...
-    //   },
-    //   "keyword2": {
-    //     "pubAppManifestURL3": [
-    //       "subAppManifestURL5",
-    //       ...
-    //     ],
-    //     ...
-    //   },
-    //   ...
-    // }
-    //
-    // For example:
-    //
-    // {
-    //   "foo": {
-    //     "app://pubApp1.gaiamobile.org/manifest.webapp": [
-    //       "app://subApp1.gaiamobile.org/manifest.webapp",
-    //       "app://subApp2.gaiamobile.org/manifest.webapp"
-    //     ],
-    //     "app://pubApp2.gaiamobile.org/manifest.webapp": [
-    //       "app://subApp3.gaiamobile.org/manifest.webapp",
-    //       "app://subApp4.gaiamobile.org/manifest.webapp"
-    //     ]
-    //   },
-    //   "bar": {
-    //     "app://pubApp3.gaiamobile.org/manifest.webapp": [
-    //       "app://subApp5.gaiamobile.org/manifest.webapp",
-    //     ]
-    //   }
-    // }
-    //
-    // TODO Bug 908999 - Update allowed connections when app gets uninstalled.
-    this._allowedConnections = {};
-
-    // This matrix is used for saving the caller info from the content process,
-    // which is indexed by a random UUID, to know where to return the promise
-    // resolvser's callback when the prompt UI for allowing connections returns.
-    // An example of the object literal is shown as below:
-    //
-    // {
-    //   "fooID": {
-    //     outerWindowID: 12,
-    //     requestID: 34,
-    //     target: pubAppTarget1
-    //   },
-    //   "barID": {
-    //     outerWindowID: 56,
-    //     requestID: 78,
-    //     target: pubAppTarget2
-    //   }
-    // }
-    //
-    // where |outerWindowID| is the ID of the window requesting the connection,
-    //       |requestID| is the ID specifying the promise resolver to return,
-    //       |target| is the target of the process requesting the connection.
-    this._promptUICallers = {};
-
-    // This matrix is used for saving the pair of message ports, which is indexed
-    // by a random UUID, so that each port can know whom it should talk to.
-    // An example of the object literal is shown as below:
-    //
-    // {
-    //   "UUID1": {
-    //     keyword: "keyword1",
-    //     publisher: {
-    //       manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
-    //       target: pubAppTarget1,
-    //       pageURL: "app://pubApp1.gaiamobile.org/caller.html",
-    //       messageQueue: [...]
-    //     },
-    //     subscriber: {
-    //       manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
-    //       target: subAppTarget1,
-    //       pageURL: "app://pubApp1.gaiamobile.org/handler.html",
-    //       messageQueue: [...]
-    //     }
-    //   },
-    //   "UUID2": {
-    //     keyword: "keyword2",
-    //     publisher: {
-    //       manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
-    //       target: pubAppTarget2,
-    //       pageURL: "app://pubApp2.gaiamobile.org/caller.html",
-    //       messageQueue: [...]
-    //     },
-    //     subscriber: {
-    //       manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
-    //       target: subAppTarget2,
-    //       pageURL: "app://pubApp2.gaiamobile.org/handler.html",
-    //       messageQueue: [...]
-    //     }
-    //   }
-    // }
-    this._messagePortPairs = {};
-  },
-
-  /* These attributes main use is to allow testing this in an isolated way
-   * that doesn't depend on the app service, or the system messenger working on
-   * the test environment
-   */
-  get appsService() {
-    return this._appsService || appsService;
-  },
-  set appsService(aService) {
-    this._appsService = aService;
-  },
-  get messenger() {
-    return this._messenger || messenger;
-  },
-  set messenger(aMessenger) {
-    this._messenger = aMessenger;
-  },
-
-
-  /**
-   * Registration of a page that wants to be connected to other apps through
-   * the Inter-App Communication API.
-   *
-   * @param aKeyword        The connection's keyword.
-   * @param aHandlerPageURI The URI of the handler's page.
-   * @param aManifestURI    The webapp's manifest URI.
-   * @param aDescription    The connection's description.
-   * @param aRules          The connection's rules.
-   */
-  registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
-                               aDescription, aRules) {
-    let manifestURL = aManifestURI.spec;
-    let pageURL = aHandlerPageURI.spec;
-
-    if (DEBUG) {
-      debug("registerConnection: aKeyword: " + aKeyword +
-            " manifestURL: " + manifestURL + " pageURL: " + pageURL +
-            " aDescription: " + aDescription +
-            " aRules.minimumAccessLevel: " + aRules.minimumAccessLevel +
-            " aRules.manifestURLs: " + aRules.manifestURLs +
-            " aRules.pageURLs: " + aRules.pageURLs +
-            " aRules.installOrigins: " + aRules.installOrigins);
-    }
-
-    let subAppManifestURLs = this._registeredConnections[aKeyword];
-    if (!subAppManifestURLs) {
-      subAppManifestURLs = this._registeredConnections[aKeyword] = {};
-    }
-
-    subAppManifestURLs[manifestURL] = {
-      pageURL: pageURL,
-      description: aDescription,
-      rules: aRules,
-      manifestURL: manifestURL
-    };
-  },
-
-  _matchMinimumAccessLevel: function(aRules, aAppStatus) {
-    if (!aRules || !aRules.minimumAccessLevel) {
-      if (DEBUG) {
-        debug("rules.minimumAccessLevel is not available. No need to match.");
-      }
-      return true;
-    }
-
-    let minAccessLevel = aRules.minimumAccessLevel;
-    switch (minAccessLevel) {
-      case "web":
-        if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_INSTALLED ||
-            aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
-            aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-          return true;
-        }
-        break;
-      case "privileged":
-        if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
-            aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-          return true;
-        }
-        break;
-      case "certified":
-        if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
-          return true;
-        }
-        break;
-    }
-
-    if (DEBUG) {
-      debug("rules.minimumAccessLevel is not matched!" +
-            " minAccessLevel: " + minAccessLevel +
-            " aAppStatus : " + aAppStatus);
-    }
-    return false;
-  },
-
-  _matchManifestURLs: function(aRules, aManifestURL) {
-    if (!aRules || !Array.isArray(aRules.manifestURLs)) {
-      if (DEBUG) {
-        debug("rules.manifestURLs is not available. No need to match.");
-      }
-      return true;
-    }
-
-    let manifestURLs = aRules.manifestURLs;
-    if (manifestURLs.indexOf(aManifestURL) != -1) {
-      return true;
-    }
-
-    if (DEBUG) {
-      debug("rules.manifestURLs is not matched!" +
-            " manifestURLs: " + manifestURLs +
-            " aManifestURL : " + aManifestURL);
-    }
-    return false;
-  },
-
-  _matchPageURLs: function(aRules, aPageURL) {
-
-    if (!aRules || !aRules.pageURLs) {
-      if (DEBUG) {
-        debug("rules.pageURLs is not available. No need to match.");
-      }
-      return true;
-    }
-
-    if (!Array.isArray(aRules.pageURLs)) {
-      aRules.pageURLs = [aRules.pageURLs];
-    }
-
-    let pageURLs = aRules.pageURLs;
-    let isAllowed = false;
-    for (let i = 0, li = pageURLs.length; i < li && !isAllowed ; i++) {
-      let regExpAllowedURL = new RegExp(pageURLs[i]);
-      isAllowed = regExpAllowedURL.test(aPageURL);
-    }
-
-    if (DEBUG) {
-      debug("rules.pageURLs is " + (isAllowed ? "" : "not") + " matched!" +
-            " pageURLs: " + pageURLs +
-            " aPageURL: " + aPageURL);
-    }
-
-    return isAllowed;
-  },
-
-  _matchInstallOrigins: function(aRules, aInstallOrigin) {
-    if (!aRules || !Array.isArray(aRules.installOrigins)) {
-      if (DEBUG) {
-        debug("rules.installOrigins is not available. No need to match.");
-      }
-      return true;
-    }
-
-    let installOrigins = aRules.installOrigins;
-    if (installOrigins.indexOf(aInstallOrigin) != -1) {
-      return true;
-    }
-
-    if (DEBUG) {
-      debug("rules.installOrigins is not matched!" +
-            " installOrigins: " + installOrigins +
-            " installOrigin : " + aInstallOrigin);
-    }
-    return false;
-  },
-
-  // A connection is allowed if all the rules are matched.
-  // The publisher is matched against the rules defined by the subscriber on the
-  // manifest, and the subscriber is matched against the rules defined by the
-  // publisher on the call to connect.
-  // The possible rules for both subscribers and publishers are:
-  //  * minimumAccessLevel: "privileged"|"certified"|"web"|undefined
-  //     The default (non existant or undefined value) is "certified".
-  //     That means that if an explicit minimumAccessLevel rule does not
-  //     exist then the peer of the connection *must* be a certified app.
-  //  * pageURLs: Array of regExp of URLs. If the value exists, only the pages
-  //     whose URLs are explicitly declared on the array (matched) can connect.
-  //     Otherwise all pages can connect
-  //  * installOrigins: Array of origin URLs. If the value exist, only the apps
-  //    whose origins are on the array can connect. Otherwise, all origins are
-  //    allowed. This is only checked for non certified apps!
-  // The default value (empty or non existant rules) is:
-  //   * Only certified apps can connect
-  //   * Any originator/receiving page URLs are valid
-  //   * Any origin is valid.
-  _matchRules: function(aPubAppManifestURL, aPubRules,
-                        aSubAppManifestURL, aSubRules,
-                        aPubPageURL, aSubPageURL) {
-    let pubApp = this.appsService.getAppByManifestURL(aPubAppManifestURL);
-    let subApp = this.appsService.getAppByManifestURL(aSubAppManifestURL);
-
-    let isPubAppCertified =
-      (pubApp.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED);
-
-    let isSubAppCertified =
-      (subApp.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED);
-
-#ifndef NIGHTLY_BUILD
-
-    if (!isPubAppCertified || !isSubAppCertified) {
-      if (DEBUG) {
-        debug("Only certified apps are allowed to do connections.");
-      }
-      return false;
-    }
-
-#else
-
-    let numSubRules =  (aSubRules && Object.keys(aSubRules).length) || 0;
-    let numPubRules =  (aPubRules && Object.keys(aPubRules).length) || 0;
-
-    if ((!isSubAppCertified && !numPubRules) ||
-        (!isPubAppCertified && !numSubRules)) {
-      if (DEBUG) {
-        debug("If there aren't rules defined only certified apps are allowed " +
-              "to do connections.");
-      }
-      return false;
-    }
-
-#endif
-
-    if (!aPubRules && !aSubRules) {
-      if (DEBUG) {
-        debug("No rules for publisher and subscriber. No need to match.");
-      }
-      return true;
-    }
-
-    // Check minimumAccessLevel.
-    if (!this._matchMinimumAccessLevel(aPubRules, subApp.appStatus) ||
-        !this._matchMinimumAccessLevel(aSubRules, pubApp.appStatus)) {
-      return false;
-    }
-
-    // Check manifestURLs.
-    if (!this._matchManifestURLs(aPubRules, aSubAppManifestURL) ||
-        !this._matchManifestURLs(aSubRules, aPubAppManifestURL)) {
-      return false;
-    }
-
-    // Check pageURLs.
-    if (!this._matchPageURLs(aPubRules, aSubPageURL) ||
-        !this._matchPageURLs(aSubRules, aPubPageURL)) {
-      return false;
-    }
-
-    // Check installOrigins. Note that we only check the install origin for the
-    // non-certified app, because the certified app doesn't have install origin.
-    if ((!isSubAppCertified &&
-         !this._matchInstallOrigins(aPubRules, subApp.installOrigin)) ||
-        (!isPubAppCertified &&
-         !this._matchInstallOrigins(aSubRules, pubApp.installOrigin))) {
-      return false;
-    }
-
-    if (DEBUG) debug("All rules are matched.");
-    return true;
-  },
-
-  _dispatchMessagePorts: function(aKeyword, aPubAppManifestURL,
-                                  aAllowedSubAppManifestURLs,
-                                  aTarget, aOuterWindowID, aRequestID,
-                                  aPubPageURL) {
-    if (DEBUG) {
-      debug("_dispatchMessagePorts: aKeyword: " + aKeyword +
-            " aPubAppManifestURL: " + aPubAppManifestURL +
-            " aAllowedSubAppManifestURLs: " + aAllowedSubAppManifestURLs +
-            " aPubPageURL: " + aPubPageURL);
-    }
-
-    if (aAllowedSubAppManifestURLs.length == 0) {
-      if (DEBUG) debug("No apps are allowed to connect. Returning.");
-      aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
-                               { oid: aOuterWindowID, requestID: aRequestID });
-      return;
-    }
-
-    let subAppManifestURLs = this._registeredConnections[aKeyword];
-    if (!subAppManifestURLs) {
-      if (DEBUG) debug("No apps are subscribed to connect. Returning.");
-      aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
-                               { oid: aOuterWindowID, requestID: aRequestID });
-      return;
-    }
-
-    let messagePortIDs = [];
-    aAllowedSubAppManifestURLs.forEach(function(aAllowedSubAppManifestURL) {
-      let subscribedInfo = subAppManifestURLs[aAllowedSubAppManifestURL];
-      if (!subscribedInfo) {
-        if (DEBUG) {
-          debug("The sunscribed info is not available. Skipping: " +
-                aAllowedSubAppManifestURL);
-        }
-        return;
-      }
-
-      // The message port ID is aimed for identifying the coupling targets
-      // to deliver messages with each other. This ID is centrally generated
-      // by the parent and dispatched to both the sender and receiver ends
-      // for creating their own message ports respectively.
-      let messagePortID = UUIDGenerator.generateUUID().toString();
-      this._messagePortPairs[messagePortID] = {
-        keyword: aKeyword,
-        publisher: {
-          manifestURL: aPubAppManifestURL
-        },
-        subscriber: {
-          manifestURL: aAllowedSubAppManifestURL
-        }
-      };
-
-      // Fire system message to deliver the message port to the subscriber.
-      this.messenger.sendMessage("connection",
-        { keyword: aKeyword,
-          messagePortID: messagePortID,
-          pubPageURL: aPubPageURL},
-        Services.io.newURI(subscribedInfo.pageURL, null, null),
-        Services.io.newURI(subscribedInfo.manifestURL, null, null));
-
-      messagePortIDs.push(messagePortID);
-    }, this);
-
-    if (messagePortIDs.length == 0) {
-      if (DEBUG) debug("No apps are subscribed to connect. Returning.");
-      aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
-                               { oid: aOuterWindowID, requestID: aRequestID });
-      return;
-    }
-
-    // Return the message port IDs to open the message ports for the publisher.
-    if (DEBUG) debug("messagePortIDs: " + messagePortIDs);
-    aTarget.sendAsyncMessage("Webapps:Connect:Return:OK",
-                             { keyword: aKeyword,
-                               messagePortIDs: messagePortIDs,
-                               oid: aOuterWindowID, requestID: aRequestID });
-  },
-
-  /**
-   * Fetch the subscribers that are currently allowed to connect.
-   *
-   * @param aKeyword           The connection's keyword.
-   * @param aPubAppManifestURL The manifest URL of the publisher.
-   *
-   * @param return an array of manifest URLs of the subscribers.
-   */
-  _getAllowedSubAppManifestURLs: function(aKeyword, aPubAppManifestURL) {
-    let allowedPubAppManifestURLs = this._allowedConnections[aKeyword];
-    if (!allowedPubAppManifestURLs) {
-      return [];
-    }
-
-    let allowedSubAppManifestURLs =
-      allowedPubAppManifestURLs[aPubAppManifestURL];
-    if (!allowedSubAppManifestURLs) {
-      return [];
-    }
-
-    return allowedSubAppManifestURLs;
-  },
-
-  /**
-   * Add the newly selected apps into the allowed connections and return the
-   * aggregated allowed connections.
-   *
-   * @param aKeyword           The connection's keyword.
-   * @param aPubAppManifestURL The manifest URL of the publisher.
-   * @param aSelectedApps      An array of the subscribers' information.
-   *
-   * @param return an array of manifest URLs of the subscribers.
-   */
-  _addSelectedApps: function(aKeyword, aPubAppManifestURL, aSelectedApps) {
-    let allowedPubAppManifestURLs = this._allowedConnections[aKeyword];
-
-    // Add a new entry for |aKeyword|.
-    if (!allowedPubAppManifestURLs) {
-      allowedPubAppManifestURLs = this._allowedConnections[aKeyword] = {};
-    }
-
-    let allowedSubAppManifestURLs =
-      allowedPubAppManifestURLs[aPubAppManifestURL];
-
-    // Add a new entry for |aPubAppManifestURL|.
-    if (!allowedSubAppManifestURLs) {
-      allowedSubAppManifestURLs =
-        allowedPubAppManifestURLs[aPubAppManifestURL] = [];
-    }
-
-    // Add the selected apps into the existing set of allowed connections.
-    aSelectedApps.forEach(function(aSelectedApp) {
-      let allowedSubAppManifestURL = aSelectedApp.manifestURL;
-      if (allowedSubAppManifestURLs.indexOf(allowedSubAppManifestURL) == -1) {
-        allowedSubAppManifestURLs.push(allowedSubAppManifestURL);
-      }
-    });
-
-    return allowedSubAppManifestURLs;
-  },
-
-  _connect: function(aMessage, aTarget) {
-    let keyword = aMessage.keyword;
-    let pubRules = aMessage.rules;
-    let pubPageURL = aMessage.pubPageURL;
-    let pubAppManifestURL = aMessage.manifestURL;
-    let outerWindowID = aMessage.outerWindowID;
-    let requestID = aMessage.requestID;
-
-    let subAppManifestURLs = this._registeredConnections[keyword];
-    if (!subAppManifestURLs) {
-      if (DEBUG) {
-        debug("No apps are subscribed for this connection. Returning.");
-      }
-      this._dispatchMessagePorts(keyword, pubAppManifestURL, [],
-                                 aTarget, outerWindowID, requestID, pubPageURL);
-      return;
-    }
-
-    // Fetch the apps that are currently allowed to connect, so that users
-    // don't need to select/allow them again, which means we only pop up the
-    // prompt UI for the *new* connections.
-    let allowedSubAppManifestURLs =
-      this._getAllowedSubAppManifestURLs(keyword, pubAppManifestURL);
-
-    // Check rules to see if a subscribed app is allowed to connect.
-    let appsToSelect = [];
-    for (let subAppManifestURL in subAppManifestURLs) {
-      if (allowedSubAppManifestURLs.indexOf(subAppManifestURL) != -1) {
-        if (DEBUG) {
-          debug("Don't need to select again. Skipping: " + subAppManifestURL);
-        }
-        continue;
-      }
-
-      // Only rule-matched publishers/subscribers are allowed to connect.
-      let subscribedInfo = subAppManifestURLs[subAppManifestURL];
-      let subRules = subscribedInfo.rules;
-
-      let matched =
-        this._matchRules(pubAppManifestURL, pubRules,
-                         subAppManifestURL, subRules,
-                         pubPageURL, subscribedInfo.pageURL);
-      if (!matched) {
-        if (DEBUG) {
-          debug("Rules are not matched. Skipping: " + subAppManifestURL);
-        }
-        continue;
-      }
-
-      appsToSelect.push({
-        manifestURL: subAppManifestURL,
-        description: subscribedInfo.description
-      });
-    }
-
-    if (appsToSelect.length == 0) {
-      if (DEBUG) {
-        debug("No additional apps need to be selected for this connection. " +
-              "Just dispatch message ports for the existing connections.");
-      }
-
-      this._dispatchMessagePorts(keyword, pubAppManifestURL,
-                                 allowedSubAppManifestURLs,
-                                 aTarget, outerWindowID, requestID, pubPageURL);
-      return;
-    }
-
-    // Remember the caller info with an UUID so that we can know where to
-    // return the promise resolver's callback when the prompt UI returns.
-    let callerID = UUIDGenerator.generateUUID().toString();
-    this._promptUICallers[callerID] = {
-      outerWindowID: outerWindowID,
-      requestID: requestID,
-      target: aTarget
-    };
-
-    let glue = Cc["@mozilla.org/dom/apps/inter-app-comm-ui-glue;1"]
-        .createInstance(Ci.nsIInterAppCommUIGlue);
-    if (glue) {
-      glue.selectApps(callerID, pubAppManifestURL, keyword, appsToSelect).then(
-        function(aData) {
-          aData.pubPageURL = pubPageURL;
-          this._handleSelectedApps(aData);
-        }.bind(this),
-        function(aError) {
-          if (DEBUG) {
-            debug("Error occurred in the UI glue component. " + aError);
-          }
-
-          // Resolve the caller as if there were no selected apps.
-          this._handleSelectedApps({ callerID: callerID,
-                                     keyword: keyword,
-                                     manifestURL: pubAppManifestURL,
-                                     pubPageURL: pubPageURL,
-                                     selectedApps: [] });
-        }.bind(this)
-      );
-    } else {
-      if (DEBUG) {
-        debug("Error! The UI glue component is not implemented.");
-      }
-
-      // Resolve the caller as if there were no selected apps.
-      this._handleSelectedApps({ callerID: callerID,
-                                 keyword: keyword,
-                                 manifestURL: pubAppManifestURL,
-                                 pubPageURL: pubPageURL,
-                                 selectedApps: [] });
-    }
-  },
-
-  _getConnections: function(aMessage, aTarget) {
-    let outerWindowID = aMessage.outerWindowID;
-    let requestID = aMessage.requestID;
-
-    let connections = [];
-    for (let keyword in this._allowedConnections) {
-      let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-      for (let allowedPubAppManifestURL in allowedPubAppManifestURLs) {
-        let allowedSubAppManifestURLs =
-          allowedPubAppManifestURLs[allowedPubAppManifestURL];
-        allowedSubAppManifestURLs.forEach(function(allowedSubAppManifestURL) {
-          connections.push({ keyword: keyword,
-                             pubAppManifestURL: allowedPubAppManifestURL,
-                             subAppManifestURL: allowedSubAppManifestURL });
-        });
-      }
-    }
-
-    aTarget.sendAsyncMessage("Webapps:GetConnections:Return:OK",
-                             { connections: connections,
-                               oid: outerWindowID, requestID: requestID });
-  },
-
-  _cancelConnection: function(aMessage) {
-    let keyword = aMessage.keyword;
-    let pubAppManifestURL = aMessage.pubAppManifestURL;
-    let subAppManifestURL = aMessage.subAppManifestURL;
-
-    let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-    if (!allowedPubAppManifestURLs) {
-      if (DEBUG) debug("keyword is not found: " + keyword);
-      return;
-    }
-
-    let allowedSubAppManifestURLs =
-      allowedPubAppManifestURLs[pubAppManifestURL];
-    if (!allowedSubAppManifestURLs) {
-      if (DEBUG) debug("publisher is not found: " + pubAppManifestURL);
-      return;
-    }
-
-    let index = allowedSubAppManifestURLs.indexOf(subAppManifestURL);
-    if (index == -1) {
-      if (DEBUG) debug("subscriber is not found: " + subAppManifestURL);
-      return;
-    }
-
-    if (DEBUG) debug("Cancelling the connection.");
-    allowedSubAppManifestURLs.splice(index, 1);
-
-    // Clean up the parent entries if needed.
-    if (allowedSubAppManifestURLs.length == 0) {
-      delete allowedPubAppManifestURLs[pubAppManifestURL];
-      if (Object.keys(allowedPubAppManifestURLs).length == 0) {
-        delete this._allowedConnections[keyword];
-      }
-    }
-
-    if (DEBUG) debug("Unregistering message ports based on this connection.");
-    let messagePortIDs = [];
-    for (let messagePortID in this._messagePortPairs) {
-      let pair = this._messagePortPairs[messagePortID];
-      if (pair.keyword == keyword &&
-          pair.publisher.manifestURL == pubAppManifestURL &&
-          pair.subscriber.manifestURL == subAppManifestURL) {
-        messagePortIDs.push(messagePortID);
-      }
-    }
-    messagePortIDs.forEach(function(aMessagePortID) {
-      delete this._messagePortPairs[aMessagePortID];
-    }, this);
-  },
-
-  _identifyMessagePort: function(aMessagePortID, aManifestURL) {
-    let pair = this._messagePortPairs[aMessagePortID];
-    if (!pair) {
-      if (DEBUG) {
-        debug("Error! The message port ID is invalid: " + aMessagePortID +
-              ", which should have been generated by parent.");
-      }
-      return null;
-    }
-
-    // Check it the message port is for publisher.
-    if (pair.publisher.manifestURL == aManifestURL) {
-      return { pair: pair, isPublisher: true };
-    }
-
-    // Check it the message port is for subscriber.
-    if (pair.subscriber.manifestURL == aManifestURL) {
-      return { pair: pair, isPublisher: false };
-    }
-
-    if (DEBUG) {
-      debug("Error! The manifest URL is invalid: " + aManifestURL +
-            ", which might be a hacked app.");
-    }
-    return null;
-  },
-
-  _registerMessagePort: function(aMessage, aTarget) {
-    let messagePortID = aMessage.messagePortID;
-    let manifestURL = aMessage.manifestURL;
-    let pageURL = aMessage.pageURL;
-
-    let identity = this._identifyMessagePort(messagePortID, manifestURL);
-    if (!identity) {
-      if (DEBUG) {
-        debug("Cannot identify the message port. Failed to register.");
-      }
-      return;
-    }
-
-    if (DEBUG) debug("Registering message port for " + manifestURL);
-    let pair = identity.pair;
-    let isPublisher = identity.isPublisher;
-
-    let sender = isPublisher ? pair.publisher : pair.subscriber;
-    sender.target = aTarget;
-    sender.pageURL = pageURL;
-    sender.messageQueue = [];
-
-    // Check if the other port has queued messages. Deliver them if needed.
-    if (DEBUG) {
-      debug("Checking if the other port used to send messages but queued.");
-    }
-    let receiver = isPublisher ? pair.subscriber : pair.publisher;
-    if (receiver.messageQueue) {
-      while (receiver.messageQueue.length) {
-        let message = receiver.messageQueue.shift();
-        if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
-        sender.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
-                                       { message: message,
-                                         manifestURL: sender.manifestURL,
-                                         pageURL: sender.pageURL,
-                                         messagePortID: messagePortID });
-      }
-    }
-  },
-
-  _unregisterMessagePort: function(aMessage) {
-    let messagePortID = aMessage.messagePortID;
-    let manifestURL = aMessage.manifestURL;
-
-    let identity = this._identifyMessagePort(messagePortID, manifestURL);
-    if (!identity) {
-      if (DEBUG) {
-        debug("Cannot identify the message port. Failed to unregister.");
-      }
-      return;
-    }
-
-    if (DEBUG) {
-      debug("Unregistering message port for " + manifestURL);
-    }
-
-    let receiver = identity.isPublisher ? identity.pair.subscriber
-                                        : identity.pair.publisher;
-    receiver.target.sendAsyncMessage("InterAppMessagePort:OnClose",
-                                     { manifestURL: receiver.manifestURL,
-                                       pageURL: receiver.pageURL,
-                                       messagePortID: messagePortID });
-
-    delete this._messagePortPairs[messagePortID];
-  },
-
-  _removeTarget: function(aTarget) {
-    if (!aTarget) {
-      if (DEBUG) debug("Error! aTarget cannot be null/undefined in any way.");
-      return
-    }
-
-    if (DEBUG) debug("Unregistering message ports based on this target.");
-    let messagePortIDs = [];
-    for (let messagePortID in this._messagePortPairs) {
-      let pair = this._messagePortPairs[messagePortID];
-      if (pair.publisher.target === aTarget ||
-          pair.subscriber.target === aTarget) {
-        messagePortIDs.push(messagePortID);
-        // Send a shutdown message to the part of the pair that is still alive.
-        let actor = pair.publisher.target === aTarget ? pair.subscriber
-                                                      : pair.publisher;
-        actor.target.sendAsyncMessage("InterAppMessagePort:Shutdown",
-          { manifestURL: actor.manifestURL,
-            pageURL: actor.pageURL,
-            messagePortID: messagePortID });
-      }
-    }
-    messagePortIDs.forEach(function(aMessagePortID) {
-      delete this._messagePortPairs[aMessagePortID];
-    }, this);
-  },
-
-  _postMessage: function(aMessage) {
-    let messagePortID = aMessage.messagePortID;
-    let manifestURL = aMessage.manifestURL;
-    let message = aMessage.message;
-
-    let identity = this._identifyMessagePort(messagePortID, manifestURL);
-    if (!identity) {
-      if (DEBUG) debug("Cannot identify the message port. Failed to post.");
-      return;
-    }
-
-    let pair = identity.pair;
-    let isPublisher = identity.isPublisher;
-
-    let receiver = isPublisher ? pair.subscriber : pair.publisher;
-    if (!receiver.target) {
-      if (DEBUG) {
-        debug("The receiver's target is not ready yet. Queuing the message.");
-      }
-      let sender = isPublisher ? pair.publisher : pair.subscriber;
-      sender.messageQueue.push(message);
-      return;
-    }
-
-    if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
-    receiver.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
-                                     { manifestURL: receiver.manifestURL,
-                                       pageURL: receiver.pageURL,
-                                       messagePortID: messagePortID,
-                                       message: message });
-  },
-
-  _handleSelectedApps: function(aData) {
-    let callerID = aData.callerID;
-    let caller = this._promptUICallers[callerID];
-    if (!caller) {
-      if (DEBUG) debug("Error! Cannot find the caller.");
-      return;
-    }
-
-    delete this._promptUICallers[callerID];
-
-    let outerWindowID = caller.outerWindowID;
-    let requestID = caller.requestID;
-    let target = caller.target;
-
-    let pubAppManifestURL = aData.manifestURL;
-    let pubPageURL = aData.pubPageURL;
-    let keyword = aData.keyword;
-    let selectedApps = aData.selectedApps;
-
-    let allowedSubAppManifestURLs;
-    if (selectedApps.length == 0) {
-      // Only do the connections for the existing allowed subscribers because
-      // no new apps are selected to connect.
-      if (DEBUG) debug("No new apps are selected to connect.");
-
-      allowedSubAppManifestURLs =
-        this._getAllowedSubAppManifestURLs(keyword, pubAppManifestURL);
-    } else {
-      // Do connections for for the existing allowed subscribers and the newly
-      // selected subscribers.
-      if (DEBUG) debug("Some new apps are selected to connect.");
-
-      allowedSubAppManifestURLs =
-        this._addSelectedApps(keyword, pubAppManifestURL, selectedApps);
-    }
-
-    // Finally, dispatch the message ports for the allowed connections,
-    // including the old connections and the newly selected connection.
-    this._dispatchMessagePorts(keyword, pubAppManifestURL,
-                               allowedSubAppManifestURLs,
-                               target, outerWindowID, requestID, pubPageURL);
-  },
-
-  receiveMessage: function(aMessage) {
-    if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
-    let message = aMessage.json;
-    let target = aMessage.target;
-
-    // To prevent the hacked child process from sending commands to parent
-    // to do illegal connections, we need to check its manifest URL.
-    if (aMessage.name !== "child-process-shutdown" &&
-        // TODO: fix bug 988142 to re-enable "InterAppMessagePort:Unregister".
-        aMessage.name !== "InterAppMessagePort:Unregister" &&
-        kMessages.indexOf(aMessage.name) != -1) {
-      if (!target.assertContainApp(message.manifestURL)) {
-        if (DEBUG) {
-          debug("Got message from a process carrying illegal manifest URL.");
-        }
-        return null;
-      }
-    }
-
-    switch (aMessage.name) {
-      case "Webapps:Connect":
-        this._connect(message, target);
-        break;
-      case "Webapps:GetConnections":
-        this._getConnections(message, target);
-        break;
-      case "InterAppConnection:Cancel":
-        this._cancelConnection(message);
-        break;
-      case "InterAppMessagePort:PostMessage":
-        this._postMessage(message);
-        break;
-      case "InterAppMessagePort:Register":
-        this._registerMessagePort(message, target);
-        break;
-      case "InterAppMessagePort:Unregister":
-        this._unregisterMessagePort(message);
-        break;
-      case "child-process-shutdown":
-        this._removeTarget(target);
-        break;
-    }
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    switch (aTopic) {
-      case "xpcom-shutdown":
-        Services.obs.removeObserver(this, "xpcom-shutdown");
-        Services.obs.removeObserver(this, "webapps-clear-data");
-        kMessages.forEach(function(aMsg) {
-          ppmm.removeMessageListener(aMsg, this);
-        }, this);
-        ppmm = null;
-        break;
-      case "webapps-clear-data":
-        let params =
-          aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
-        if (!params) {
-          if (DEBUG) {
-            debug("Error updating registered/allowed connections for an " +
-                  "uninstalled app.");
-          }
-          return;
-        }
-
-        // Only update registered/allowed connections for apps.
-        if (params.browserOnly) {
-          if (DEBUG) {
-            debug("Only update registered/allowed connections for apps.");
-          }
-          return;
-        }
-
-        let manifestURL =
-                     this.appsService.getManifestURLByLocalId(params.appId);
-        if (!manifestURL) {
-          if (DEBUG) {
-            debug("Error updating registered/allowed connections for an " +
-                  "uninstalled app.");
-          }
-          return;
-        }
-
-        // Update registered connections.
-        for (let keyword in this._registeredConnections) {
-          let subAppManifestURLs = this._registeredConnections[keyword];
-          if (subAppManifestURLs[manifestURL]) {
-            delete subAppManifestURLs[manifestURL];
-            if (DEBUG) {
-              debug("Remove " + manifestURL + " from registered connections " +
-                    "due to app uninstallation.");
-            }
-          }
-        }
-
-        // Update allowed connections.
-        for (let keyword in this._allowedConnections) {
-          let allowedPubAppManifestURLs = this._allowedConnections[keyword];
-          if (allowedPubAppManifestURLs[manifestURL]) {
-            delete allowedPubAppManifestURLs[manifestURL];
-            if (DEBUG) {
-              debug("Remove " + manifestURL + " (as a pub app) from allowed " +
-                    "connections due to app uninstallation.");
-            }
-          }
-
-          for (let pubAppManifestURL in allowedPubAppManifestURLs) {
-            let subAppManifestURLs = allowedPubAppManifestURLs[pubAppManifestURL];
-            for (let i = subAppManifestURLs.length - 1; i >= 0; i--) {
-              if (subAppManifestURLs[i] === manifestURL) {
-                subAppManifestURLs.splice(i, 1);
-                if (DEBUG) {
-                  debug("Remove " + manifestURL + " (as a sub app to pub " +
-                        pubAppManifestURL + ") from allowed connections " +
-                        "due to app uninstallation.");
-                }
-              }
-            }
-          }
-        }
-        debug("Finish updating registered/allowed connections for an " +
-              "uninstalled app.");
-        break;
-    }
-  }
-};
-
-InterAppCommService.init();
deleted file mode 100644
--- a/dom/apps/InterAppConnection.js
+++ /dev/null
@@ -1,149 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
-XPCOMUtils.defineLazyServiceGetter(this, "appsService",
-                                   "@mozilla.org/AppsService;1",
-                                   "nsIAppsService");
-
-const DEBUG = false;
-function debug(aMsg) {
-  dump("-- InterAppConnection: " + Date.now() + ": " + aMsg + "\n");
-}
-
-/**
- * MozInterAppConnection implementation.
- */
-
-function InterAppConnection() {
-  if (DEBUG) debug("InterAppConnection()");
-  this.keyword = null;
-  this.publisher = null;
-  this.subscriber = null;
-};
-
-InterAppConnection.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  classDescription: "MozInterAppConnection",
-
-  classID: Components.ID("{9dbfa904-0718-11e3-8e77-0721a45514b8}"),
-
-  contractID: "@mozilla.org/dom/inter-app-connection;1",
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver]),
-
-  __init: function(aKeyword, aPublisher, aSubscriber) {
-    if (DEBUG) {
-      debug("__init: aKeyword: " + aKeyword +
-            " aPublisher: " + aPublisher + " aSubscriber: " + aSubscriber);
-    }
-    this.keyword = aKeyword;
-    this.publisher = aPublisher;
-    this.subscriber = aSubscriber;
-  },
-
-  // Ci.nsIDOMGlobalPropertyInitializer implementation.
-  init: function(aWindow) {
-    if (DEBUG) debug("init");
-
-    this.initDOMRequestHelper(aWindow, []);
-    let principal = aWindow.document.nodePrincipal;
-    this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
-  },
-
-  cancel: function() {
-    if (DEBUG) debug("cancel");
-
-    cpmm.sendAsyncMessage("InterAppConnection:Cancel",
-                          { keyword: this.keyword,
-                            pubAppManifestURL: this.publisher,
-                            subAppManifestURL: this.subscriber,
-                            manifestURL: this._manifestURL });
-  }
-};
-
-
-/**
- * MozInterAppConnectionRequest implementation.
- */
-
-function InterAppConnectionRequest() {
-  if (DEBUG) debug("InterAppConnectionRequest()");
-  this.keyword = null;
-  this.port = null;
-  this.from = null;
-};
-
-InterAppConnectionRequest.prototype = {
-  classDescription: "MozInterAppConnectionRequest",
-
-  classID: Components.ID("{6a77e9e0-0645-11e3-b90b-73bb7c78e06a}"),
-
-  contractID: "@mozilla.org/dom/inter-app-connection-request;1",
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
-
-  __init: function(aKeyword, aPort, aFrom) {
-    if (DEBUG) debug("__init: aKeyword: " + aKeyword + " aPort: " + aPort +
-                     " aFrom: " + aFrom);
-    this.keyword = aKeyword;
-    this.port = aPort;
-    this.from = aFrom;
-  }
-};
-
-/**
- * InterAppConnectionRequestWrapper implementation.
- *
- * This implements nsISystemMessagesWrapper.wrapMessage(), which provides a
- * plugable way to wrap a "connection" type system message.
- *
- * Please see SystemMessageManager.js to know how it customizes the wrapper.
- */
-
-function InterAppConnectionRequestWrapper() {
-  if (DEBUG) debug("InterAppConnectionRequestWrapper()");
-}
-
-InterAppConnectionRequestWrapper.prototype = {
-  // nsISystemMessagesWrapper implementation.
-  wrapMessage: function(aMessage, aWindow) {
-    if (DEBUG) debug("wrapMessage: " + JSON.stringify(aMessage));
-
-    let port = new aWindow.MozInterAppMessagePort(aMessage.messagePortID);
-    let connectionRequest =
-      new aWindow.MozInterAppConnectionRequest(aMessage.keyword, port,
-                                               aMessage.pubPageURL);
-
-    return connectionRequest;
-  },
-
-  classDescription: "InterAppConnectionRequestWrapper",
-
-  classID: Components.ID("{d7c7a466-f91d-11e2-812a-6fab12ece58e}"),
-
-  contractID: "@mozilla.org/dom/system-messages/wrapper/connection;1",
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
-}
-
-
-this.NSGetFactory =
-  XPCOMUtils.generateNSGetFactory([InterAppConnection,
-                                   InterAppConnectionRequest,
-                                   InterAppConnectionRequestWrapper]);
deleted file mode 100644
--- a/dom/apps/InterAppMessagePort.js
+++ /dev/null
@@ -1,280 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// TODO Bug 907060 Per off-line discussion, after the MessagePort is done
-// at Bug 643325, we will start to refactorize the common logic of both
-// Inter-App Communication and Shared Worker. For now, we hope to design an
-// MozInterAppMessagePort to meet the timeline, which still follows exactly
-// the same interface and semantic as the MessagePort is. In the future,
-// we can then align it back to MessagePort with backward compatibility.
-
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-
-const DEBUG = false;
-function debug(aMsg) {
-  dump("-- InterAppMessagePort: " + Date.now() + ": " + aMsg + "\n");
-}
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
-XPCOMUtils.defineLazyServiceGetter(this, "appsService",
-                                   "@mozilla.org/AppsService;1",
-                                   "nsIAppsService");
-
-const kMessages = ["InterAppMessagePort:OnClose",
-                   "InterAppMessagePort:OnMessage",
-                   "InterAppMessagePort:Shutdown"];
-
-function InterAppMessagePort() {
-  if (DEBUG) debug("InterAppMessagePort()");
-};
-
-InterAppMessagePort.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  classDescription: "MozInterAppMessagePort",
-
-  classID: Components.ID("{c66e0f8c-e3cb-11e2-9e85-43ef6244b884}"),
-
-  contractID: "@mozilla.org/dom/inter-app-message-port;1",
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver]),
-
-  // Ci.nsIDOMGlobalPropertyInitializer implementation.
-  init: function(aWindow) {
-    if (DEBUG) debug("Calling init().");
-
-    this.initDOMRequestHelper(aWindow, kMessages);
-
-    let principal = aWindow.document.nodePrincipal;
-    this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
-    this._pageURL = principal.URI.specIgnoringRef;
-
-    // Remove query string.
-    this._pageURL = this._pageURL.split("?")[0];
-
-    this._started = false;
-    this._closed = false;
-    this._messageQueue = [];
-    this._deferredClose = false;
-  },
-
-  // WebIDL implementation for constructor.
-  __init: function(aMessagePortID) {
-    if (DEBUG) {
-      debug("Calling __init(): aMessagePortID: " + aMessagePortID);
-    }
-
-    this._messagePortID = aMessagePortID;
-
-    cpmm.sendAsyncMessage("InterAppMessagePort:Register",
-                          { messagePortID: this._messagePortID,
-                            manifestURL: this._manifestURL,
-                            pageURL: this._pageURL });
-  },
-
-  // DOMRequestIpcHelper implementation.
-  uninit: function() {
-    if (DEBUG) debug("Calling uninit().");
-
-    // When the message port is uninitialized, we need to disentangle the
-    // coupling ports, as if the close() method had been called.
-    if (this._closed) {
-      if (DEBUG) debug("close() has been called. Don't need to close again.");
-      return;
-    }
-
-    this.close();
-  },
-
-  postMessage: function(aMessage) {
-    if (DEBUG) debug("Calling postMessage().");
-
-    if (this._closed) {
-      if (DEBUG) debug("close() has been called. Cannot post message.");
-      return;
-    }
-
-    cpmm.sendAsyncMessage("InterAppMessagePort:PostMessage",
-                          { messagePortID: this._messagePortID,
-                            manifestURL: this._manifestURL,
-                            message: aMessage });
-  },
-
-  start: function() {
-    // Begin dispatching messages received on the port.
-    if (DEBUG) debug("Calling start().");
-
-    if (this._closed) {
-      if (DEBUG) debug("close() has been called. Cannot call start().");
-      return;
-    }
-
-    if (this._started) {
-      if (DEBUG) debug("start() has been called. Don't need to start again.");
-      return;
-    }
-
-    // When a port's port message queue is enabled, the event loop must use it
-    // as one of its task sources.
-    this._started = true;
-    while (this._messageQueue.length) {
-      let message = this._messageQueue.shift();
-      this._dispatchMessage(message);
-    }
-
-    if (this._deferredClose) {
-      this._dispatchClose();
-    }
-  },
-
-  close: function() {
-    // Disconnecting the port, so that it is no longer active.
-    if (DEBUG) debug("Calling close().");
-
-    if (this._closed) {
-      if (DEBUG) debug("close() has been called. Don't need to close again.");
-      return;
-    }
-
-    this._closed = true;
-    this._deferredClose = false;
-    this._messageQueue.length = 0;
-
-    // When this method called on a local port that is entangled with another
-    // port, must cause the user agent to disentangle the coupling ports.
-    cpmm.sendAsyncMessage("InterAppMessagePort:Unregister",
-                          { messagePortID: this._messagePortID,
-                            manifestURL: this._manifestURL });
-
-    this.removeMessageListeners(kMessages);
-
-    this._dispatchClose();
-  },
-
-  get onmessage() {
-    if (DEBUG) debug("Getting onmessage handler.");
-
-    return this.__DOM_IMPL__.getEventHandler("onmessage");
-  },
-
-  set onmessage(aHandler) {
-    if (DEBUG) debug("Setting onmessage handler.");
-
-    this.__DOM_IMPL__.setEventHandler("onmessage", aHandler);
-
-    // The first time a MessagePort object's onmessage IDL attribute is set,
-    // the port's message queue must be enabled, as if the start() method had
-    // been called.
-    if (this._started) {
-      if (DEBUG) debug("start() has been called. Don't need to start again.");
-      return;
-    }
-
-    this.start();
-  },
-
-  get onclose() {
-    if (DEBUG) debug("Getting onclose handler.");
-    return this.__DOM_IMPL__.getEventHandler("onclose");
-  },
-
-  set onclose(aHandler) {
-    if (DEBUG) debug("Setting onclose handler.");
-    this.__DOM_IMPL__.setEventHandler("onclose", aHandler);
-  },
-
-  _dispatchMessage: function _dispatchMessage(aMessage) {
-    let wrappedMessage = Cu.cloneInto(aMessage, this._window);
-    if (DEBUG) {
-      debug("_dispatchMessage: wrappedMessage: " +
-            JSON.stringify(wrappedMessage));
-    }
-
-    let event = new this._window
-                    .MozInterAppMessageEvent("message",
-                                             { data: wrappedMessage });
-    this.__DOM_IMPL__.dispatchEvent(event);
-  },
-
-  _dispatchClose() {
-    if (DEBUG) debug("_dispatchClose");
-    let event = new this._window.Event("close", {
-      bubbles: true,
-      cancelable: true
-    });
-    this.__DOM_IMPL__.dispatchEvent(event);
-  },
-
-  receiveMessage: function(aMessage) {
-    if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
-
-    let message = aMessage.json;
-    if (message.manifestURL != this._manifestURL ||
-        message.pageURL != this._pageURL ||
-        message.messagePortID != this._messagePortID) {
-      if (DEBUG) debug("The message doesn't belong to this page. Returning. " +
-                       uneval(message));
-      return;
-    }
-
-    switch (aMessage.name) {
-      case "InterAppMessagePort:OnMessage":
-        if (this._closed) {
-          if (DEBUG) debug("close() has been called. Drop the message.");
-          return;
-        }
-
-        if (!this._started) {
-          if (DEBUG) debug("Not yet called start(). Queue up the message.");
-          this._messageQueue.push(message.message);
-          return;
-        }
-
-        this._dispatchMessage(message.message);
-        break;
-
-      case "InterAppMessagePort:OnClose":
-        if (this._closed) {
-          if (DEBUG) debug("close() has been called. Drop the message.");
-          return;
-        }
-
-        // It is possible that one side of the port posts messages and calls
-        // close() before calling start() or setting the onmessage handler. In
-        // that case we need to queue the messages and defer the onclose event
-        // until the messages are delivered to the other side of the port.
-        if (!this._started) {
-          if (DEBUG) debug("Not yet called start(). Defer close notification.");
-          this._deferredClose = true;
-          return;
-        }
-
-        this._dispatchClose();
-        break;
-
-      case "InterAppMessagePort:Shutdown":
-        this.close();
-        break;
-
-      default:
-        dump("WARNING - Invalid InterAppMessagePort message type " +
-             aMessage.name + "\n");
-        break;
-    }
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppMessagePort]);
-
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -447,21 +447,16 @@ this.PermissionsTable =  { geolocation: 
                            },
                            "settings:wallpaper.image": {
                              app: DENY_ACTION,
                              privileged: ALLOW_ACTION,
                              certified: ALLOW_ACTION,
                              access: ["read", "write"],
                              additional: ["settings-api"]
                            },
-                           "engineering-mode": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
                            "tv": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "before-after-keyboard-event": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
--- a/dom/apps/Webapps.js
+++ b/dom/apps/Webapps.js
@@ -604,45 +604,16 @@ WebappsApplication.prototype = {
         requestID: this.getRequestId(request)
       });
     } else {
       Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER");
     }
     return request;
   },
 
-  connect: function(aKeyword, aRules) {
-    this.addMessageListeners(["Webapps:Connect:Return:OK",
-                              "Webapps:Connect:Return:KO"]);
-    return this.createPromiseWithId((aResolverId) => {
-      let from = this._window.location.origin + this._window.location.pathname;
-      cpmm.sendAsyncMessage("Webapps:Connect", {
-        keyword: aKeyword,
-        rules: aRules,
-        manifestURL: this.manifestURL,
-        pubPageURL: from,
-        outerWindowID: this._id,
-        topWindowID: this._topId,
-        requestID: aResolverId
-      });
-    });
-  },
-
-  getConnections: function() {
-    this.addMessageListeners("Webapps:GetConnections:Return:OK");
-    return this.createPromiseWithId((aResolverId) => {
-      cpmm.sendAsyncMessage("Webapps:GetConnections", {
-        manifestURL: this.manifestURL,
-        outerWindowID: this._id,
-        topWindowID: this._topId,
-        requestID: aResolverId
-      });
-    });
-  },
-
   addReceipt: function(receipt) {
     let request = this.createRequest();
 
     this.addMessageListeners(["Webapps:AddReceipt:Return:OK",
                               "Webapps:AddReceipt:Return:KO"]);
 
     cpmm.sendAsyncMessage("Webapps:AddReceipt", { manifestURL: this.manifestURL,
                                                   receipt: receipt,
@@ -740,20 +711,17 @@ WebappsApplication.prototype = {
 
     aIsError ? Services.DOMRequest.fireError(req, msg.error)
              : Services.DOMRequest.fireSuccess(req, msg.result);
   },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
     let req;
-    if (aMessage.name == "Webapps:Connect:Return:OK" ||
-        aMessage.name == "Webapps:Connect:Return:KO" ||
-        aMessage.name == "Webapps:GetConnections:Return:OK" ||
-        aMessage.name == "Webapps:Export:Return" ||
+    if (aMessage.name == "Webapps:Export:Return" ||
         aMessage.name == "Webapps:GetLocalizedValue:Return") {
       req = this.takePromiseResolver(msg.requestID);
     } else {
       req = this.takeRequest(msg.requestID);
     }
 
     if (msg.oid !== this._id || !req) {
       return;
@@ -769,43 +737,16 @@ WebappsApplication.prototype = {
         this.removeMessageListeners(["Webapps:Launch:Return:OK",
                                      "Webapps:Launch:Return:KO"]);
         Services.DOMRequest.fireSuccess(req, null);
         break;
       case "Webapps:ClearBrowserData:Return":
         this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireSuccess(req, null);
         break;
-      case "Webapps:Connect:Return:OK":
-        this.removeMessageListeners(["Webapps:Connect:Return:OK",
-                                     "Webapps:Connect:Return:KO"]);
-        let messagePorts = new this._window.Array();
-        msg.messagePortIDs.forEach((aPortID) => {
-          let port = new this._window.MozInterAppMessagePort(aPortID);
-          messagePorts.push(port);
-        });
-        req.resolve(messagePorts);
-        break;
-      case "Webapps:Connect:Return:KO":
-        this.removeMessageListeners(["Webapps:Connect:Return:OK",
-                                     "Webapps:Connect:Return:KO"]);
-        req.reject("No connections registered");
-        break;
-      case "Webapps:GetConnections:Return:OK":
-        this.removeMessageListeners(aMessage.name);
-        let connections = new this._window.Array();
-        msg.connections.forEach((aConnection) => {
-          let connection =
-            new this._window.MozInterAppConnection(aConnection.keyword,
-                                                   aConnection.pubAppManifestURL,
-                                                   aConnection.subAppManifestURL);
-          connections.push(connection);
-        });
-        req.resolve(connections);
-        break;
       case "Webapps:AddReceipt:Return:OK":
         this.removeMessageListeners(["Webapps:AddReceipt:Return:OK",
                                      "Webapps:AddReceipt:Return:KO"]);
         this.__DOM_IMPL__._clearCachedReceiptsValue();
         this._proxy.receipts = msg.receipts;
         Services.DOMRequest.fireSuccess(req, null);
         break;
       case "Webapps:AddReceipt:Return:KO":
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -137,21 +137,16 @@ const chromeWindowType = "navigator:brow
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
-XPCOMUtils.defineLazyGetter(this, "interAppCommService", function() {
-  return Cc["@mozilla.org/inter-app-communication-service;1"]
-         .getService(Ci.nsIInterAppCommService);
-});
-
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
 XPCOMUtils.defineLazyGetter(this, "msgmgr", function() {
   return Cc["@mozilla.org/system-message-internal;1"]
          .getService(Ci.nsISystemMessagesInternal);
 });
@@ -861,104 +856,28 @@ this.DOMApplicationRegistry = {
                                                 aApp.manifestURL,
                                                 aApp.origin,
                                                 aManifest)) {
         msgmgr.registerPage(messageName, handlerPageURI, manifestURI);
       }
     });
   },
 
-  // |aEntryPoint| is either the entry_point name or the null in which case we
-  // use the root of the manifest.
-  //
-  // TODO Bug 908094 Refine _registerInterAppConnectionsForEntryPoint(...).
-  _registerInterAppConnectionsForEntryPoint: function(aManifest, aApp,
-                                                      aEntryPoint) {
-    let root = aManifest;
-    if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
-      root = aManifest.entry_points[aEntryPoint];
-    }
-
-    let connections = root.connections;
-    if (!connections) {
-      return;
-    }
-
-    if ((typeof connections) !== "object") {
-      debug("|connections| is not an object. Skipping: " + connections);
-      return;
-    }
-
-    let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
-    let launchPathURI = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint),
-                                           null, null);
-    let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
-
-    for (let keyword in connections) {
-      let connection = connections[keyword];
-
-      // Resolve the handler path from origin. If |handler_path| is absent,
-      // use |launch_path| as default.
-      let fullHandlerPath;
-      let handlerPath = connection.handler_path;
-      if (handlerPath) {
-        try {
-          fullHandlerPath = manifest.resolveURL(handlerPath);
-        } catch(e) {
-          debug("Connection's handler path is invalid. Skipping: keyword: " +
-                keyword + " handler_path: " + handlerPath);
-          continue;
-        }
-      }
-      let handlerPageURI = fullHandlerPath
-                           ? Services.io.newURI(fullHandlerPath, null, null)
-                           : launchPathURI;
-
-      if (SystemMessagePermissionsChecker
-            .isSystemMessagePermittedToRegister("connection",
-                                                aApp.manifestURL,
-                                                aApp.origin,
-                                                aManifest)) {
-        msgmgr.registerPage("connection", handlerPageURI, manifestURI);
-      }
-
-      interAppCommService.
-        registerConnection(keyword,
-                           handlerPageURI,
-                           manifestURI,
-                           connection.description,
-                           connection.rules);
-    }
-  },
-
   _registerSystemMessages: function(aManifest, aApp) {
     this._registerSystemMessagesForEntryPoint(aManifest, aApp, null);
 
     if (!aManifest.entry_points) {
       return;
     }
 
     for (let entryPoint in aManifest.entry_points) {
       this._registerSystemMessagesForEntryPoint(aManifest, aApp, entryPoint);
     }
   },
 
-  _registerInterAppConnections: function(aManifest, aApp) {
-    this._registerInterAppConnectionsForEntryPoint(aManifest, aApp, null);
-
-    if (!aManifest.entry_points) {
-      return;
-    }
-
-    for (let entryPoint in aManifest.entry_points) {
-      this._registerInterAppConnectionsForEntryPoint(aManifest, aApp,
-                                                     entryPoint);
-    }
-  },
-
   // |aEntryPoint| is either the entry_point name or the null in which case we
   // use the root of the manifest.
   _createActivitiesToRegister: function(aManifest, aApp, aEntryPoint,
                                         aRunUpdate, aUninstall) {
     let activitiesToRegister = [];
     let root = aManifest;
     if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
       root = aManifest.entry_points[aEntryPoint];
@@ -1118,17 +1037,16 @@ this.DOMApplicationRegistry = {
         app.role = localeManifest.role;
         this._saveWidgetsFullPath(localeManifest, app);
 
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(manifest.redirects);
         }
         app.kind = this.appKind(app, aResult.manifest);
         this._registerSystemMessages(manifest, app);
-        this._registerInterAppConnections(manifest, app);
         appsToRegister.push({ manifest: manifest, app: app });
         UserCustomizations.register(app);
         Langpacks.register(app, manifest);
       });
       this._safeToClone.resolve();
       this._registerActivitiesForApps(appsToRegister, aRunUpdate);
     });
   },
@@ -2000,17 +1918,16 @@ this.DOMApplicationRegistry = {
     aApp.role = manifest.role ? manifest.role : "";
 
     if (supportSystemMessages()) {
       if (aOldManifest) {
         this._unregisterActivities(aOldManifest, aApp);
       }
       this._registerSystemMessages(aNewManifest, aApp);
       this._registerActivities(aNewManifest, aApp, true);
-      this._registerInterAppConnections(aNewManifest, aApp);
     } else {
       // Nothing else to do but notifying we're ready.
       this.notifyAppsRegistryReady();
     }
 
     // Update user customizations and langpacks.
     if (aOldManifest) {
       UserCustomizations.unregister(aApp);
--- a/dom/apps/moz.build
+++ b/dom/apps/moz.build
@@ -4,35 +4,22 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
 MOCHITEST_CHROME_MANIFESTS += [
-    'tests/b2g_chrome.ini',
     'tests/chrome.ini'
 ]
 
-EXPORTS.mozilla.dom += [
-    'InterAppComm.h',
-]
-
-SOURCES += [
-    'InterAppComm.cpp',
-]
-
 EXTRA_COMPONENTS += [
     'AppsService.js',
     'AppsService.manifest',
-    'InterAppComm.manifest',
-    'InterAppCommService.js',
-    'InterAppConnection.js',
-    'InterAppMessagePort.js',
     'Webapps.js',
     'Webapps.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'AppDownloadManager.jsm',
     'AppsServiceChild.jsm',
     'FreeSpaceWatcher.jsm',
@@ -43,22 +30,12 @@ EXTRA_JS_MODULES += [
     'PermissionsTable.jsm',
     'StoreTrustAnchor.jsm',
     'UserCustomizations.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'AppsUtils.jsm',
     'ImportExport.jsm',
-    'InterAppCommService.jsm',
     'OperatorApps.jsm',
     'ScriptPreloader.jsm',
     'Webapps.jsm',
 ]
-
-if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wshadow']
-
-FINAL_LIBRARY = 'xul'
-
-LOCAL_INCLUDES += [
-    '/js/xpconnect/wrappers',
-]
deleted file mode 100644
--- a/dom/apps/tests/b2g_chrome.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[DEFAULT]
-skip-if = buildapp != 'b2g'
-support-files =
-  iac/*
-
-[test_iac.html]
deleted file mode 100644
--- a/dom/apps/tests/iac/README.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-subscriber.list and publisher.zip contain the lists of files that are part of each app.
-
-To update the packages of both apps when changing one of those files listed on *.list, run makezips.sh.
deleted file mode 100755
--- a/dom/apps/tests/iac/makezips.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-rm publisher/publisher.zip
-rm subscriber/subscriber.zip
-cd publisher
-zip publisher.zip `cat publisher.list`
-cd ../subscriber
-zip subscriber.zip `cat subscriber.list`
-cd ..
deleted file mode 100644
--- a/dom/apps/tests/iac/publisher/index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>Publisher app for IAC API</title>
-    <script src="test.js"></script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/apps/tests/iac/publisher/manifest.webapp
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "name": "IAC publisher app",
-  "launch_path": "/index.html",
-  "type": "certified"
-}
deleted file mode 100644
--- a/dom/apps/tests/iac/publisher/publisher.list
+++ /dev/null
@@ -1,3 +0,0 @@
-manifest.webapp
-index.html
-test.js
deleted file mode 100644
index dcfc3cdf7ff8e61ab0d58bad60bb8be8b69fccae..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/apps/tests/iac/publisher/test.js
+++ /dev/null
@@ -1,46 +0,0 @@
-function ok(aCondition, aMessage) {
-  if (aCondition) {
-    alert("OK: " + aMessage);
-  } else {
-    alert("KO: " + aMessage);
-  }
-}
-
-function ready() {
-  alert("READY");
-}
-
-let _port = null;
-let responseReceived = false;
-
-function onmessage(message) {
-  responseReceived = (message.data == "response");
-  ok(responseReceived, "response received");
-}
-
-function onclose() {
-  ok(true, "onclose received");
-  if (responseReceived) {
-    ready();
-  }
-}
-
-(function makeConnection() {
-  ok(true, "Connecting");
-  navigator.mozApps.getSelf().onsuccess = event => {
-    ok(true, "Got self");
-    let app = event.target.result;
-    app.connect("a-connection").then(ports => {
-      if (!ports || !ports.length) {
-        return ok(false, "No ports");
-      }
-      ok(true, "Got port");
-      _port = ports[0];
-      _port.onmessage = onmessage;
-      _port.onclose = onclose;
-      _port.postMessage('something');
-    }).catch(error => {
-      ok(false, "Unexpected " + error);
-    });
-  };
-})();
deleted file mode 100644
--- a/dom/apps/tests/iac/publisher/update.webapp
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "name": "IAC publisher app",
-  "launch_path": "/index.html",
-  "package_path": "publisher.zip",
-  "type": "certified"
-}
deleted file mode 100644
--- a/dom/apps/tests/iac/publisher/update.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/x-web-app-manifest+json
deleted file mode 100644
--- a/dom/apps/tests/iac/subscriber/index.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>Subscriber app for IAC API</title>
-    <script src="test.js"></script>
-  </head>
-  <body>
-  </body>
-</html>
deleted file mode 100644
--- a/dom/apps/tests/iac/subscriber/manifest.webapp
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "name": "IAC subscriber app",
-  "launch_path": "/index.html",
-  "type": "certified",
-  "connections": {
-    "a-connection": {
-      "description": "A connection",
-      "rules": {}
-    }
-  }
-}
deleted file mode 100644
--- a/dom/apps/tests/iac/subscriber/subscriber.list
+++ /dev/null
@@ -1,3 +0,0 @@
-manifest.webapp
-index.html
-test.js
deleted file mode 100644
index 024252362af2bbaa5c593b8683e20e446f56b3f3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/apps/tests/iac/subscriber/test.js
+++ /dev/null
@@ -1,9 +0,0 @@
-let port;
-navigator.mozSetMessageHandler('connection', request => {
-  port = request.port;
-  port.onmessage = () => {
-    port.postMessage('response');
-    port.close();
-  };
-});
-alert('READY');
deleted file mode 100644
--- a/dom/apps/tests/iac/subscriber/update.webapp
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "IAC subscriber app",
-  "launch_path": "/index.html",
-  "type": "certified",
-  "package_path": "subscriber.zip",
-  "connections": {
-    "a-connection": {
-      "description": "A connection",
-      "rules": {}
-    }
-  }
-}
deleted file mode 100644
--- a/dom/apps/tests/iac/subscriber/update.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/x-web-app-manifest+json
deleted file mode 100644
--- a/dom/apps/tests/test_iac.html
+++ /dev/null
@@ -1,236 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=915880
--->
-<head>
-  <title>Test for IAC API</title>
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript"
-          src="chrome://mochikit/content/chrome-harness.js"></script>
-  <script type="application/javascript"
-          src="http://test/chrome/dom/activities/tests/mochi/common.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={915880}">Mozilla Bug {915880}</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.7">
-
-SimpleTest.waitForExplicitFinish();
-
-const gURL = "http://test/chrome/dom/apps/tests/iac/";
-const IAC_UI_GLUE_CID =
-  Components.ID("{384afeee-f1d2-4819-9d2e-9b62f6b0e382}");
-
-function registerComponent(aObject, aDescription, aContract, aCid) {
-  info("Registering " + aCid);
-
-  var componentManager =
-    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  componentManager.registerFactory(aCid, aDescription, aContract, aObject);
-
-  // Keep the id on the object so we can unregister later.
-  aObject.cid = aCid;
-}
-
-function unregisterComponent(aObject) {
-  info("Unregistering " + aObject.cid);
-  var componentManager =
-    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  componentManager.unregisterFactory(aObject.cid, aObject);
-}
-
-let InterAppCommUIGlue = {
-  // nsISupports implementation.
-  QueryInterface: function(iid) {
-    if (iid.equals(Ci.nsISupports) ||
-        iid.equals(Ci.nsIFactory) ||
-        iid.equals(Ci.nsIInterAppCommUIGlue)) {
-      return this;
-    }
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-  // nsIFactory implementation.
-  createInstance: function(outer, iid) {
-    return this.QueryInterface(iid);
-  },
-
-  // nsIInterAppCommUIGlue implementation
-  selectApps(aCallerID, aPubAppManifestURL, aKeyword, aAppsToSelect) {
-    return Promise.resolve({
-      callerID: aCallerID,
-      keyword: aKeyword,
-      manifestURL: aPubAppManifestURL,
-      selectedApps: aAppsToSelect
-    });
-  }
-};
-
-registerComponent(InterAppCommUIGlue,
-                  "InterAppComm UI Glue",
-                  "@mozilla.org/dom/apps/inter-app-comm-ui-glue;1",
-                  IAC_UI_GLUE_CID);
-
-function finish() {
-  unregisterComponent(InterAppCommUIGlue);
-  SimpleTest.finish();
-}
-
-function cbError(aEvent) {
-  ok(false, "Error callback invoked " +
-            aEvent.target.error.name + " " + aEvent.target.error.message);
-  finish();
-}
-
-let subscriber = null;
-let publisher = null;
-
-function installApp(path) {
-  return new Promise((resolve, reject) => {
-    let request = navigator.mozApps.installPackage(gURL + path);
-    request.onerror = () => {
-      ok(false, request.error.name);
-      reject();
-    };
-    request.onsuccess = () => {
-      let app = request.result;
-      ok(app, "App is not null");
-      if (app.installState == "installed") {
-        return resolve(app);
-      }
-      app.ondownloadapplied = () => {
-        resolve(app);
-      };
-      app.ondownloaderror = () => {
-        ok(false, "Unexpected download error");
-        reject();
-      };
-    };
-  });
-}
-
-function launchApp(app) {
-  if (!app) {
-    ok(false, "No app to launch");
-    return Promise.reject();
-  }
-  return new Promise((resolve, reject) => {
-    let iframe = document.createElement("iframe");
-    iframe.setAttribute("mozbrowser", "true");
-    iframe.setAttribute("mozapp", app.manifestURL);
-    iframe.addEventListener("mozbrowsershowmodalprompt", e => {
-      let message = e.detail.message;
-      if (/OK/.exec(message)) {
-        ok(true, "Message from app: " + message);
-      } else if (/KO/.exec(message)) {
-        ok(false, "Message from app: " + message);
-      } else if (/READY/.exec(message)) {
-        ok(true, "Message from app: " + message);
-        resolve();
-      } else {
-        ok(false, "Unexpected message received: " + message);
-      }
-    }, false);
-    let domParent = document.getElementById("container");
-    domParent.appendChild(iframe);
-    SpecialPowers.wrap(iframe.contentWindow).location =
-      app.origin + app.manifest.launch_path;
-  });
-}
-
-const tests = [() => {
-  info("Test start");
-  SpecialPowers.autoConfirmAppInstall(() => {
-    SpecialPowers.autoConfirmAppUninstall(next);
-  });
-}, () => {
-  info("Installing subscriber app");
-  installApp("subscriber/update.webapp").then(app => {
-    subscriber = app;
-    next();
-  }).catch(() => {
-    ok(false, "Unable to install app");
-    finish();
-  });
-}, () => {
-  info("Launching " + subscriber.manifest.name);
-  launchApp(subscriber).then(next);
-}, () => {
-  info("Installing publisher app");
-  installApp("publisher/update.webapp").then(app => {
-    publisher = app;
-    next();
-  }).catch(() => {
-    ok(false, "Unable to install app");
-    finish();
-  });
-}, () => {
-  info("Launching " + publisher.manifest.name);
-  launchApp(publisher).then(next);
-}, () => {
-  navigator.mozApps.mgmt.onuninstall = event => {
-    let app = event.application;
-    next();
-  };
-  let request = navigator.mozApps.mgmt.uninstall(subscriber);
-  request.onerror = cbError;
-}, () => {
-  navigator.mozApps.mgmt.onuninstall = event => {
-    let app = event.application;
-    next();
-  };
-  let request = navigator.mozApps.mgmt.uninstall(publisher);
-  request.onerror = cbError;
-}];
-
-const next = () => {
-  let step = tests.shift();
-  if (!step) {
-    return finish();
-  }
-  try {
-    step();
-  } catch(e) {
-    ok(false, "Test threw: " + e);
-  }
-}
-
-SpecialPowers.pushPermissions([{
-  "type": "webapps-manage",
-  "allow": 1,
-  "context": document
-}, {
-  "type": "browser",
-  "allow": 1,
-  "context": document
-}, {
-  "type": "embed-apps",
-  "allow": 1,
-  "context": document
-}], () => {
-  // IAC is only allowed for certified apps. We use dev mode to
-  // skip the security checks.
-  SpecialPowers.pushPrefEnv({
-    "set": [
-      ["dom.mozApps.debug", true],
-      ["dom.apps.developer_mode", true],
-      ["dom.mozBrowserFramesEnabled", true],
-      ["dom.sysmsg.enabled", true]
-    ]
-  }, next);
-});
-
-</script>
-<div id="container"></div>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/apps/tests/unit/test_inter_app_comm_service.js
+++ /dev/null
@@ -1,1089 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-Cu.import("resource://gre/modules/InterAppCommService.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
-
-var UUIDGenerator = Cc["@mozilla.org/uuid-generator;1"]
-                      .getService(Ci.nsIUUIDGenerator);
-
-const MESSAGE_PORT_ID = UUIDGenerator.generateUUID().toString();
-const FAKE_MESSAGE_PORT_ID = UUIDGenerator.generateUUID().toString();
-const OUTER_WINDOW_ID = UUIDGenerator.generateUUID().toString();
-const TOP_WINDOW_ID = UUIDGenerator.generateUUID().toString();
-const REQUEST_ID = UUIDGenerator.generateUUID().toString();
-
-const PUB_APP_MANIFEST_URL = "app://pubapp.gaiamobile.org/manifest.webapp";
-const PUB_APP_MANIFEST_URL_WRONG =
-                  "app://pubappnotaccepted.gaiamobile.org/manifest.webapp";
-const SUB_APP_MANIFEST_URL = "app://subapp.gaiamobile.org/manifest.webapp";
-const SUB_APP_MANIFEST_URL_WRONG =
-                  "app://subappnotaccepted.gaiamobile.org/manifest.webapp";
-
-const PUB_APP_PAGE_URL = "app://pubapp.gaiamobile.org/handler.html";
-const PUB_APP_PAGE_URL_WRONG = "app://pubapp.gaiamobile.org/notAccepted.html";
-const SUB_APP_PAGE_URL = "app://subapp.gaiamobile.org/handler.html";
-const SUB_APP_PAGE_URL_WORNG = "app://subapp.gaiamobile.org/notAccepted.html";
-
-const PAGE_URL_REG_EXP = "^app://.*\\.gaiamobile\\.org/handler.html";
-
-const KEYWORD = "test";
-const CONNECT_KEYWORD = "connect-test";
-
-function create_message_port_pair(aMessagePortId,
-                                  aKeyword,
-                                  aPubManifestURL,
-                                  aSubManifestURL) {
-  InterAppCommService._messagePortPairs[aMessagePortId] = {
-    keyword: aKeyword,
-    publisher: {
-      manifestURL: aPubManifestURL
-    },
-    subscriber: {
-      manifestURL: aSubManifestURL
-    }
-  };
-}
-
-function clear_message_port_pairs() {
-  InterAppCommService._messagePortPairs = {};
-}
-
-function register_message_port(aMessagePortId,
-                               aManifestURL,
-                               aPageURL,
-                               aTargetSendAsyncMessage) {
-  let message = {
-    name: "InterAppMessagePort:Register",
-    json: {
-      messagePortID: aMessagePortId,
-      manifestURL: aManifestURL,
-      pageURL: aPageURL
-    },
-    target: {
-      sendAsyncMessage: function(aName, aData) {
-        if (aTargetSendAsyncMessage) {
-          aTargetSendAsyncMessage(aName, aData);
-        }
-      },
-      assertContainApp: function(_manifestURL) {
-        return (aManifestURL == _manifestURL);
-      }
-    }
-  };
-
-  InterAppCommService.receiveMessage(message);
-
-  return message.target;
-}
-
-function register_message_ports(aMessagePortId,
-                                aPubTargetSendAsyncMessage,
-                                aSubTargetSendAsyncMessage) {
-  let pubTarget = register_message_port(aMessagePortId,
-                                        PUB_APP_MANIFEST_URL,
-                                        PUB_APP_PAGE_URL,
-                                        aPubTargetSendAsyncMessage);
-
-  let subTarget = register_message_port(aMessagePortId,
-                                        SUB_APP_MANIFEST_URL,
-                                        SUB_APP_PAGE_URL,
-                                        aSubTargetSendAsyncMessage);
-
-  return { pubTarget: pubTarget, subTarget: subTarget };
-}
-
-function unregister_message_port(aMessagePortId,
-                                 aManifestURL) {
-  let message = {
-    name: "InterAppMessagePort:Unregister",
-    json: {
-      messagePortID: aMessagePortId,
-      manifestURL: aManifestURL
-    },
-    target: {
-      assertContainApp: function(_manifestURL) {
-        return (aManifestURL == _manifestURL);
-      }
-    }
-  };
-
-  InterAppCommService.receiveMessage(message);
-}
-
-function remove_target(aTarget) {
-  let message = {
-    name: "child-process-shutdown",
-    target: aTarget
-  };
-
-  InterAppCommService.receiveMessage(message);
-}
-
-function post_message(aMessagePortId,
-                      aManifestURL,
-                      aMessage) {
-  let message = {
-    name: "InterAppMessagePort:PostMessage",
-    json: {
-      messagePortID: aMessagePortId,
-      manifestURL: aManifestURL,
-      message: aMessage
-    },
-    target: {
-      assertContainApp: function(_manifestURL) {
-        return (aManifestURL == _manifestURL);
-      }
-    }
-  };
-
-  InterAppCommService.receiveMessage(message);
-}
-
-function create_allowed_connections(aKeyword,
-                                    aPubManifestURL,
-                                    aSubManifestURL) {
-  let allowedPubAppManifestURLs =
-    InterAppCommService._allowedConnections[aKeyword] = {};
-
-  allowedPubAppManifestURLs[aPubManifestURL] = [aSubManifestURL];
-}
-
-function clear_allowed_connections() {
-  InterAppCommService._allowedConnections = {};
-}
-
-function get_connections(aManifestURL,
-                         aOuterWindowID,
-                         aRequestID,
-                         aTargetSendAsyncMessage) {
-  let message = {
-    name: "Webapps:GetConnections",
-    json: {
-      manifestURL: aManifestURL,
-      outerWindowID: aOuterWindowID,
-      requestID: aRequestID
-    },
-    target: {
-      sendAsyncMessage: function(aName, aData) {
-        if (aTargetSendAsyncMessage) {
-          aTargetSendAsyncMessage(aName, aData);
-        }
-      },
-      assertContainApp: function(_manifestURL) {
-        return (aManifestURL == _manifestURL);
-      }
-    }
-  };
-
-  InterAppCommService.receiveMessage(message);
-}
-
-function cancel_connections(aManifestURL,
-                            aKeyword,
-                            aPubManifestURL,
-                            aSubManifestURL) {
-  let message = {
-    name: "InterAppConnection:Cancel",
-    json: {
-      manifestURL: aManifestURL,
-      keyword: aKeyword,
-      pubAppManifestURL: aPubManifestURL,
-      subAppManifestURL: aSubManifestURL
-    },
-    target: {
-      assertContainApp: function(_manifestURL) {
-        return (aManifestURL == _manifestURL);
-      }
-    }
-  };
-
-  InterAppCommService.receiveMessage(message);
-}
-
-add_test(function test_registerMessagePort() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  let targets = register_message_ports(MESSAGE_PORT_ID);
-
-  let messagePortPair = InterAppCommService._messagePortPairs[MESSAGE_PORT_ID];
-
-  do_check_eq(PUB_APP_PAGE_URL, messagePortPair.publisher.pageURL);
-  do_check_eq(SUB_APP_PAGE_URL, messagePortPair.subscriber.pageURL);
-
-  do_check_true(targets.pubTarget === messagePortPair.publisher.target);
-  do_check_true(targets.subTarget === messagePortPair.subscriber.target);
-
-  clear_message_port_pairs();
-  run_next_test();
-});
-
-add_test(function test_failToRegisterMessagePort() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  let targets = register_message_ports(FAKE_MESSAGE_PORT_ID);
-
-  let messagePortPair = InterAppCommService._messagePortPairs[MESSAGE_PORT_ID];
-
-  // Because it failed to register, the page URLs and targets don't exist.
-  do_check_true(messagePortPair.publisher.pageURL === undefined);
-  do_check_true(messagePortPair.subscriber.pageURL === undefined);
-
-  do_check_true(messagePortPair.publisher.target === undefined);
-  do_check_true(messagePortPair.subscriber.target === undefined);
-
-  clear_message_port_pairs();
-  run_next_test();
-});
-
-add_test(function test_unregisterMessagePort() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  register_message_ports(MESSAGE_PORT_ID);
-
-  unregister_message_port(MESSAGE_PORT_ID, PUB_APP_MANIFEST_URL);
-
-  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
-                === undefined);
-
-  clear_message_port_pairs();
-  run_next_test();
-});
-
-add_test(function test_failToUnregisterMessagePort() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  register_message_ports(MESSAGE_PORT_ID);
-
-  unregister_message_port(FAKE_MESSAGE_PORT_ID, PUB_APP_MANIFEST_URL);
-
-  // Because it failed to unregister, the entry still exists.
-  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
-                !== undefined);
-
-  clear_message_port_pairs();
-  run_next_test();
-});
-
-add_test(function test_removeTarget() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  let targets = register_message_ports(MESSAGE_PORT_ID);
-
-  remove_target(targets.pubTarget);
-
-  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
-                === undefined);
-
-  clear_message_port_pairs();
-  run_next_test();
-});
-
-add_test(function test_postMessage() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  let countPubAppOnMessage = 0;
-  function pubAppOnMessage(aName, aData) {
-    countPubAppOnMessage++;
-
-    do_check_eq(aName, "InterAppMessagePort:OnMessage");
-    do_check_eq(aData.manifestURL, PUB_APP_MANIFEST_URL);
-    do_check_eq(aData.pageURL, PUB_APP_PAGE_URL);
-    do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
-
-    if (countPubAppOnMessage == 1) {
-      do_check_eq(aData.message.text, "sub app says world");
-
-      post_message(MESSAGE_PORT_ID,
-                   PUB_APP_MANIFEST_URL,
-                   { text: "pub app says hello again" });
-
-    } else if (countPubAppOnMessage == 2) {
-      do_check_eq(aData.message.text, "sub app says world again");
-
-      clear_message_port_pairs();
-      run_next_test();
-    } else {
-      do_throw("pub app receives an unexpected message")
-    }
-  };
-
-  let countSubAppOnMessage = 0;
-  function subAppOnMessage(aName, aData) {
-    countSubAppOnMessage++;
-
-    do_check_eq(aName, "InterAppMessagePort:OnMessage");
-    do_check_eq(aData.manifestURL, SUB_APP_MANIFEST_URL);
-    do_check_eq(aData.pageURL, SUB_APP_PAGE_URL);
-    do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
-
-    if (countSubAppOnMessage == 1) {
-      do_check_eq(aData.message.text, "pub app says hello");
-
-      post_message(MESSAGE_PORT_ID,
-                   SUB_APP_MANIFEST_URL,
-                   { text: "sub app says world" });
-
-    } else if (countSubAppOnMessage == 2) {
-      do_check_eq(aData.message.text, "pub app says hello again");
-
-      post_message(MESSAGE_PORT_ID,
-                   SUB_APP_MANIFEST_URL,
-                   { text: "sub app says world again" });
-    } else {
-      do_throw("sub app receives an unexpected message");
-    }
-  };
-
-  register_message_ports(MESSAGE_PORT_ID, pubAppOnMessage, subAppOnMessage);
-
-  post_message(MESSAGE_PORT_ID,
-               PUB_APP_MANIFEST_URL,
-               { text: "pub app says hello" });
-});
-
-add_test(function test_registerMessagePort_with_queued_messages() {
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  register_message_port(MESSAGE_PORT_ID,
-                        PUB_APP_MANIFEST_URL,
-                        PUB_APP_PAGE_URL);
-
-  post_message(MESSAGE_PORT_ID,
-               PUB_APP_MANIFEST_URL,
-               { text: "pub app says hello" });
-
-  post_message(MESSAGE_PORT_ID,
-               PUB_APP_MANIFEST_URL,
-               { text: "pub app says hello again" });
-
-  let countSubAppOnMessage = 0;
-  function subAppOnMessage(aName, aData) {
-    countSubAppOnMessage++;
-
-    do_check_eq(aName, "InterAppMessagePort:OnMessage");
-    do_check_eq(aData.manifestURL, SUB_APP_MANIFEST_URL);
-    do_check_eq(aData.pageURL, SUB_APP_PAGE_URL);
-    do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
-
-    if (countSubAppOnMessage == 1) {
-      do_check_eq(aData.message.text, "pub app says hello");
-    } else if (countSubAppOnMessage == 2) {
-      do_check_eq(aData.message.text, "pub app says hello again");
-
-      clear_message_port_pairs();
-      run_next_test();
-    } else {
-      do_throw("sub app receives an unexpected message");
-    }
-  };
-
-  register_message_port(MESSAGE_PORT_ID,
-                        SUB_APP_MANIFEST_URL,
-                        SUB_APP_PAGE_URL,
-                        subAppOnMessage);
-});
-
-add_test(function test_getConnections() {
-  create_allowed_connections(KEYWORD,
-                             PUB_APP_MANIFEST_URL,
-                             SUB_APP_MANIFEST_URL);
-
-  function onGetConnections(aName, aData) {
-    do_check_eq(aName, "Webapps:GetConnections:Return:OK");
-    do_check_eq(aData.oid, OUTER_WINDOW_ID);
-    do_check_eq(aData.requestID, REQUEST_ID);
-
-    let connections = aData.connections;
-    do_check_eq(connections.length, 1);
-    do_check_eq(connections[0].keyword, KEYWORD);
-    do_check_eq(connections[0].pubAppManifestURL, PUB_APP_MANIFEST_URL);
-    do_check_eq(connections[0].subAppManifestURL, SUB_APP_MANIFEST_URL);
-
-    clear_allowed_connections();
-    run_next_test();
-  };
-
-  get_connections(PUB_APP_MANIFEST_URL,
-                  OUTER_WINDOW_ID,
-                  REQUEST_ID,
-                  onGetConnections);
-});
-
-add_test(function test_cancelConnection() {
-  create_allowed_connections(KEYWORD,
-                             PUB_APP_MANIFEST_URL,
-                             SUB_APP_MANIFEST_URL);
-
-  create_message_port_pair(MESSAGE_PORT_ID,
-                           KEYWORD,
-                           PUB_APP_MANIFEST_URL,
-                           SUB_APP_MANIFEST_URL);
-
-  register_message_ports(MESSAGE_PORT_ID);
-
-  cancel_connections(PUB_APP_MANIFEST_URL,
-                     KEYWORD,
-                     PUB_APP_MANIFEST_URL,
-                     SUB_APP_MANIFEST_URL);
-
-  do_check_true(InterAppCommService._allowedConnections[KEYWORD]
-                === undefined);
-
-  do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
-                === undefined);
-
-  clear_allowed_connections();
-  clear_message_port_pairs();
-  run_next_test();
-});
-
-
-function registerConnection(aKeyword, aSubAppPageURL, aSubAppManifestURL,
-                            aDescription, aRules) {
-  var subAppPageUrl = Services.io.newURI(aSubAppPageURL, null, null);
-  var subAppManifestUrl = Services.io.newURI(aSubAppManifestURL, null, null);
-  InterAppCommService.registerConnection(aKeyword, subAppPageUrl,
-                                         subAppManifestUrl, aDescription,
-                                         aRules);
-}
-
-add_test(function test_registerConnection() {
-  InterAppCommService._registeredConnections = {};
-  var description = "A test connection";
-
-  // Rules can have (ATM):
-  //  * minimumAccessLevel
-  //  * manifestURLs
-  //  * pageURLs
-  //  * installOrigins
-  var sampleRules = {
-    minimumAccessLevel: "certified",
-    pageURLs: ["http://a.server.com/a/page.html"]
-  };
-
-  registerConnection(CONNECT_KEYWORD, SUB_APP_PAGE_URL, SUB_APP_MANIFEST_URL,
-                     description, sampleRules);
-
-  var regConn = InterAppCommService._registeredConnections[CONNECT_KEYWORD];
-  do_check_true(regConn !== undefined);
-  var regEntry = regConn[SUB_APP_MANIFEST_URL];
-  do_check_true(regEntry !== undefined);
-  do_check_eq(regEntry.pageURL, SUB_APP_PAGE_URL);
-  do_check_eq(regEntry.description, description);
-  do_check_eq(regEntry.rules, sampleRules);
-  do_check_eq(regEntry.manifestURL, SUB_APP_MANIFEST_URL);
-
-  InterAppCommService._registeredConnections = {};
-
-  run_next_test();
-});
-
-
-// Simulates mozApps connect
-function connect(publisher, aTargetSendAsyncMessage) {
-  let message = {
-    name: "Webapps:Connect",
-    json: {
-      keyword: publisher.connectKw,
-      rules: publisher.rules,
-      manifestURL: publisher.manifestURL,
-      pubPageURL: publisher.pageURL,
-      outerWindowID: OUTER_WINDOW_ID,
-      topWindowID: TOP_WINDOW_ID,
-      requestID: REQUEST_ID
-    },
-    target: {
-      sendAsyncMessage: function(aName, aData) {
-        if (aTargetSendAsyncMessage) {
-          aTargetSendAsyncMessage(aName, aData);
-        }
-      },
-      assertContainApp: function(_manifestURL) {
-        return (publisher.manifestURL == _manifestURL);
-      }
-    }
-  };
-  InterAppCommService.receiveMessage(message);
-};
-
-function registerComponent(aObject, aDescription, aContract) {
-  var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
-                        .getService(Ci.nsIUUIDGenerator);
-  var cid = uuidGenerator.generateUUID();
-
-  var componentManager =
-    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  componentManager.registerFactory(cid, aDescription, aContract, aObject);
-
-   // Keep the id on the object so we can unregister later.
-   aObject.cid = cid;
-}
-
-function unregisterComponent(aObject) {
-  var componentManager =
-    Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  componentManager.unregisterFactory(aObject.cid, aObject);
-}
-
-// The fun thing about this mock is that it actually does the same than the
-// current actual implementation does.
-var mockUIGlue =  {
-   // nsISupports implementation.
-   QueryInterface: function(iid) {
-     if (iid.equals(Ci.nsISupports) ||
-         iid.equals(Ci.nsIFactory) ||
-         iid.equals(Ci.nsIInterAppCommUIGlue)) {
-       return this;
-     }
-
-     throw Cr.NS_ERROR_NO_INTERFACE;
-   },
-
-   // nsIFactory implementation.
-   createInstance: function(outer, iid) {
-    return this.QueryInterface(iid);
-   },
-
-   // nsIActivityUIGlue implementation.
-   selectApps: function(aCallerID, aPubAppManifestURL, aKeyword,
-                        aAppsToSelect) {
-     return Promise.resolve({
-       callerID: aCallerID,
-       keyword: aKeyword,
-       manifestURL: aPubAppManifestURL,
-       selectedApps: aAppsToSelect
-     });
-   }
- };
-
-// Used to keep a fake table of installed apps (needed by mockAppsService)
-var webappsTable = {
-};
-
-var mockAppsService = {
-  // We use the status and the installOrigin here only.
-  getAppByManifestURL: function (aPubAppManifestURL) {
-    return webappsTable[aPubAppManifestURL];
-  },
-
-  // And so far so well this should not be used by tests at all.
-  getManifestURLByLocalId: function(appId) {
-  }
-};
-
-
-// Template test for connect
-function simpleConnectTestTemplate(aTestCase) {
-  dump("TEST: " + aTestCase.subscriber.description + "\n");
-
-  // First, add some mocking....
-
-  // System Messenger
-  var resolveMessenger, rejectMessenger;
-  var messengerPromise = new Promise((resolve, reject) => {
-    resolveMessenger = resolve;
-    rejectMessenger = reject;
-  });
-  var mockMessenger = {
-    sendMessage: function(aName, aData, aDestURL, aDestManURL) {
-      do_check_eq(aName, "connection");
-      resolveMessenger(aData);
-    }
-  };
-  InterAppCommService.messenger = mockMessenger;
-
-  // AppsService
-  InterAppCommService.appsService = mockAppsService;
-
-  // Set the initial state:
-  // First, setup our fake webappsTable:
-  var subs = aTestCase.subscriber;
-  var pub = aTestCase.publisher;
-  webappsTable[subs.manifestURL] = subs.webappEntry;
-  webappsTable[pub.manifestURL] = pub.webappEntry;
-
-  InterAppCommService._registeredConnections = {};
-  clear_allowed_connections();
-  clear_message_port_pairs();
-
-  registerConnection(subs.connectKw, subs.pageURL, subs.manifestURL,
-                     subs.description, subs.rules);
-
-  // And now we can try connecting...
-  connect(pub, function(aName, aData) {
-
-    var expectedName = "Webapps:Connect:Return:OK";
-    var expectedLength = 1;
-    if (!aTestCase.expectedSuccess) {
-      expectedName = "Webapps:Connect:Return:KO";
-      expectedLength = 0;
-    }
-
-    do_check_eq(aName, expectedName);
-    var numPortIDs =
-      (aData.messagePortIDs && aData.messagePortIDs.length ) || 0;
-    do_check_eq(numPortIDs, expectedLength);
-    if (expectedLength) {
-      var portPair =
-        InterAppCommService._messagePortPairs[aData.messagePortIDs[0]];
-      do_check_eq(portPair.publisher.manifestURL,pub.manifestURL);
-      do_check_eq(portPair.subscriber.manifestURL, subs.manifestURL);
-    } else {
-      run_next_test();
-      return;
-    }
-
-    // We need to wait for the message to be "received" on the publisher also
-    messengerPromise.then(messageData => {
-      do_check_eq(messageData.keyword, subs.connectKw);
-      do_check_eq(messageData.pubPageURL, pub.pageURL);
-      do_check_eq(messageData.messagePortID, aData.messagePortIDs[0]);
-      // Cleanup
-      InterAppCommService.registeredConnections = {};
-      clear_allowed_connections();
-      clear_message_port_pairs();
-      run_next_test();
-    });
-
-  });
-}
-
-const CERTIFIED = Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
-const PRIVILEGED = Ci.nsIPrincipal.APP_STATUS_PRIVILEGED;
-const INSTALLED = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
-const NOT_INSTALLED = Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED;
-
-var connectTestCases = [
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "Trivial case [empty rules]. Successful test",
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "not certified SUB status and not PUB rules",
-      rules: {},
-      webappEntry: {
-        appStatus: PRIVILEGED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "not certified PUB status and not SUB rules",
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: INSTALLED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchMinimumAccessLvl --> Sub INSTALLED PubRul web",
-      rules: {},
-      webappEntry: {
-        appStatus: INSTALLED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {
-        minimumAccessLevel:"web"
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchMinimumAccessLvl --> Sub NOT INSTALLED PubRul web",
-      rules: {},
-      webappEntry: {
-        appStatus: NOT_INSTALLED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {
-        minimumAccessLevel:"web"
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchMinimumAccessLvl --> Pub CERTIFIED SubRul certified",
-      rules: {
-        minimumAccessLevel:"certified"
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchMinimumAccessLvl --> Pub PRIVILEGED SubRul certified",
-      rules: {
-        minimumAccessLevel:"certified"
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: PRIVILEGED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchManifest --> Pub manifest1 SubRules:{ manifest1 }",
-      rules: {
-        manifestURLs: [PUB_APP_MANIFEST_URL]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchManifest --> Pub manifest2 SubRules:{ manifest1 }",
-      rules: {
-        manifestURLs: [PUB_APP_MANIFEST_URL]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL_WRONG,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchManifest --> Sub manifest1 PubRules:{ manifest1 }",
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {
-        manifestURLs: [SUB_APP_MANIFEST_URL]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL_WRONG,
-      description: "matchManifest --> Sub manifest2 PubRules:{ manifest1 }",
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {
-        manifestURLs: [SUB_APP_MANIFEST_URL]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL_WRONG,
-      description: "matchPage --> Pub page1 SubRules:{ page1 }",
-      rules: {
-        pageURLs: [PAGE_URL_REG_EXP]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchPage --> Pub page2 SubRules:{ page1 }",
-      rules: {
-        pageURLs: [PAGE_URL_REG_EXP]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL_WRONG,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchPage --> Sub page1 PubRules:{ page1 }",
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {
-        pageURLs: [PAGE_URL_REG_EXP]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: true
-  },
-  {
-    subscriber: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: SUB_APP_PAGE_URL_WORNG,
-      manifestURL: SUB_APP_MANIFEST_URL,
-      description: "matchPage --> Sub page2 PubRules:{ page1 }",
-      rules: {},
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    publisher: {
-      connectKw: CONNECT_KEYWORD,
-      pageURL: PUB_APP_PAGE_URL,
-      manifestURL: PUB_APP_MANIFEST_URL,
-      rules: {
-        pageURLs: [PAGE_URL_REG_EXP]
-      },
-      webappEntry: {
-        appStatus: CERTIFIED,
-        installOrigin: "app://system.gaiamobile.org"
-      }
-    },
-    expectedSuccess: false
-  }
-];
-
-// Only run these test cases if we're on a nightly build. Otherwise they
-// don't make much sense.
-if (AppConstants.NIGHTLY_BUILD) {
-  registerComponent(mockUIGlue,
-                    "Mock InterApp UI Glue",
-                    "@mozilla.org/dom/apps/inter-app-comm-ui-glue;1");
-
-  do_register_cleanup(function () {
-    // Cleanup the mocks
-    InterAppCommService.messenger = undefined;
-    InterAppCommService.appsService = undefined;
-    unregisterComponent(mockUIGlue);
-  });
-  connectTestCases.forEach(
-    aTestCase =>
-      add_test(simpleConnectTestTemplate.bind(undefined, aTestCase))
-  );
-}
-
-function run_test() {
-  do_get_profile();
-
-  run_next_test();
-}
--- a/dom/apps/tests/unit/xpcshell.ini
+++ b/dom/apps/tests/unit/xpcshell.ini
@@ -1,9 +1,8 @@
 [DEFAULT]
 head = head.js
 tail = tail.js
 
 [test_has_widget_criterion.js]
-[test_inter_app_comm_service.js]
 [test_manifestSanitizer.js]
 [test_manifestHelper.js]
 [test_moziapplication.js]
--- a/dom/archivereader/ArchiveEvent.cpp
+++ b/dom/archivereader/ArchiveEvent.cpp
@@ -81,18 +81,17 @@ ArchiveReaderEvent::Run()
   return Exec();
 }
 
 nsresult
 ArchiveReaderEvent::RunShare(nsresult aStatus)
 {
   mStatus = aStatus;
 
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &ArchiveReaderEvent::ShareMainThread);
-  NS_DispatchToMainThread(event);
+  NS_DispatchToMainThread(NewRunnableMethod(this, &ArchiveReaderEvent::ShareMainThread));
 
   return NS_OK;
 }
 
 void
 ArchiveReaderEvent::ShareMainThread()
 {
   nsTArray<RefPtr<File>> fileList;
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -196,49 +196,47 @@ AudioChannelAgent::InitInternal(nsPIDOMW
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, InitInternal, this = %p, type = %d, "
           "owner = %p, hasCallback = %d\n", this, mAudioChannelType,
           mWindow.get(), (!!mCallback || !!mWeakCallback)));
 
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStartedPlaying(float *aVolume,
-                                                      bool* aMuted)
+NS_IMETHODIMP
+AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig)
 {
-  MOZ_ASSERT(aVolume);
-  MOZ_ASSERT(aMuted);
-
-  // Window-less AudioChannelAgents are muted by default.
-  if (!mWindow) {
-    *aVolume = 0;
-    *aMuted = true;
-    return NS_OK;
+  if (NS_WARN_IF(!aConfig)) {
+    return NS_ERROR_FAILURE;
   }
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   service->RegisterAudioChannelAgent(this,
     static_cast<AudioChannel>(mAudioChannelType));
 
-  service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
+  AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
+                                                       mAudioChannelType);
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioChannelAgent, NotifyStartedPlaying, this = %p, mute = %d, "
-          "volume = %f\n", this, *aMuted, *aVolume));
+         ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
+          "mute = %d, volume = %f, suspend = %d\n", this,
+          config.mMuted, config.mVolume, config.mSuspend));
 
+  aConfig->SetConfig(config.mVolume, config.mMuted, config.mSuspend);
   mIsRegToService = true;
   return NS_OK;
 }
 
-NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying()
+NS_IMETHODIMP
+AudioChannelAgent::NotifyStoppedPlaying()
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       !mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
@@ -265,29 +263,59 @@ AudioChannelAgent::GetCallback()
 void
 AudioChannelAgent::WindowVolumeChanged()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
   if (!callback) {
     return;
   }
 
-  float volume = 1.0;
-  bool muted = false;
+  AudioPlaybackConfig config = GetMediaConfig();
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
+          "volume = %f\n", this, config.mMuted, config.mVolume));
+
+  callback->WindowVolumeChanged(config.mVolume, config.mMuted);
+}
 
-  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
-  if (service) {
-    service->GetState(mWindow, mAudioChannelType, &volume, &muted);
+void
+AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend)
+{
+  nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
+  if (!callback) {
+    return;
+  }
+
+  if (!IsDisposableSuspend(aSuspend)) {
+    aSuspend = GetMediaConfig().mSuspend;
   }
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
-          "volume = %f\n", this, muted, volume));
+         ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
+          "suspended = %d\n", this, aSuspend));
+
+  callback->WindowSuspendChanged(aSuspend);
+}
 
-  callback->WindowVolumeChanged(volume, muted);
+AudioPlaybackConfig
+AudioChannelAgent::GetMediaConfig()
+{
+  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
+  if (service) {
+    config = service->GetMediaConfig(mWindow, mAudioChannelType);
+  }
+  return config;
+}
+
+bool
+AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const
+{
+  return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+          aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
 }
 
 uint64_t
 AudioChannelAgent::WindowID() const
 {
   return mWindow ? mWindow->WindowID() : 0;
 }
 
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -18,41 +18,47 @@
       {0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}}
 
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 
 namespace mozilla {
 namespace dom {
 
+class AudioPlaybackConfig;
+
 /* Header file */
 class AudioChannelAgent : public nsIAudioChannelAgent
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIAUDIOCHANNELAGENT
 
   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
   AudioChannelAgent();
 
   void WindowVolumeChanged();
+  void WindowSuspendChanged(nsSuspendedTypes aSuspend);
   void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture);
 
   nsPIDOMWindowOuter* Window() const
   {
     return mWindow;
   }
 
   uint64_t WindowID() const;
   uint64_t InnerWindowID() const;
 
 private:
   virtual ~AudioChannelAgent();
 
+  AudioPlaybackConfig GetMediaConfig();
+  bool IsDisposableSuspend(nsSuspendedTypes aSuspend) const;
+
   // Returns mCallback if that's non-null, or otherwise tries to get an
   // nsIAudioChannelAgentCallback out of mWeakCallback.
   already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
 
   nsresult InitInternal(nsPIDOMWindowInner* aWindow, int32_t aAudioAgentType,
                         nsIAudioChannelAgentCallback* aCallback,
                         bool aUseWeakRef);
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -348,59 +348,62 @@ AudioChannelService::RegisterTabParent(T
 
 void
 AudioChannelService::UnregisterTabParent(TabParent* aTabParent)
 {
   MOZ_ASSERT(aTabParent);
   mTabParents.RemoveElement(aTabParent);
 }
 
-void
-AudioChannelService::GetState(nsPIDOMWindowOuter* aWindow, uint32_t aAudioChannel,
-                              float* aVolume, bool* aMuted)
+AudioPlaybackConfig
+AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow,
+                                    uint32_t aAudioChannel) const
 {
   MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
-  MOZ_ASSERT(aVolume && aMuted);
   MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
 
+  AudioPlaybackConfig config(1.0, false,
+                             nsISuspendedTypes::NONE_SUSPENDED);
 
   if (!aWindow || !aWindow->IsOuterWindow()) {
-    *aVolume = 0.0;
-    *aMuted = true;
-    return;
+    config.SetConfig(0.0, true,
+                     nsISuspendedTypes::SUSPENDED_BLOCK);
+    return config;
   }
 
-  *aVolume = 1.0;
-  *aMuted = false;
-
   AudioChannelWindow* winData = nullptr;
   nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
 
   // The volume must be calculated based on the window hierarchy. Here we go up
   // to the top window and we calculate the volume and the muted flag.
   do {
     winData = GetWindowData(window->WindowID());
     if (winData) {
-      *aVolume *= winData->mChannels[aAudioChannel].mVolume;
-      *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted;
+      config.mVolume *= winData->mChannels[aAudioChannel].mVolume;
+      config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted;
     }
 
-    *aVolume *= window->GetAudioVolume();
-    // TODO : distiguish between suspend and mute, it would be done in bug1242874.
-    *aMuted = *aMuted || window->GetMediaSuspended() || window->GetAudioMuted();
+    config.mVolume *= window->GetAudioVolume();
+    config.mMuted = config.mMuted || window->GetAudioMuted();
+
+    // If the mSuspend is already suspended, we don't need to set it again.
+    config.mSuspend = (config.mSuspend == nsISuspendedTypes::NONE_SUSPENDED) ?
+      window->GetMediaSuspend() : config.mSuspend;
 
     nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
     if (!win) {
       break;
     }
 
     window = do_QueryInterface(win);
 
     // If there is no parent, or we are the toplevel we don't continue.
   } while (window && window != aWindow);
+
+  return config;
 }
 
 bool
 AudioChannelService::TelephonyChannelIsActive()
 {
   nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator windowsIter(mWindows);
   while (windowsIter.HasMore()) {
     AudioChannelWindow* next = windowsIter.GetNext();
@@ -595,17 +598,18 @@ AudioChannelService::RefreshAgentsVolume
                                                    winData->mChannels[(uint32_t)aAudioChannel].mVolume,
                                                    winData->mChannels[(uint32_t)aAudioChannel].mMuted);
   }
 
   RefreshAgentsVolume(aWindow);
 }
 
 void
-AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
+AudioChannelService::RefreshAgents(nsPIDOMWindowOuter* aWindow,
+                                   mozilla::function<void(AudioChannelAgent*)> aFunc)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
   if (!topWindow) {
     return;
   }
@@ -613,21 +617,38 @@ AudioChannelService::RefreshAgentsVolume
   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
   if (!winData) {
     return;
   }
 
   nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     iter(winData->mAgents);
   while (iter.HasMore()) {
-    iter.GetNext()->WindowVolumeChanged();
+    aFunc(iter.GetNext());
   }
 }
 
 void
+AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
+{
+  RefreshAgents(aWindow, [] (AudioChannelAgent* agent) {
+    agent->WindowVolumeChanged();
+  });
+}
+
+void
+AudioChannelService::RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
+                                          nsSuspendedTypes aSuspend)
+{
+  RefreshAgents(aWindow, [aSuspend] (AudioChannelAgent* agent) {
+    agent->WindowSuspendChanged(aSuspend);
+  });
+}
+
+void
 AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow,
                                             uint64_t aInnerWindowID,
                                             bool aCapture)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -11,32 +11,60 @@
 #include "nsAutoPtr.h"
 #include "nsIObserver.h"
 #include "nsTObserverArray.h"
 #include "nsTArray.h"
 
 #include "AudioChannelAgent.h"
 #include "nsAttrValue.h"
 #include "mozilla/dom/AudioChannelBinding.h"
+#include "mozilla/Function.h"
 
 class nsIRunnable;
 class nsPIDOMWindowOuter;
 struct PRLogModuleInfo;
 
 namespace mozilla {
 namespace dom {
 
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
 
 class TabParent;
 
 #define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_
 
+class AudioPlaybackConfig
+{
+public:
+  AudioPlaybackConfig()
+    : mVolume(1.0)
+    , mMuted(false)
+    , mSuspend(nsISuspendedTypes::NONE_SUSPENDED)
+  {}
+
+  AudioPlaybackConfig(float aVolume, bool aMuted, uint32_t aSuspended)
+    : mVolume(aVolume)
+    , mMuted(aMuted)
+    , mSuspend(aSuspended)
+  {}
+
+  void SetConfig(float aVolume, bool aMuted, uint32_t aSuspended)
+  {
+    mVolume = aVolume;
+    mMuted = aMuted;
+    mSuspend = aSuspended;
+  }
+
+  float mVolume;
+  bool mMuted;
+  uint32_t mSuspend;
+};
+
 class AudioChannelService final : public nsIAudioChannelService
                                 , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIAUDIOCHANNELSERVICE
 
@@ -67,20 +95,20 @@ public:
   /**
    * For nested iframes.
    */
   void RegisterTabParent(TabParent* aTabParent);
   void UnregisterTabParent(TabParent* aTabParent);
 
   /**
    * Return the state to indicate this audioChannel for his window should keep
-   * playing/muted.
+   * playing/muted/suspended.
    */
-  void GetState(nsPIDOMWindowOuter* aWindow, uint32_t aChannel,
-                float* aVolume, bool* aMuted);
+  AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow,
+                                     uint32_t aAudioChannel) const;
 
   /* Methods for the BrowserElementAudioChannel */
   float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
 
   void SetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel,
                              float aVolume);
 
   bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
@@ -109,16 +137,18 @@ public:
    * AudioChannel enum.
    */
   virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
                                               bool aVisible);
 
   bool AnyAudioChannelIsActive();
 
   void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow);
+  void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
+                            nsSuspendedTypes aSuspend);
 
   void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
                                        nsPIDOMWindowOuter* aWindow);
 
   // This method needs to know the inner window that wants to capture audio. We
   // group agents per top outer window, but we can have multiple innerWindow per
   // top outerWindow (subiframes, etc.) and we have to identify all the agents
   // just for a particular innerWindow.
@@ -150,42 +180,43 @@ public:
 
   void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
                            bool aContentOrNormalChannel, bool aAnyChannel);
 
 private:
   AudioChannelService();
   ~AudioChannelService();
 
+  void RefreshAgents(nsPIDOMWindowOuter* aWindow,
+                     mozilla::function<void(AudioChannelAgent*)> aFunc);
+
   static void CreateServiceIfNeeded();
 
   /**
    * Shutdown the singleton.
    */
   static void Shutdown();
 
   void MaybeSendStatusUpdate();
 
   bool ContentOrNormalChannelIsActive();
 
   /* Send the default-volume-channel-changed notification */
   void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
                                               bool aVisible, uint64_t aChildID);
 
-  struct AudioChannelConfig final
+  class AudioChannelConfig final : public AudioPlaybackConfig
   {
+  public:
     AudioChannelConfig()
-      : mVolume(1.0)
-      , mMuted(IsAudioChannelMutedByDefault())
+      : AudioPlaybackConfig(1.0, IsAudioChannelMutedByDefault(),
+                            nsISuspendedTypes::NONE_SUSPENDED)
       , mNumberOfAgents(0)
     {}
 
-    float mVolume;
-    bool mMuted;
-
     uint32_t mNumberOfAgents;
   };
 
   struct AudioChannelWindow final
   {
     explicit AudioChannelWindow(uint64_t aWindowID)
       : mWindowID(aWindowID),
         mIsAudioCaptured(false)
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -1,24 +1,88 @@
 /* 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"
 
 interface mozIDOMWindow;
 
+typedef uint32_t nsSuspendedTypes;
+
+[scriptable, builtinclass, uuid(2822a840-f009-11e5-a837-0800200c9a66)]
+interface nsISuspendedTypes : nsISupports
+{
+  /**
+   * The suspended enum is used in three different situations,
+   * - platform audio focus (Fennec/B2G)
+   * - remote media control (Fennec)
+   * - block auto-play video in non-active page
+   *
+   * Note: the "remote side" must control the AudioChannelAgent using
+   * nsIAudioChannelAgentCallback.windowSuspendChanged() callback instead using
+   * play/pause methods or any button in the webpage.
+   *
+   * - SUSPENDED_PAUSE :
+   * It's used when transiently losing audio focus, the media can't be resumed
+   * until we gain the audio focus again. It would change the internal state of
+   * MediaElement when it's being suspended/resumed, and it would trigger the
+   * related JS event. eg. "play" and "pause" event.
+   *
+   * - SUSPENDED_BLOCK
+   * It's used to prevent auto-playing media in inactive page in order to
+   * reduce the power consumption, and the media can't be resumed until the
+   * page becomes active again. It would change the internal state of
+   * MediaElement when it's being blocked/resumed, so it won't trigger the
+   * related JS event. eg. "play" and "pause" event.
+   *
+   * - SUSPENDED_PAUSE_DISPOSABLE
+   * It's used for remote media-control to pause the playing media and when we
+   * lose audio focus permanently. It's disposable suspended, so the media can
+   * be resumed arbitrary after that. Same as SUSPENDED_PAUSE, it would change
+   * the internal state of MediaElement when it's being suspended.
+   *
+   * - SUSPENDED_STOP_DISPOSABLE
+   * It's used for remote media-control to stop the playing media. The remote
+   * control would disappear after stopping the media, so we would disconnect
+   * the audio channel agent. It's disposable suspended, so the media can be
+   * resumed arbitrary after that. Same as SUSPENDED_PAUSE, it would change
+   * the internal state of MediaElement when it's being suspended.
+   */
+
+  const uint32_t NONE_SUSPENDED             = 0;
+  const uint32_t SUSPENDED_PAUSE            = 1;
+  const uint32_t SUSPENDED_BLOCK            = 2;
+  const uint32_t SUSPENDED_PAUSE_DISPOSABLE = 3;
+  const uint32_t SUSPENDED_STOP_DISPOSABLE  = 4;
+};
+
+%{C++
+namespace mozilla {
+namespace dom {
+// It's defined in dom/audiochannel/AudioChannelService.h.
+class AudioPlaybackConfig;
+}
+}
+%}
+[ptr] native AudioPlaybackConfig(mozilla::dom::AudioPlaybackConfig);
+
 [uuid(15c05894-408e-4798-b527-a8c32d9c5f8c)]
 interface nsIAudioChannelAgentCallback : nsISupports
 {
   /**
    * Notified when the window volume/mute is changed
    */
   void windowVolumeChanged(in float aVolume, in bool aMuted);
 
+   /**
+   * Notified when the window needs to be suspended or resumed.
+   */
+  void windowSuspendChanged(in uint32_t aSuspend);
+
   /**
    * Notified when the capture state is changed.
    */
   void windowAudioCaptureChanged(in bool aCapture);
 };
 
 /**
  * This interface provides an agent for gecko components to participate
@@ -93,25 +157,20 @@ interface nsIAudioChannelAgent : nsISupp
   void initWithWeakCallback(in mozIDOMWindow window, in long channelType,
                             in nsIAudioChannelAgentCallback callback);
 
   /**
    * Notify the agent that we want to start playing.
    * Note: Gecko component SHOULD call this function first then start to
    *          play audio stream only when return value is true.
    *
-   * @return
-   *    normal state: the agent has registered with audio channel service and
-   *          the component should start playback.
-   *    muted state: the agent has registered with audio channel service but
-   *          the component should not start playback.
-   *    faded state: the agent has registered with audio channel service the
-   *          component should start playback as well as reducing the volume.
+   * @param config
+   *    It contains the playback related states (volume/mute/suspend)
    */
-  void notifyStartedPlaying(out float volume, out bool muted);
+  void notifyStartedPlaying(in AudioPlaybackConfig config);
 
   /**
    * Notify the agent we no longer want to play.
    *
    * Note : even if notifyStartedPlaying() returned false, the agent would
    * still be registered with the audio channel service and receive callbacks
    * for status changes. So notifyStoppedPlaying must still eventually be
    * called to unregister the agent with the channel service.
new file mode 100644
--- /dev/null
+++ b/dom/base/DOMTokenListSupportedTokens.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+/*
+ * Definitions of supported tokens data types for nsDOMTokenList.  This is in a
+ * separate header so Element.h can include it too.
+ */
+
+#ifndef mozilla_dom_DOMTokenListSupportedTokens_h
+#define mozilla_dom_DOMTokenListSupportedTokens_h
+
+namespace mozilla {
+namespace dom {
+
+// A single supported token.
+typedef const char* const DOMTokenListSupportedToken;
+
+// An array of supported tokens.  This should end with a null
+// DOMTokenListSupportedToken to indicate array termination.  A null value for
+// the DOMTokenListSupportedTokenArray means there is no definition of supported
+// tokens for the given DOMTokenList.  This should generally be a static table,
+// or at least outlive the DOMTokenList whose constructor it's passed to.
+typedef DOMTokenListSupportedToken* DOMTokenListSupportedTokenArray;
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DOMTokenListSupportedTokens_h
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -512,17 +512,17 @@ Element::WrapObject(JSContext *aCx, JS::
     xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding), &dummy);
 
     if (binding) {
       if (nsContentUtils::IsSafeToRunScript()) {
         binding->ExecuteAttachedHandler();
       }
       else {
         nsContentUtils::AddScriptRunner(
-          NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler));
+          NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler));
       }
     }
   }
 
   return obj;
 }
 
 /* virtual */
@@ -3089,17 +3089,18 @@ static nsIAtom** sPropertiesToTraverseAn
 // static
 nsIAtom***
 Element::HTMLSVGPropertiesToTraverseAndUnlink()
 {
   return sPropertiesToTraverseAndUnlink;
 }
 
 nsDOMTokenList*
-Element::GetTokenList(nsIAtom* aAtom)
+Element::GetTokenList(nsIAtom* aAtom,
+                      const DOMTokenListSupportedTokenArray aSupportedTokens)
 {
 #ifdef DEBUG
   nsIAtom*** props =
     HTMLSVGPropertiesToTraverseAndUnlink();
   bool found = false;
   for (uint32_t i = 0; props[i]; ++i) {
     if (*props[i] == aAtom) {
       found = true;
@@ -3109,17 +3110,17 @@ Element::GetTokenList(nsIAtom* aAtom)
   MOZ_ASSERT(found, "Trying to use an unknown tokenlist!");
 #endif
 
   nsDOMTokenList* list = nullptr;
   if (HasProperties()) {
     list = static_cast<nsDOMTokenList*>(GetProperty(aAtom));
   }
   if (!list) {
-    list = new nsDOMTokenList(this, aAtom);
+    list = new nsDOMTokenList(this, aAtom, aSupportedTokens);
     NS_ADDREF(list);
     SetProperty(aAtom, list, nsDOMTokenListPropertyDestructor);
   }
   return list;
 }
 
 void
 Element::GetTokenList(nsIAtom* aAtom, nsIVariant** aResult)
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -29,16 +29,17 @@
 #include "mozilla/Attributes.h"
 #include "nsIScrollableFrame.h"
 #include "mozilla/dom/Attr.h"
 #include "nsISMILAttr.h"
 #include "mozilla/dom/DOMRect.h"
 #include "nsAttrValue.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/DOMTokenListSupportedTokens.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "Units.h"
 
 class nsIFrame;
 class nsIDOMMozNamedAttrMap;
 class nsIURI;
@@ -1328,17 +1329,18 @@ protected:
    *
    * Note: for HTML this gets the value of the 'target' attribute; for XLink
    * this gets the value of the xlink:_moz_target attribute, or failing that,
    * the value of xlink:show, converted to a suitably equivalent named target
    * (e.g. _blank).
    */
   virtual void GetLinkTarget(nsAString& aTarget);
 
-  nsDOMTokenList* GetTokenList(nsIAtom* aAtom);
+  nsDOMTokenList* GetTokenList(nsIAtom* aAtom,
+                               const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr);
   void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult);
   nsresult SetTokenList(nsIAtom* aAtom, nsIVariant* aValue);
 
 private:
   /**
    * Get this element's client area rect in app units.
    * @return the frame's client area
    */
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -359,21 +359,17 @@ EventSource::OnStartRequest(nsIRequest *
   rv = httpChannel->GetContentType(contentType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
     DispatchFailConnection();
     return NS_ERROR_ABORT;
   }
 
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &EventSource::AnnounceConnection);
-  NS_ENSURE_STATE(event);
-
-  rv = NS_DispatchToMainThread(event);
+  rv = NS_DispatchToMainThread(NewRunnableMethod(this, &EventSource::AnnounceConnection));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mStatus = PARSE_STATE_BEGIN_OF_STREAM;
 
   return NS_OK;
 }
 
 // this method parses the characters as they become available instead of
@@ -469,21 +465,17 @@ EventSource::OnStopRequest(nsIRequest *a
     return NS_ERROR_ABORT;
   }
 
   nsresult rv = CheckHealthOfRequestCallback(aRequest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   ClearFields();
 
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &EventSource::ReestablishConnection);
-  NS_ENSURE_STATE(event);
-
-  rv = NS_DispatchToMainThread(event);
+  rv = NS_DispatchToMainThread(NewRunnableMethod(this, &EventSource::ReestablishConnection));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // EventSource::nsIChannelEventSink
 //-----------------------------------------------------------------------------
@@ -895,21 +887,18 @@ EventSource::ConsoleError()
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 EventSource::DispatchFailConnection()
 {
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &EventSource::FailConnection);
-  NS_ENSURE_STATE(event);
 
-  return NS_DispatchToMainThread(event);
+  return NS_DispatchToMainThread(NewRunnableMethod(this, &EventSource::FailConnection));
 }
 
 void
 EventSource::FailConnection()
 {
   if (mReadyState == CLOSED) {
     return;
   }
@@ -973,17 +962,17 @@ EventSource::Thaw()
   }
 
   NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
 
   mFrozen = false;
   nsresult rv;
   if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) {
     nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents);
+      NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents);
     NS_ENSURE_STATE(event);
 
     mGoingToDispatchAllMessages = true;
 
     rv = NS_DispatchToMainThread(event);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -1033,17 +1022,17 @@ EventSource::DispatchCurrentMessageEvent
   size_t sizeBefore = mMessagesToDispatch.GetSize();
   mMessagesToDispatch.Push(message.forget());
   NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1,
                  NS_ERROR_OUT_OF_MEMORY);
 
 
   if (!mGoingToDispatchAllMessages) {
     nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents);
+      NewRunnableMethod(this, &EventSource::DispatchAllMessageEvents);
     NS_ENSURE_STATE(event);
 
     mGoingToDispatchAllMessages = true;
 
     return NS_DispatchToMainThread(event);
   }
 
   return NS_OK;
new file mode 100644
--- /dev/null
+++ b/dom/base/IframeSandboxKeywordList.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+/* NOTE: no include guard; this file is meant to maybe be included multiple
+   times.  It has a list of the sandbox keywords we support, with their
+   corresponding sandbox flags. */
+
+#include "nsSandboxFlags.h"
+
+// Each entry has the sandbox keyword as a string, the corresponding nsGkAtoms
+// atom name, and the corresponding sandbox flags.
+SANDBOX_KEYWORD("allow-same-origin", allowsameorigin,  SANDBOXED_ORIGIN)
+SANDBOX_KEYWORD("allow-forms", allowforms,  SANDBOXED_FORMS)
+SANDBOX_KEYWORD("allow-scripts", allowscripts,
+		SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES)
+SANDBOX_KEYWORD("allow-top-navigation", allowtopnavigation,
+		SANDBOXED_TOPLEVEL_NAVIGATION)
+SANDBOX_KEYWORD("allow-pointer-lock", allowpointerlock, SANDBOXED_POINTER_LOCK)
+SANDBOX_KEYWORD("allow-orientation-lock", alloworientationlock,
+		SANDBOXED_ORIENTATION_LOCK)
+SANDBOX_KEYWORD("allow-popups", allowpopups, SANDBOXED_AUXILIARY_NAVIGATION)
+
--- a/dom/base/ScreenOrientation.cpp
+++ b/dom/base/ScreenOrientation.cpp
@@ -535,17 +535,17 @@ ScreenOrientation::Notify(const hal::Scr
     doc->SetCurrentOrientation(mType, mAngle);
 
     Promise* pendingPromise = doc->GetOrientationPendingPromise();
     if (pendingPromise) {
       pendingPromise->MaybeResolve(JS::UndefinedHandleValue);
       doc->SetOrientationPendingPromise(nullptr);
     }
 
-    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this,
+    nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(this,
       &ScreenOrientation::DispatchChangeEvent);
     rv = NS_DispatchToMainThread(runnable);
     NS_WARN_IF(NS_FAILED(rv));
   }
 }
 
 void
 ScreenOrientation::UpdateActiveOrientationLock(ScreenOrientationInternal aOrientation)
@@ -610,17 +610,17 @@ ScreenOrientation::VisibleEventListener:
     doc->SetCurrentOrientation(orientation->DeviceType(), orientation->DeviceAngle());
 
     Promise* pendingPromise = doc->GetOrientationPendingPromise();
     if (pendingPromise) {
       pendingPromise->MaybeResolve(JS::UndefinedHandleValue);
       doc->SetOrientationPendingPromise(nullptr);
     }
 
-    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(orientation,
+    nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(orientation,
       &ScreenOrientation::DispatchChangeEvent);
     rv = NS_DispatchToMainThread(runnable);
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
   }
 
   return NS_OK;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -41,16 +41,17 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'dom'
 
 EXPORTS += [
     'AutocompleteFieldList.h',
     'Crypto.h',
     'HTMLSplitOnSpacesTokenizer.h',
+    'IframeSandboxKeywordList.h',
     'mozAutoDocUpdate.h',
     'mozFlushType.h',
     'nsAtomListUtils.h',
     'nsAttrAndChildArray.h',
     'nsAttrName.h',
     'nsAttrValue.h',
     'nsAttrValueInlines.h',
     'nsCaseTreatment.h',
@@ -171,16 +172,17 @@ EXPORTS.mozilla.dom += [
     'DOMImplementation.h',
     'DOMMatrix.h',
     'DOMParser.h',
     'DOMPoint.h',
     'DOMQuad.h',
     'DOMRect.h',
     'DOMRequest.h',
     'DOMStringList.h',
+    'DOMTokenListSupportedTokens.h',
     'Element.h',
     'ElementInlines.h',
     'EventSource.h',
     'File.h',
     'FileList.h',
     'FileReader.h',
     'FormData.h',
     'FragmentOrElement.h',
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -260,17 +260,17 @@ nsContentSink::ProcessHTTPHeaders(nsICha
   if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) {
     mDocument->SetHeaderData(nsGkAtoms::link,
                              NS_ConvertASCIItoUTF16(linkHeader));
 
     NS_ASSERTION(!mProcessLinkHeaderEvent.get(),
                  "Already dispatched an event?");
 
     mProcessLinkHeaderEvent =
-      NS_NewNonOwningRunnableMethod(this,
+      NewNonOwningRunnableMethod(this,
         &nsContentSink::DoProcessLinkHeader);
     rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get());
     if (NS_FAILED(rv)) {
       mProcessLinkHeaderEvent.Forget();
     }
   }
   
   return NS_OK;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -259,16 +259,17 @@ nsString* nsContentUtils::sShiftText = n
 nsString* nsContentUtils::sControlText = nullptr;
 nsString* nsContentUtils::sMetaText = nullptr;
 nsString* nsContentUtils::sOSText = nullptr;
 nsString* nsContentUtils::sAltText = nullptr;
 nsString* nsContentUtils::sModifierSeparator = nullptr;
 
 bool nsContentUtils::sInitialized = false;
 bool nsContentUtils::sIsFullScreenApiEnabled = false;
+bool nsContentUtils::sIsUnprefixedFullscreenApiEnabled = false;
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sIsCutCopyAllowed = true;
 bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
@@ -534,16 +535,19 @@ nsContentUtils::Init()
   sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
 
   Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
                                "dom.allow_XUL_XBL_for_file");
 
   Preferences::AddBoolVarCache(&sIsFullScreenApiEnabled,
                                "full-screen-api.enabled");
 
+  Preferences::AddBoolVarCache(&sIsUnprefixedFullscreenApiEnabled,
+                               "full-screen-api.unprefix.enabled");
+
   Preferences::AddBoolVarCache(&sTrustedFullScreenOnly,
                                "full-screen-api.allow-trusted-requests-only");
 
   Preferences::AddBoolVarCache(&sIsCutCopyAllowed,
                                "dom.allow_cut_copy", true);
 
   Preferences::AddBoolVarCache(&sIsPerformanceTimingEnabled,
                                "dom.enable_performance", true);
@@ -1364,29 +1368,23 @@ nsContentUtils::ParseSandboxAttributeToF
                | SANDBOXED_FORMS
                | SANDBOXED_SCRIPTS
                | SANDBOXED_AUTOMATIC_FEATURES
                | SANDBOXED_POINTER_LOCK
                | SANDBOXED_ORIENTATION_LOCK
                | SANDBOXED_DOMAIN;
 
 // Macro for updating the flag according to the keywords
-#define IF_KEYWORD(atom, flags) \
+#define SANDBOX_KEYWORD(string, atom, flags)                             \
   if (sandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { out &= ~(flags); }
 
-  IF_KEYWORD(allowsameorigin, SANDBOXED_ORIGIN)
-  IF_KEYWORD(allowforms,  SANDBOXED_FORMS)
-  IF_KEYWORD(allowscripts, SANDBOXED_SCRIPTS | SANDBOXED_AUTOMATIC_FEATURES)
-  IF_KEYWORD(allowtopnavigation, SANDBOXED_TOPLEVEL_NAVIGATION)
-  IF_KEYWORD(allowpointerlock, SANDBOXED_POINTER_LOCK)
-  IF_KEYWORD(alloworientationlock, SANDBOXED_ORIENTATION_LOCK)
-  IF_KEYWORD(allowpopups, SANDBOXED_AUXILIARY_NAVIGATION)
+#include "IframeSandboxKeywordList.h"
 
   return out;
-#undef IF_KEYWORD
+#undef SANDBOX_KEYWORD
 }
 
 nsIBidiKeyboard*
 nsContentUtils::GetBidiKeyboard()
 {
   if (!sBidiKeyboard) {
     nsresult rv = CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard);
     if (NS_FAILED(rv)) {
@@ -5037,33 +5035,40 @@ nsContentUtils::WarnScriptWasIgnored(nsI
   }
   msg.AppendLiteral("Unable to run script because scripts are blocked internally.");
 
   LogSimpleConsoleError(msg, "DOM");
 }
 
 /* static */
 bool
-nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
-{
-  if (!aRunnable) {
+nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable)
+{
+  nsCOMPtr<nsIRunnable> runnable = aRunnable;
+  if (!runnable) {
     return false;
   }
 
   if (sScriptBlockerCount) {
-    return sBlockedScriptRunners->AppendElement(aRunnable) != nullptr;
+    return sBlockedScriptRunners->AppendElement(runnable.forget()) != nullptr;
   }
   
-  nsCOMPtr<nsIRunnable> run = aRunnable;
-  run->Run();
+  runnable->Run();
 
   return true;
 }
 
 /* static */
+bool
+nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
+  nsCOMPtr<nsIRunnable> runnable = aRunnable;
+  return AddScriptRunner(runnable.forget());
+}
+
+/* static */
 void
 nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
 {
   MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!");
   CycleCollectedJSRuntime::Get()->RunInStableState(Move(aRunnable));
 }
 
 /* static */
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1603,16 +1603,17 @@ public:
    *       synchronously before the function returns.
    *
    * @param aRunnable  The nsIRunnable to run as soon as it's safe to execute
    *                   scripts. Passing null is allowed and results in nothing
    *                   happening. It is also allowed to pass an object that
    *                   has not yet been AddRefed.
    * @return false on out of memory, true otherwise.
    */
+  static bool AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable);
   static bool AddScriptRunner(nsIRunnable* aRunnable);
 
   /**
    * Returns true if it's safe to execute content script and false otherwise.
    *
    * The only known case where this lies is mutation events. They run, and can
    * run anything else, when this function returns false, but this is ok.
    */
@@ -1908,16 +1909,22 @@ public:
   static bool IsFocusedContent(const nsIContent *aContent);
 
   /**
    * Returns true if the DOM full-screen API is enabled.
    */
   static bool IsFullScreenApiEnabled();
 
   /**
+   * Returns true if the unprefixed fullscreen API is enabled.
+   */
+  static bool IsUnprefixedFullscreenApiEnabled()
+    { return sIsUnprefixedFullscreenApiEnabled; }
+
+  /**
    * Returns true if requests for full-screen are allowed in the current
    * context. Requests are only allowed if the user initiated them (like with
    * a mouse-click or key press), unless this check has been disabled by
    * setting the pref "full-screen-api.allow-trusted-requests-only" to false.
    */
   static bool IsRequestFullScreenAllowed();
 
   /**
@@ -2656,16 +2663,17 @@ private:
   static uint32_t sRunnersCountAtFirstBlocker;
   static uint32_t sScriptBlockerCountWhereRunnersPrevented;
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static bool sIsHandlingKeyBoardEvent;
   static bool sAllowXULXBL_for_file;
   static bool sIsFullScreenApiEnabled;
+  static bool sIsUnprefixedFullscreenApiEnabled;
   static bool sTrustedFullScreenOnly;
   static bool sIsCutCopyAllowed;
   static uint32_t sHandlingInputTimeout;
   static bool sIsPerformanceTimingEnabled;
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
--- a/dom/base/nsDOMTokenList.cpp
+++ b/dom/base/nsDOMTokenList.cpp
@@ -14,19 +14,21 @@
 #include "nsError.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/DOMTokenListBinding.h"
 #include "mozilla/ErrorResult.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-nsDOMTokenList::nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom)
+nsDOMTokenList::nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom,
+                               const DOMTokenListSupportedTokenArray aSupportedTokens)
   : mElement(aElement),
-    mAttrAtom(aAttrAtom)
+    mAttrAtom(aAttrAtom),
+    mSupportedTokens(aSupportedTokens)
 {
   // We don't add a reference to our element. If it goes away,
   // we'll be told to drop our reference
 }
 
 nsDOMTokenList::~nsDOMTokenList() { }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMTokenList, mElement)
@@ -340,16 +342,38 @@ nsDOMTokenList::Replace(const nsAString&
 
   tokens.AppendElement(aToken);
   RemoveInternal(attr, tokens);
 
   tokens[0] = aNewToken;
   AddInternal(attr, tokens);
 }
 
+bool
+nsDOMTokenList::Supports(const nsAString& aToken,
+                         ErrorResult& aError)
+{
+  if (!mSupportedTokens) {
+    aError.ThrowTypeError<MSG_TOKENLIST_NO_SUPPORTED_TOKENS>(
+      mElement->LocalName(),
+      nsDependentAtomString(mAttrAtom));
+    return false;
+  }
+
+  for (DOMTokenListSupportedToken* supportedToken = mSupportedTokens;
+       *supportedToken;
+       ++supportedToken) {
+    if (aToken.LowerCaseEqualsASCII(*supportedToken)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void
 nsDOMTokenList::Stringify(nsAString& aResult)
 {
   if (!mElement) {
     aResult.Truncate();
     return;
   }
 
--- a/dom/base/nsDOMTokenList.h
+++ b/dom/base/nsDOMTokenList.h
@@ -11,16 +11,17 @@
 #ifndef nsDOMTokenList_h___
 #define nsDOMTokenList_h___
 
 #include "nsCOMPtr.h"
 #include "nsDOMString.h"
 #include "nsWrapperCache.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/DOMTokenListSupportedTokens.h"
 
 namespace mozilla {
 class ErrorResult;
 
 } // namespace mozilla
 
 class nsAttrValue;
 class nsIAtom;
@@ -32,17 +33,18 @@ class nsDOMTokenList : public nsISupport
 {
 protected:
   typedef mozilla::dom::Element Element;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMTokenList)
 
-  nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom);
+  nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom,
+                 const mozilla::dom::DOMTokenListSupportedTokenArray = nullptr);
 
   virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
   Element* GetParentObject()
   {
     return mElement;
   }
 
@@ -64,16 +66,18 @@ public:
   void Remove(const nsTArray<nsString>& aTokens,
               mozilla::ErrorResult& aError);
   void Replace(const nsAString& aToken,
                const nsAString& aNewToken,
                mozilla::ErrorResult& aError);
   bool Toggle(const nsAString& aToken,
               const mozilla::dom::Optional<bool>& force,
               mozilla::ErrorResult& aError);
+  bool Supports(const nsAString& aToken,
+                mozilla::ErrorResult& aError);
 
   void GetValue(nsAString& aResult) { Stringify(aResult); }
   void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv);
   void Stringify(nsAString& aResult);
 
 protected:
   virtual ~nsDOMTokenList();
 
@@ -82,11 +86,12 @@ protected:
   void AddInternal(const nsAttrValue* aAttr,
                    const nsTArray<nsString>& aTokens);
   void RemoveInternal(const nsAttrValue* aAttr,
                       const nsTArray<nsString>& aTokens);
   inline const nsAttrValue* GetParsedAttr();
 
   nsCOMPtr<Element> mElement;
   nsCOMPtr<nsIAtom> mAttrAtom;
+  const mozilla::dom::DOMTokenListSupportedTokenArray mSupportedTokens;
 };
 
 #endif // nsDOMTokenList_h___
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1003,17 +1003,17 @@ nsDOMWindowUtils::SendNativeKeyEvent(int
                                      const nsAString& aUnmodifiedCharacters,
                                      nsIObserver* aObserver)
 {
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+  NS_DispatchToMainThread(NewRunnableMethod
     <int32_t, int32_t, uint32_t, nsString, nsString, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeKeyEvent, aNativeKeyboardLayout,
     aNativeKeyCode, aModifiers, aCharacters, aUnmodifiedCharacters, aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX,
@@ -1023,17 +1023,17 @@ nsDOMWindowUtils::SendNativeMouseEvent(i
                                        nsIDOMElement* aElement,
                                        nsIObserver* aObserver)
 {
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement);
   if (!widget)
     return NS_ERROR_FAILURE;
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+  NS_DispatchToMainThread(NewRunnableMethod
     <LayoutDeviceIntPoint, int32_t, int32_t, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeMouseEvent,
     LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, aModifierFlags,
     aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1042,17 +1042,17 @@ nsDOMWindowUtils::SendNativeMouseMove(in
                                       nsIDOMElement* aElement,
                                       nsIObserver* aObserver)
 {
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement);
   if (!widget)
     return NS_ERROR_FAILURE;
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+  NS_DispatchToMainThread(NewRunnableMethod
     <LayoutDeviceIntPoint, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeMouseMove,
     LayoutDeviceIntPoint(aScreenX, aScreenY), aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendNativeMouseScrollEvent(int32_t aScreenX,
@@ -1067,17 +1067,17 @@ nsDOMWindowUtils::SendNativeMouseScrollE
                                              nsIObserver* aObserver)
 {
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement);
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+  NS_DispatchToMainThread(NewRunnableMethod
     <mozilla::LayoutDeviceIntPoint, uint32_t, double, double, double, uint32_t, uint32_t, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeMouseScrollEvent,
     LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, aDeltaX, aDeltaY,
     aDeltaZ, aModifierFlags, aAdditionalFlags, aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1093,17 +1093,17 @@ nsDOMWindowUtils::SendNativeTouchPoint(u
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
   if (aPressure < 0 || aPressure > 1 || aOrientation > 359) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+  NS_DispatchToMainThread(NewRunnableMethod
     <uint32_t, nsIWidget::TouchPointerState, LayoutDeviceIntPoint, double, uint32_t, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeTouchPoint, aPointerId,
     (nsIWidget::TouchPointerState)aTouchState,
     LayoutDeviceIntPoint(aScreenX, aScreenY),
     aPressure, aOrientation, aObserver));
   return NS_OK;
 }
 
@@ -1113,32 +1113,32 @@ nsDOMWindowUtils::SendNativeTouchTap(int
                                      bool aLongTap,
                                      nsIObserver* aObserver)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs
+  NS_DispatchToMainThread(NewRunnableMethod
     <LayoutDeviceIntPoint, bool, nsIObserver*>
     (widget, &nsIWidget::SynthesizeNativeTouchTap,
     LayoutDeviceIntPoint(aScreenX, aScreenY), aLongTap, aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::ClearNativeTouchSequence(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
-  NS_DispatchToMainThread(NS_NewRunnableMethodWithArgs<nsIObserver*>
+  NS_DispatchToMainThread(NewRunnableMethod<nsIObserver*>
     (widget, &nsIWidget::ClearNativeTouchSequence, aObserver));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString)
 {
   // get the widget to send the event to
@@ -3662,32 +3662,32 @@ nsDOMWindowUtils::PostRestyleSelfEvent(n
     return NS_ERROR_INVALID_ARG;
   }
 
   nsLayoutUtils::PostRestyleEvent(element, eRestyle_Self, nsChangeHint(0));
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetMediaSuspended(bool* aSuspended)
+nsDOMWindowUtils::GetMediaSuspend(uint32_t* aSuspend)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
 
-  *aSuspended = window->GetMediaSuspended();
+  *aSuspend = window->GetMediaSuspend();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::SetMediaSuspended(bool aSuspended)
+nsDOMWindowUtils::SetMediaSuspend(uint32_t aSuspend)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
 
-  window->SetMediaSuspended(aSuspended);
+  window->SetMediaSuspend(aSuspend);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetAudioMuted(bool* aMuted)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2808,21 +2808,29 @@ nsDocument::InitCSP(nsIChannel* aChannel
   // Check if this is a document from a WebExtension.
   nsString addonId;
   principal->GetAddonId(addonId);
   bool applyAddonCSP = !addonId.IsEmpty();
 
   // Check if this is part of the Loop/Hello service
   bool applyLoopCSP = IsLoopDocument(aChannel);
 
+  // Check if this is a signed content to apply default CSP.
+  bool applySignedContentCSP = false;
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (loadInfo && loadInfo->GetVerifySignedContent()) {
+    applySignedContentCSP = true;
+  }
+
   // If there's no CSP to apply, go ahead and return early
   if (!applyAppDefaultCSP &&
       !applyAppManifestCSP &&
       !applyAddonCSP &&
       !applyLoopCSP &&
+      !applySignedContentCSP &&
       cspHeaderValue.IsEmpty() &&
       cspROHeaderValue.IsEmpty()) {
     if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
       nsCOMPtr<nsIURI> chanURI;
       aChannel->GetURI(getter_AddRefs(chanURI));
       nsAutoCString aspec;
       chanURI->GetAsciiSpec(aspec);
       MOZ_LOG(gCspPRLog, LogLevel::Debug,
@@ -2885,16 +2893,25 @@ nsDocument::InitCSP(nsIChannel* aChannel
     }
 
     rv = aps->GetAddonCSP(addonId, addonCSP);
     if (NS_SUCCEEDED(rv)) {
       csp->AppendPolicy(addonCSP, false, false);
     }
   }
 
+  // ----- if the doc is a signed content, apply the default CSP.
+  // Note that when the content signing becomes a standard, we might have
+  // to restrict this enforcement to "remote content" only.
+  if (applySignedContentCSP) {
+    nsAdoptingString signedContentCSP =
+      Preferences::GetString("security.signed_content.CSP.default");
+    csp->AppendPolicy(signedContentCSP, false, false);
+  }
+
   // ----- if the doc is part of Loop, apply the loop CSP
   if (applyLoopCSP) {
     nsAdoptingString loopCSP;
     loopCSP = Preferences::GetString("loop.CSP");
     NS_ASSERTION(loopCSP, "Missing loop.CSP preference");
     // If the pref has been removed, we continue without setting a CSP
     if (loopCSP) {
       csp->AppendPolicy(loopCSP, false, false);
@@ -4347,17 +4364,17 @@ nsDocument::SetStyleSheetApplicableState
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
                                "StyleSheetApplicableStateChanged",
                                mApplicable,
                                aApplicable);
   }
 
   if (!mSSApplicableStateNotificationPending) {
-    nsCOMPtr<nsIRunnable> notification = NS_NewRunnableMethod(this,
+    nsCOMPtr<nsIRunnable> notification = NewRunnableMethod(this,
       &nsDocument::NotifyStyleSheetApplicableStateChanged);
     mSSApplicableStateNotificationPending =
       NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
   }
 }
 
 void
 nsDocument::NotifyStyleSheetApplicableStateChanged()
@@ -4956,17 +4973,17 @@ nsDocument::MaybeEndOutermostXBLUpdate()
   // Only call BindingManager()->EndOutermostUpdate() when
   // we're not in an update and it is safe to run scripts.
   if (mUpdateNestLevel == 0 && mInXBLUpdate) {
     if (nsContentUtils::IsSafeToRunScript()) {
       mInXBLUpdate = false;
       BindingManager()->EndOutermostUpdate();
     } else if (!mInDestructor) {
       nsContentUtils::AddScriptRunner(
-        NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate));
+        NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate));
     }
   }
 }
 
 void
 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
 {
   if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
@@ -5274,17 +5291,17 @@ nsDocument::UnblockDOMContentLoaded()
   if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
     return;
   }
   mDidFireDOMContentLoaded = true;
 
   MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
   if (!mSynchronousDOMContentLoaded) {
     nsCOMPtr<nsIRunnable> ev =
-      NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
+      NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
     NS_DispatchToCurrentThread(ev);
   } else {
     DispatchContentLoadedEvents();
   }
 }
 
 void
 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
@@ -7257,17 +7274,17 @@ nsDocument::NotifyPossibleTitleChange(bo
 
   if (aBoundTitleElement) {
     mMayHaveTitleElement = true;
   }
   if (mPendingTitleChangeEvent.IsPending())
     return;
 
   RefPtr<nsRunnableMethod<nsDocument, void, false> > event =
-    NS_NewNonOwningRunnableMethod(this,
+    NewNonOwningRunnableMethod(this,
       &nsDocument::DoNotifyPossibleTitleChange);
   nsresult rv = NS_DispatchToCurrentThread(event);
   if (NS_SUCCEEDED(rv)) {
     mPendingTitleChangeEvent = event;
   }
 }
 
 void
@@ -7406,17 +7423,17 @@ nsDocument::InitializeFrameLoader(nsFram
     NS_WARNING("Trying to initialize a frame loader while"
                "document is being deleted");
     return NS_ERROR_FAILURE;
   }
 
   mInitializableFrameLoaders.AppendElement(aLoader);
   if (!mFrameLoaderRunner) {
     mFrameLoaderRunner =
-      NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
+      NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
   }
   return NS_OK;
 }
 
 nsresult
 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer)
@@ -7424,17 +7441,17 @@ nsDocument::FinalizeFrameLoader(nsFrameL
   mInitializableFrameLoaders.RemoveElement(aLoader);
   if (mInDestructor) {
     return NS_ERROR_FAILURE;
   }
 
   mFrameLoaderFinalizers.AppendElement(aFinalizer);
   if (!mFrameLoaderRunner) {
     mFrameLoaderRunner =
-      NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
+      NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
   }
   return NS_OK;
 }
 
 void
 nsDocument::MaybeInitializeFinalizeFrameLoaders()
@@ -7448,17 +7465,17 @@ nsDocument::MaybeInitializeFinalizeFrame
 
   // We're not in an update, but it is not safe to run scripts, so
   // postpone frameloader initialization and finalization.
   if (!nsContentUtils::IsSafeToRunScript()) {
     if (!mInDestructor && !mFrameLoaderRunner &&
         (mInitializableFrameLoaders.Length() ||
          mFrameLoaderFinalizers.Length())) {
       mFrameLoaderRunner =
-        NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
+        NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
       nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
     }
     return;
   }
   mFrameLoaderRunner = nullptr;
 
   // Don't use a temporary array for mInitializableFrameLoaders, because
   // loading a frame may cause some other frameloader to be removed from the
@@ -9059,17 +9076,17 @@ nsDocument::BlockOnload()
   // -- it's not ours.
   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
     if (!nsContentUtils::IsSafeToRunScript()) {
       // Because AddRequest may lead to OnStateChange calls in chrome,
       // block onload only when there are no script blockers.
       ++mAsyncOnloadBlockCount;
       if (mAsyncOnloadBlockCount == 1) {
         bool success = nsContentUtils::AddScriptRunner(
-          NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
+          NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
 
         // The script runner shouldn't fail to add. But if somebody broke
         // something and it does, we'll thrash at 100% cpu forever. The best
         // response is just to ignore the onload blocking request. See bug 579535.
         if (!success) {
           NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
           mAsyncOnloadBlockCount = 0;
         }
@@ -11675,16 +11692,24 @@ nsresult nsDocument::RemoteFrameFullscre
 }
 
 nsresult nsDocument::RemoteFrameFullscreenReverted()
 {
   RestorePreviousFullScreenState();
   return NS_OK;
 }
 
+/* static */ bool
+nsDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return nsContentUtils::IsCallerChrome() ||
+         nsContentUtils::IsUnprefixedFullscreenApiEnabled();
+}
+
 static void
 ReleaseVRDeviceProxyRef(void *, nsIAtom*, void *aPropertyValue, void *)
 {
   if (aPropertyValue) {
     static_cast<gfx::VRDeviceProxy*>(aPropertyValue)->Release();
   }
 }
 
@@ -12767,17 +12792,17 @@ nsDocument::GetVisibilityState() const
 
   return dom::VisibilityState::Visible;
 }
 
 /* virtual */ void
 nsDocument::PostVisibilityUpdateEvent()
 {
   nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState);
+    NewRunnableMethod(this, &nsDocument::UpdateVisibilityState);
   NS_DispatchToMainThread(event);
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozHidden(bool* aHidden)
 {
   *aHidden = MozHidden();
   return NS_OK;
@@ -13498,17 +13523,17 @@ nsIDocument::RebuildUserFontSet()
   // post an event to rebuild it.  Setting the user font set to be dirty
   // and lazily rebuilding it isn't sufficient, since it is only the act
   // of rebuilding it that will trigger the style change reflow that
   // calls GetUserFontSet.  (This reflow causes rebuilding of text runs,
   // which starts font loads, whose completion causes another style
   // change reflow).
   if (!mPostedFlushUserFontSet) {
     nsCOMPtr<nsIRunnable> ev =
-      NS_NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet);
+      NewRunnableMethod(this, &nsIDocument::HandleRebuildUserFontSet);
     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
       mPostedFlushUserFontSet = true;
     }
   }
 }
 
 FontFaceSet*
 nsIDocument::Fonts()
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1231,16 +1231,18 @@ public:
   // Returns strong references to mBlockedTrackingNodes. (nsIDocument.h)
   //
   // This array contains nodes that have been blocked to prevent
   // user tracking. They most likely have had their nsIChannel
   // canceled by the URL classifier (Safebrowsing).
   //
   already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
 
+  static bool IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject);
+
   // Do the "fullscreen element ready check" from the fullscreen spec.
   // It returns true if the given element is allowed to go into fullscreen.
   bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
 
   // This is called asynchronously by nsIDocument::AsyncRequestFullScreen()
   // to move this document into full-screen mode if allowed.
   void RequestFullScreen(mozilla::UniquePtr<FullscreenRequest>&& aRequest);
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -136,17 +136,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserPersistable)
 NS_INTERFACE_MAP_END
 
 nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
   : mOwnerContent(aOwner)
   , mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
-  , mDetachedSubdocViews(nullptr)
+  , mDetachedSubdocFrame(nullptr)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
   , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
   , mIsPrerendered(false)
   , mDepthTooGreat(false)
   , mIsTopLevelContent(false)
   , mDestroyCalled(false)
   , mNeedsAsyncDestroy(false)
@@ -2883,28 +2883,28 @@ nsFrameLoader::SwapRemoteBrowser(nsITabP
   if (!mRemoteBrowserShown) {
     ShowRemoteFrame(ScreenIntSize(0, 0));
   }
 
   return NS_OK;
 }
 
 void
-nsFrameLoader::SetDetachedSubdocView(nsView* aDetachedViews,
-                                     nsIDocument* aContainerDoc)
+nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame,
+                                      nsIDocument* aContainerDoc)
 {
-  mDetachedSubdocViews = aDetachedViews;
+  mDetachedSubdocFrame = aDetachedFrame;
   mContainerDocWhileDetached = aContainerDoc;
 }
 
-nsView*
-nsFrameLoader::GetDetachedSubdocView(nsIDocument** aContainerDoc) const
+nsIFrame*
+nsFrameLoader::GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const
 {
   NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached);
-  return mDetachedSubdocViews;
+  return mDetachedSubdocFrame.GetFrame();
 }
 
 void
 nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags)
 {
   if (mDocShell) {
     uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
 
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -20,16 +20,17 @@
 #include "nsIURI.h"
 #include "nsAutoPtr.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Attributes.h"
 #include "nsStubMutationObserver.h"
 #include "Units.h"
 #include "nsIWebBrowserPersistable.h"
+#include "nsIFrame.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
 class nsIInProcessContentFrameMessageManager;
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
@@ -186,33 +187,33 @@ public:
    * this object, which means you can't have called ShowRemoteFrame()
    * or ReallyStartLoading().
    */
   void SetRemoteBrowser(nsITabParent* aTabParent);
 
   nsresult SwapRemoteBrowser(nsITabParent* aTabParent);
 
   /**
-   * Stashes a detached view on the frame loader. We do this when we're
+   * Stashes a detached nsIFrame on the frame loader. We do this when we're
    * destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is
-   * being reframed we'll restore the detached view when it's recreated,
+   * being reframed we'll restore the detached nsIFrame when it's recreated,
    * otherwise we'll discard the old presentation and set the detached
-   * subdoc view to null. aContainerDoc is the document containing the
+   * subdoc nsIFrame to null. aContainerDoc is the document containing the
    * the subdoc frame. This enables us to detect when the containing
    * document has changed during reframe, so we can discard the presentation
    * in that case.
    */
-  void SetDetachedSubdocView(nsView* aDetachedView,
-                             nsIDocument* aContainerDoc);
+  void SetDetachedSubdocFrame(nsIFrame* aDetachedFrame,
+                              nsIDocument* aContainerDoc);
 
   /**
-   * Retrieves the detached view and the document containing the view,
-   * as set by SetDetachedSubdocView().
+   * Retrieves the detached nsIFrame and the document containing the nsIFrame,
+   * as set by SetDetachedSubdocFrame().
    */
-  nsView* GetDetachedSubdocView(nsIDocument** aContainerDoc) const;
+  nsIFrame* GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const;
 
   /**
    * Applies a new set of sandbox flags. These are merged with the sandbox
    * flags from our owning content's owning document with a logical OR, this
    * ensures that we can only add restrictions and never remove them.
    */
   void ApplySandboxFlags(uint32_t sandboxFlags);
 
@@ -352,22 +353,22 @@ private:
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
   RefPtr<mozilla::dom::Element> mOwnerContentStrong;
 
   // Note: this variable must be modified only by ResetPermissionManagerStatus()
   uint32_t mAppIdSentToPermissionManager;
 
-  // Stores the root view of the subdocument while the subdocument is being
+  // Stores the root frame of the subdocument while the subdocument is being
   // reframed. Used to restore the presentation after reframing.
-  nsView* mDetachedSubdocViews;
+  nsWeakFrame mDetachedSubdocFrame;
   // Stores the containing document of the frame corresponding to this
   // frame loader. This is reference is kept valid while the subframe's
-  // presentation is detached and stored in mDetachedSubdocViews. This
+  // presentation is detached and stored in mDetachedSubdocFrame. This
   // enables us to detect whether the frame has moved documents during
   // a reframe, so that we know not to restore the presentation.
   nsCOMPtr<nsIDocument> mContainerDocWhileDetached;
 
   TabParent* mRemoteBrowser;
   uint64_t mChildID;
 
   // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -610,17 +610,18 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW
 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
   mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
   mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mInnerObjectsFreed(false),
   mIsModalContentWindow(false),
-  mIsActive(false), mIsBackground(false), mMediaSuspended(false),
+  mIsActive(false), mIsBackground(false),
+  mMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED),
   mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
   mDesktopModeViewport(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
  {}
 
@@ -2673,17 +2674,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
     }
   }
 
   /* No mDocShell means we're already been partially closed down.  When that
      happens, setting status isn't a big requirement, so don't. (Doesn't happen
      under normal circumstances, but bug 49615 describes a case.) */
 
   nsContentUtils::AddScriptRunner(
-    NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus));
+    NewRunnableMethod(this, &nsGlobalWindow::ClearStatus));
 
   // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner
   // window (see bug 776497). Be safe.
   bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) &&
                           GetCurrentInnerWindowInternal();
 
   nsresult rv = NS_OK;
 
@@ -2979,31 +2980,31 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
   nsJSContext::PokeGC(JS::gcreason::SET_NEW_DOCUMENT);
   mContext->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
     nsContentUtils::AddScriptRunner(
-      NS_NewRunnableMethod(newInnerWindow,
-                           &nsGlobalWindow::FireOnNewGlobalObject));
+      NewRunnableMethod(newInnerWindow,
+                        &nsGlobalWindow::FireOnNewGlobalObject));
   }
 
   if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
     // We should probably notify. However if this is the, arguably bad,
     // situation when we're creating a temporary non-chrome-about-blank
     // document in a chrome docshell, don't notify just yet. Instead wait
     // until we have a real chrome doc.
     if (!mDocShell ||
         mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome ||
         nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) {
       newInnerWindow->mHasNotifiedGlobalCreated = true;
       nsContentUtils::AddScriptRunner(
-        NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated));
+        NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated));
     }
   }
 
   PreloadLocalStorage();
 
   return NS_OK;
 }
 
@@ -3885,40 +3886,39 @@ nsPIDOMWindowInner::CreatePerformanceObj
         parentPerformance = parentInnerWindow->GetPerformance();
       }
     }
     mPerformance =
       new nsPerformance(this, timing, timedChannel, parentPerformance);
   }
 }
 
-bool
-nsPIDOMWindowOuter::GetMediaSuspended() const
+SuspendTypes
+nsPIDOMWindowOuter::GetMediaSuspend() const
 {
   if (IsInnerWindow()) {
-    return mOuterWindow->GetMediaSuspended();
-  }
-
-  return mMediaSuspended;
-}
-
-void
-nsPIDOMWindowOuter::SetMediaSuspended(bool aSuspended)
+    return mOuterWindow->GetMediaSuspend();
+  }
+
+  return mMediaSuspend;
+}
+
+void
+nsPIDOMWindowOuter::SetMediaSuspend(SuspendTypes aSuspend)
 {
   if (IsInnerWindow()) {
-    mOuterWindow->SetMediaSuspended(aSuspended);
-    return;
-  }
-
-  if (mMediaSuspended == aSuspended) {
-    return;
-  }
-
-  mMediaSuspended = aSuspended;
-  RefreshMediaElements();
+    mOuterWindow->SetMediaSuspend(aSuspend);
+    return;
+  }
+
+  if (!IsDisposableSuspend(aSuspend)) {
+    mMediaSuspend = aSuspend;
+  }
+
+  RefreshMediaElementsSuspend(aSuspend);
 }
 
 bool
 nsPIDOMWindowOuter::GetAudioMuted() const
 {
   if (IsInnerWindow()) {
     return mOuterWindow->GetAudioMuted();
   }
@@ -3934,17 +3934,17 @@ nsPIDOMWindowOuter::SetAudioMuted(bool a
     return;
   }
 
   if (mAudioMuted == aMuted) {
     return;
   }
 
   mAudioMuted = aMuted;
-  RefreshMediaElements();
+  RefreshMediaElementsVolume();
 }
 
 float
 nsPIDOMWindowOuter::GetAudioVolume() const
 {
   if (IsInnerWindow()) {
     return mOuterWindow->GetAudioVolume();
   }
@@ -3963,30 +3963,46 @@ nsPIDOMWindowOuter::SetAudioVolume(float
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   if (mAudioVolume == aVolume) {
     return NS_OK;
   }
 
   mAudioVolume = aVolume;
-  RefreshMediaElements();
+  RefreshMediaElementsVolume();
   return NS_OK;
 }
 
 void
-nsPIDOMWindowOuter::RefreshMediaElements()
+nsPIDOMWindowOuter::RefreshMediaElementsVolume()
 {
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (service) {
     service->RefreshAgentsVolume(GetOuterWindow());
   }
 }
 
 void
+nsPIDOMWindowOuter::RefreshMediaElementsSuspend(SuspendTypes aSuspend)
+{
+  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  if (service) {
+    service->RefreshAgentsSuspend(GetOuterWindow(), aSuspend);
+  }
+}
+
+bool
+nsPIDOMWindowOuter::IsDisposableSuspend(SuspendTypes aSuspend) const
+{
+  return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+          aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
+}
+
+void
 nsPIDOMWindowOuter::SetServiceWorkersTestingEnabled(bool aEnabled)
 {
   // Devtools should only be setting this on the top level window.  Its
   // ok if devtools clears the flag on clean up of nested windows, though.
   // It will have no affect.
 #ifdef DEBUG
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetScriptableTop();
   MOZ_ASSERT_IF(aEnabled, this == topWindow);
@@ -10007,18 +10023,23 @@ nsGlobalWindow::GetFocusMethod()
   FORWARD_TO_INNER(GetFocusMethod, (), 0);
 
   return mFocusMethod;
 }
 
 void
 nsGlobalWindow::InitializeShowFocusRings()
 {
+  MOZ_ASSERT(IsInnerWindow());
+
+  // Initialize the focus ring state from the parent window. For root windows,
+  // there is no need to do this as SetKeyboardIndicators will propogate any
+  // non-default value to child windows.
   nsPIDOMWindowOuter* root = GetPrivateRoot();
-  if (root) {
+  if (root && GetOuterWindow() != root) {
     bool showAccelerators = false, showFocusRings = false;
     root->GetKeyboardIndicators(&showAccelerators, &showFocusRings);
     mShowFocusRings = showFocusRings;
   }
 }
 
 bool
 nsGlobalWindow::ShouldShowFocusRing()
@@ -11702,17 +11723,17 @@ public:
     : mWin(aWin)
   {
     MOZ_ASSERT(mWin);
     MOZ_ASSERT(mWin->IsOuterWindow());
   }
   ~AutoUnblockScriptClosing()
   {
     void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing;
-    NS_DispatchToCurrentThread(NS_NewRunnableMethod(mWin, run));
+    NS_DispatchToCurrentThread(NewRunnableMethod(mWin, run));
   }
 };
 
 nsresult
 nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
                              const nsAString& aOptions, bool aDialog,
                              bool aContentModal, bool aCalledNoScript,
                              bool aDoJSFixups, bool aNavigate,
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -33,16 +33,18 @@ class nsIScriptTimeoutHandler;
 class nsIURI;
 class nsPerformance;
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 struct nsTimeout;
 
+typedef uint32_t SuspendTypes;
+
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
 class ServiceWorkerRegistrationMainThread;
 } // namespace dom
 namespace gfx {
 class VRDeviceProxy;
@@ -650,17 +652,29 @@ protected:
   // Only used on outer windows.
   bool                   mIsActive;
 
   // Tracks whether our docshell is active.  If it is, mIsBackground
   // is false.  Too bad we have so many different concepts of
   // "active".  Only used on outer windows.
   bool                   mIsBackground;
 
-  bool                   mMediaSuspended;
+  /**
+   * The suspended types can be "disposable" or "permanent". This varable only
+   * stores the value about permanent suspend.
+   * - disposable
+   * To pause all playing media in that window, but doesn't affect the media
+   * which starts after that.
+   *
+   * - permanent
+   * To pause all media in that window, and also affect the media which starts
+   * after that.
+   */
+  SuspendTypes       mMediaSuspend;
+
   bool                   mAudioMuted;
   float                  mAudioVolume;
 
   bool                   mAudioCaptured;
 
   // current desktop mode flag.
   bool                   mDesktopModeViewport;
 
@@ -806,17 +820,19 @@ protected:
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 // NB: It's very very important that these two classes have identical vtables
 // and memory layout!
 class nsPIDOMWindowOuter : public nsPIDOMWindow<mozIDOMWindowProxy>
 {
 protected:
-  void RefreshMediaElements();
+  void RefreshMediaElementsVolume();
+  void RefreshMediaElementsSuspend(SuspendTypes aSuspend);
+  bool IsDisposableSuspend(SuspendTypes aSuspend) const;
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOWOUTER_IID)
 
   static nsPIDOMWindowOuter* From(mozIDOMWindowProxy* aFrom) {
     return static_cast<nsPIDOMWindowOuter*>(aFrom);
   }
 
@@ -857,18 +873,18 @@ public:
     return mDesktopModeViewport;
   }
   bool IsBackground()
   {
     return mIsBackground;
   }
 
   // Audio API
-  bool GetMediaSuspended() const;
-  void SetMediaSuspended(bool aSuspended);
+  SuspendTypes GetMediaSuspend() const;
+  void SetMediaSuspend(SuspendTypes aSuspend);
 
   bool GetAudioMuted() const;
   void SetAudioMuted(bool aMuted);
 
   float GetAudioVolume() const;
   nsresult SetAudioVolume(float aVolume);
 
   void SetServiceWorkersTestingEnabled(bool aEnabled);
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -492,18 +492,18 @@ nsScriptLoader::ProcessScriptElement(nsI
   nsresult rv = NS_OK;
   RefPtr<nsScriptLoadRequest> request;
   if (aElement->GetScriptExternal()) {
     // external script
     nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
     if (!scriptURI) {
       // Asynchronously report the failure to create a URI object
       NS_DispatchToCurrentThread(
-        NS_NewRunnableMethod(aElement,
-                             &nsIScriptElement::FireErrorEvent));
+        NewRunnableMethod(aElement,
+                          &nsIScriptElement::FireErrorEvent));
       return false;
     }
 
     // Double-check that the preload matches what we're asked to load now.
     mozilla::net::ReferrerPolicy ourRefPolicy = mDocument->GetReferrerPolicy();
     CORSMode ourCORSMode = aElement->GetCORSMode();
     nsTArray<PreloadInfo>::index_type i =
       mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
@@ -559,18 +559,18 @@ nsScriptLoader::ProcessScriptElement(nsI
       request->mReferrerPolicy = ourRefPolicy;
 
       // set aScriptFromHead to false so we don't treat non preloaded scripts as
       // blockers for full page load. See bug 792438.
       rv = StartLoad(request, type, false);
       if (NS_FAILED(rv)) {
         // Asynchronously report the load failure
         NS_DispatchToCurrentThread(
-          NS_NewRunnableMethod(aElement,
-                               &nsIScriptElement::FireErrorEvent));
+          NewRunnableMethod(aElement,
+                            &nsIScriptElement::FireErrorEvent));
         return false;
       }
     }
 
     // Should still be in loading stage of script.
     NS_ASSERTION(!request->InCompilingStage(),
                  "Request should not yet be in compiling stage.");
 
@@ -1139,20 +1139,18 @@ nsScriptLoader::EvaluateScript(nsScriptL
   context->SetProcessingScriptTag(oldProcessingScriptTag);
   return rv;
 }
 
 void
 nsScriptLoader::ProcessPendingRequestsAsync()
 {
   if (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) {
-    nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
-      &nsScriptLoader::ProcessPendingRequests);
-
-    NS_DispatchToCurrentThread(ev);
+    NS_DispatchToCurrentThread(NewRunnableMethod(this,
+                                                 &nsScriptLoader::ProcessPendingRequests));
   }
 }
 
 void
 nsScriptLoader::ProcessPendingRequests()
 {
   RefPtr<nsScriptLoadRequest> request;
 
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -139,17 +139,18 @@ nsStyleLinkElement::IsImportEnabled()
                                  false);
     sAdded = true;
   }
 
   return sImportsEnabled;
 }
 
 static uint32_t ToLinkMask(const nsAString& aLink, nsIPrincipal* aPrincipal)
-{ 
+{
+  // Keep this in sync with sRelValues in HTMLLinkElement.cpp
   if (aLink.EqualsLiteral("prefetch"))
     return nsStyleLinkElement::ePREFETCH;
   else if (aLink.EqualsLiteral("dns-prefetch"))
     return nsStyleLinkElement::eDNS_PREFETCH;
   else if (aLink.EqualsLiteral("stylesheet"))
     return nsStyleLinkElement::eSTYLESHEET;
   else if (aLink.EqualsLiteral("next"))
     return nsStyleLinkElement::eNEXT;
--- a/dom/base/nsTextNode.cpp
+++ b/dom/base/nsTextNode.cpp
@@ -268,18 +268,17 @@ nsAttributeTextNode::AttributeChanged(ns
                                       const nsAttrValue* aOldValue)
 {
   if (aNameSpaceID == mNameSpaceID && aAttribute == mAttrName &&
       aElement == mGrandparent) {
     // Since UpdateText notifies, do it when it's safe to run script.  Note
     // that if we get unbound while the event is up that's ok -- we'll just
     // have no grandparent when it fires, and will do nothing.
     void (nsAttributeTextNode::*update)() = &nsAttributeTextNode::UpdateText;
-    nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, update);
-    nsContentUtils::AddScriptRunner(ev);
+    nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update));
   }
 }
 
 void
 nsAttributeTextNode::NodeWillBeDestroyed(const nsINode* aNode)
 {
   NS_ASSERTION(aNode == static_cast<nsINode*>(mGrandparent), "Wrong node!");
   mGrandparent = nullptr;
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -1503,25 +1503,21 @@ nsXMLHttpRequest::Open(const nsACString&
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
   nsAutoCString method;
   nsresult rv = FetchUtil::GetValidRequestMethod(inMethod, method);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // sync request is not allowed using withCredential or responseType
+  // sync request is not allowed to use responseType or timeout
   // in window context
   if (!async && HasOrHasHadOwner() &&
-      (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
-       mTimeoutMilliseconds ||
+      (mTimeoutMilliseconds ||
        mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
-    if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
-      LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
-    }
     if (mTimeoutMilliseconds) {
       LogMessage("TimeoutSyncXHRWarning", GetOwner());
     }
     if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
       LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
     }
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
@@ -3200,24 +3196,16 @@ nsXMLHttpRequest::SetWithCredentials(boo
   // "sent", so we use mState directly.
   if ((!(mState & XML_HTTP_REQUEST_UNSENT) &&
        !(mState & XML_HTTP_REQUEST_OPENED)) ||
       mIsAnon) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
-  // sync request is not allowed setting withCredentials in window context
-  if (HasOrHasHadOwner() &&
-      !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
-    LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
-    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
-    return;
-  }
-
   if (aWithCredentials) {
     mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
   } else {
     mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
   }
 }
 
 nsresult
--- a/dom/base/test/test_xhr_withCredentials.html
+++ b/dom/base/test/test_xhr_withCredentials.html
@@ -13,25 +13,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=814050">Mozilla Bug 814050</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 814050 **/
+/** Test for Bug 814050, but modified now that the spec has changed **/
 var xhr = new XMLHttpRequest();
 xhr.open("GET", "", false);
-try {
-  xhr.withCredentials = true;
-  ok(false, "Should throw on withCredentials sets for sync XHR");
-} catch (e) {
-  ok(true, "Should throw on withCredentials sets for sync XHR");
-}
+xhr.withCredentials = true;
+ok(true, "Should not throw on withCredentials sets for sync XHR");
 
 var xhr = new XMLHttpRequest();
 xhr.open("GET", "", true);
 xhr.withCredentials = true;
 ok(true, "Should not throw on withCredentials sets for async XHR");
 
 </script>
 </pre>
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -85,16 +85,19 @@ CallbackObject::CallSetup::CallSetup(Cal
       // JS-implemented WebIDL we never have a window here.
       nsGlobalWindow* win =
         aIsJSImplementedWebIDL ? nullptr : xpc::WindowGlobalOrNull(realCallback);
       if (win) {
         MOZ_ASSERT(win->IsInnerWindow());
         // We don't want to run script in windows that have been navigated away
         // from.
         if (!win->AsInner()->HasActiveDocument()) {
+          aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+            NS_LITERAL_CSTRING("Refusing to execute function from window "
+                               "whose document is no longer active."));
           return;
         }
         globalObject = win;
       } else {
         // No DOM Window. Store the global.
         JSObject* glob = js::GetGlobalForObjectCrossCompartment(realCallback);
         globalObject = xpc::NativeGlobal(glob);
         MOZ_ASSERT(globalObject);
@@ -104,32 +107,38 @@ CallbackObject::CallSetup::CallSetup(Cal
       globalObject = xpc::NativeGlobal(global);
       MOZ_ASSERT(globalObject);
     }
 
     // Bail out if there's no useful global. This seems to happen intermittently
     // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
     // null in some kind of teardown state.
     if (!globalObject->GetGlobalJSObject()) {
+      aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+        NS_LITERAL_CSTRING("Refusing to execute function from global which is "
+                           "being torn down."));
       return;
     }
 
     // Off the main thread, AutoEntryScript expects us to pass a JSContext.
     mAutoEntryScript.emplace(globalObject, aExecutionReason, mIsMainThread,
                              mIsMainThread ? nullptr
                                            : workers::GetCurrentThreadJSContext());
     mAutoEntryScript->SetWebIDLCallerPrincipal(webIDLCallerPrincipal);
     nsIGlobalObject* incumbent = aCallback->IncumbentGlobalOrNull();
     if (incumbent) {
       // The callback object traces its incumbent JS global, so in general it
       // should be alive here. However, it's possible that we could run afoul
       // of the same IPC global weirdness described above, wherein the
       // nsIGlobalObject has severed its reference to the JS global. Let's just
       // be safe here, so that nobody has to waste a day debugging gaia-ui tests.
       if (!incumbent->GetGlobalJSObject()) {
+      aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+        NS_LITERAL_CSTRING("Refusing to execute function because our "
+                           "incumbent global is being torn down."));
         return;
       }
       mAutoIncumbentScript.emplace(incumbent);
     }
 
     cx = mAutoEntryScript->cx();
 
     // Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
@@ -146,16 +155,19 @@ CallbackObject::CallSetup::CallSetup(Cal
   if (mIsMainThread && !aIsJSImplementedWebIDL) {
     // Check that it's ok to run this callback at all.
     // Make sure to use realCallback to get the global of the callback object,
     // not the wrapper.
     bool allowed = nsContentUtils::GetSecurityManager()->
       ScriptAllowed(js::GetGlobalForObjectCrossCompartment(realCallback));
 
     if (!allowed) {
+      aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+        NS_LITERAL_CSTRING("Refusing to execute function from global in which "
+                           "script is disabled."));
       return;
     }
   }
 
   mAsyncStack.emplace(cx, aCallback->GetCreationStack());
   if (*mAsyncStack) {
     mAsyncStackSetter.emplace(cx, *mAsyncStack, aExecutionReason);
   }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -14874,17 +14874,17 @@ class CGCallback(CGClass):
 
         setupCall = fill(
             """
             if (!aExecutionReason) {
               aExecutionReason = "${executionReason}";
             }
             CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aCompartment);
             if (!s.GetContext()) {
-              aRv.Throw(NS_ERROR_UNEXPECTED);
+              MOZ_ASSERT(aRv.Failed());
               return${errorReturn};
             }
             """,
             errorReturn=errorReturn,
             executionReason=method.getPrettyName())
 
         bodyWithThis = fill(
             """
@@ -15239,17 +15239,17 @@ class CallbackMember(CGNativeMember):
         else:
             callSetup += ', "%s", aExceptionHandling, aCompartment' % self.getPrettyName()
         callSetup += ");\n"
         return fill(
             """
             $*{callSetup}
             JSContext* cx = s.GetContext();
             if (!cx) {
-              aRv.Throw(NS_ERROR_UNEXPECTED);
+              MOZ_ASSERT(aRv.Failed());
               return${errorReturn};
             }
             """,
             callSetup=callSetup,
             errorReturn=self.getDefaultRetval())
 
     def getArgcDecl(self):
         return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -91,8 +91,9 @@ MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYP
 MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
 MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
 MSG_DEF(MSG_TYPEDARRAY_IS_SHARED, 1, JSEXN_TYPEERR, "{0} can't be a typed array on SharedArrayBuffer")
 MSG_DEF(MSG_CACHE_ADD_FAILED_RESPONSE, 3, JSEXN_TYPEERR, "Cache got {0} response with bad status {1} while trying to add request {2}")
 MSG_DEF(MSG_SW_UPDATE_BAD_REGISTRATION, 2, JSEXN_TYPEERR, "Failed to update the ServiceWorker for scope {0] because the registration has been {1} since the update was scheduled.")
 MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, JSEXN_TYPEERR, "Invalid duration '{0}'.")
 MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
 MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
+MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -735,33 +735,33 @@ Context::ThreadsafeHandle::AllowToClose(
   if (mOwningThread == NS_GetCurrentThread()) {
     AllowToCloseOnOwningThread();
     return;
   }
 
   // Dispatch is guaranteed to succeed here because we block shutdown until
   // all Contexts have been destroyed.
   nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread);
+    NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread);
   MOZ_ALWAYS_SUCCEEDS(
     mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
 }
 
 void
 Context::ThreadsafeHandle::InvalidateAndAllowToClose()
 {
   if (mOwningThread == NS_GetCurrentThread()) {
     InvalidateAndAllowToCloseOnOwningThread();
     return;
   }
 
   // Dispatch is guaranteed to succeed here because we block shutdown until
   // all Contexts have been destroyed.
   nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
+    NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
   MOZ_ALWAYS_SUCCEEDS(
     mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
 }
 
 Context::ThreadsafeHandle::ThreadsafeHandle(Context* aContext)
   : mStrongRef(aContext)
   , mWeakRef(aContext)
   , mOwningThread(NS_GetCurrentThread())
@@ -775,20 +775,17 @@ Context::ThreadsafeHandle::~ThreadsafeHa
   // always holding a strong ref to the ThreadsafeHandle via the owning
   // runnable.  So we cannot run the ThreadsafeHandle destructor simultaneously.
   if (!mStrongRef || mOwningThread == NS_GetCurrentThread()) {
     return;
   }
 
   // Dispatch is guaranteed to succeed here because we block shutdown until
   // all Contexts have been destroyed.
-  nsCOMPtr<nsIRunnable> runnable =
-    NS_NewNonOwningRunnableMethod(mStrongRef.forget().take(), &Context::Release);
-  MOZ_ALWAYS_SUCCEEDS(
-    mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
+  NS_ProxyRelease(mOwningThread, mStrongRef.forget());
 }
 
 void
 Context::ThreadsafeHandle::AllowToCloseOnOwningThread()
 {
   MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
 
   // A Context "closes" when its ref count drops to zero.  Dropping this
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -907,17 +907,17 @@ private:
   }
 
   void
   CallOnAsyncCopyCompleteOnTargetThread(nsresult aRv)
   {
     // May be on any thread, including STS event target.  Non-owning runnable
     // here since we are guaranteed the Action will survive until
     // CompleteOnInitiatingThread is called.
-    nsCOMPtr<nsIRunnable> runnable = NS_NewNonOwningRunnableMethodWithArgs<nsresult>(
+    nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod<nsresult>(
       this, &CachePutAllAction::OnAsyncCopyComplete, aRv);
     MOZ_ALWAYS_SUCCEEDS(
       mTargetThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
   }
 
   void
   DoResolve(nsresult aRv)
   {
@@ -1756,19 +1756,17 @@ Manager::~Manager()
   MOZ_ASSERT(mState == Closing);
   MOZ_ASSERT(!mContext);
 
   nsCOMPtr<nsIThread> ioThread;
   mIOThread.swap(ioThread);
 
   // Don't spin the event loop in the destructor waiting for the thread to
   // shutdown.  Defer this to the main thread, instead.
-  nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethod(ioThread, &nsIThread::Shutdown);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioThread, &nsIThread::Shutdown)));
 }
 
 void
 Manager::Init(Manager* aOldManager)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
 
   RefPtr<Context> oldContext;
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -166,28 +166,24 @@ CameraPreviewMediaStream::SetCurrentFram
     for (nsTArray<RefPtr<VideoFrameContainer> >::size_type i = 0; i < mVideoOutputs.Length(); ++i) {
       VideoFrameContainer* output = mVideoOutputs[i];
       output->SetCurrentFrame(aIntrinsicSize, aImage, now);
     }
 
     ++mInvalidatePending;
   }
 
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &CameraPreviewMediaStream::Invalidate);
-  NS_DispatchToMainThread(event);
+  NS_DispatchToMainThread(NewRunnableMethod(this, &CameraPreviewMediaStream::Invalidate));
 }
 
 void
 CameraPreviewMediaStream::ClearCurrentFrame()
 {
   MutexAutoLock lock(mMutex);
 
   for (nsTArray<RefPtr<VideoFrameContainer> >::size_type i = 0; i < mVideoOutputs.Length(); ++i) {
     VideoFrameContainer* output = mVideoOutputs[i];
     output->ClearCurrentFrame();
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(output, &VideoFrameContainer::Invalidate);
-    NS_DispatchToMainThread(event);
+    NS_DispatchToMainThread(NewRunnableMethod(output, &VideoFrameContainer::Invalidate));
   }
 }
 
 } // namespace mozilla
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -68,20 +68,18 @@ public:
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 StreamTime aTrackOffset, uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia,
                                 MediaStream* aInputStream,
                                 TrackID aInputTrackID) override
   {
     if (aTrackEvents & TRACK_EVENT_CREATED) {
-      nsCOMPtr<nsIRunnable> runnable =
-        NS_NewRunnableMethodWithArgs<TrackID>(
-          this, &TrackCreatedListener::DoNotifyTrackCreated, aID);
-      aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod<TrackID>(
+          this, &TrackCreatedListener::DoNotifyTrackCreated, aID));
     }
   }
 
 protected:
   ~TrackCreatedListener() {}
 
   nsDOMCameraControl* mCameraControl;
 };
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -228,21 +228,21 @@ CameraPermissionRequest::GetRequester(ns
   return NS_OK;
 }
 
 nsresult
 CameraPermissionRequest::DispatchCallback(uint32_t aPermission)
 {
   nsCOMPtr<nsIRunnable> callbackRunnable;
   if (aPermission == nsIPermissionManager::ALLOW_ACTION) {
-    callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallAllow);
+    callbackRunnable = NewRunnableMethod(this, &CameraPermissionRequest::CallAllow);
   } else {
-    callbackRunnable = NS_NewRunnableMethod(this, &CameraPermissionRequest::CallCancel);
+    callbackRunnable = NewRunnableMethod(this, &CameraPermissionRequest::CallCancel);
   }
-  return NS_DispatchToMainThread(callbackRunnable);
+  return NS_DispatchToMainThread(callbackRunnable.forget());
 }
 
 void
 CameraPermissionRequest::CallAllow()
 {
   mCameraManager->PermissionAllowed(mCameraId, mInitialConfig, mPromise);
 }
 
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -3170,15 +3170,15 @@ skip-if = 1
 [generated/test_conformance__typedarrays__data-view-test.html]
 [generated/test_conformance__typedarrays__typed-arrays-in-workers.html]
 [generated/test_conformance__uniforms__gl-uniform-arrays.html]
 [generated/test_conformance__uniforms__gl-uniform-bool.html]
 [generated/test_conformance__uniforms__gl-uniformmatrix4fv.html]
 [generated/test_conformance__uniforms__gl-unknown-uniform.html]
 [generated/test_conformance__uniforms__null-uniform-location.html]
 [generated/test_conformance__uniforms__out-of-bounds-uniform-array-access.html]
-skip-if = (os == 'android')
+skip-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
 [generated/test_conformance__uniforms__uniform-default-values.html]
 skip-if = (os == 'android') || (os == 'linux') || (os == 'win')
 [generated/test_conformance__uniforms__uniform-location.html]
 [generated/test_conformance__uniforms__uniform-samplers-test.html]
 [generated/test_conformance__uniforms__uniform-values-per-program.html]
 skip-if = (os == 'win' && (os_version == '6.2' || os_version == '6.3'))
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -246,17 +246,17 @@ skip-if = (os == 'android')
 [generated/test_conformance__rendering__many-draw-calls.html]
 # Crashes
 skip-if = (os == 'android')
 [generated/test_conformance__textures__tex-image-webgl.html]
 # Crashes
 skip-if = (os == 'android')
 [generated/test_conformance__uniforms__out-of-bounds-uniform-array-access.html]
 # Crashes
-skip-if = (os == 'android')
+skip-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
 [generated/test_conformance__glsl__samplers__glsl-function-texture2dproj.html]
 # Crashes
 skip-if = (os == 'android')
 [generated/test_conformance__rendering__framebuffer-switch.html]
 # Crashes
 skip-if = (os == 'android')
 [generated/test_conformance__glsl__bugs__modulo-arithmetic-accuracy.html]
 # Crashes
--- a/dom/devicestorage/DeviceStorageStatics.cpp
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -483,17 +483,17 @@ DeviceStorageStatics::AddListener(nsDOMD
   StaticMutexAutoLock lock(sMutex);
   if (NS_WARN_IF(!sInstance)) {
     return;
   }
 
   MOZ_ASSERT(sInstance->mInitialized);
   if (sInstance->mListeners.IsEmpty()) {
     NS_DispatchToMainThread(
-      NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register));
+      NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register));
   }
 
   RefPtr<ListenerWrapper> wrapper =
     new ListenerWrapper(aListener);
   sInstance->mListeners.AppendElement(wrapper.forget());
 }
 
 /* static */ void
@@ -514,17 +514,17 @@ DeviceStorageStatics::RemoveListener(nsD
       sInstance->mListeners.RemoveElementAt(i);
       removed = true;
       break;
     }
   }
 
   if (removed && sInstance->mListeners.IsEmpty()) {
     NS_DispatchToMainThread(
-      NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister));
+      NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister));
   }
 }
 
 void
 DeviceStorageStatics::Register()
 {
   MOZ_ASSERT(NS_IsMainThread());
   DS_LOG_INFO("");
deleted file mode 100644
--- a/dom/engineeringmode/EngineeringMode.manifest
+++ /dev/null
@@ -1,6 +0,0 @@
-component {27e55b94-fc43-42b3-b0f0-28bebdd804f1} EngineeringModeAPI.js
-contract @mozilla.org/dom/engineering-mode-api;1 {27e55b94-fc43-42b3-b0f0-28bebdd804f1}
-
-component {1345a96a-7b8d-4017-a776-07d918f14d84} EngineeringModeService.js
-contract @mozilla.org/engineering-mode-service;1 {1345a96a-7b8d-4017-a776-07d918f14d84}
-category profile-after-change EngineeringModeService @mozilla.org/engineering-mode-service;1
deleted file mode 100644
--- a/dom/engineeringmode/EngineeringModeAPI.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const DEBUG = false;
-function debug(s) {
-  if (DEBUG) dump("-*- EngineeringModeAPI: " + s + "\n");
-}
-
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
-function EngineeringModeAPI() {
-}
-
-EngineeringModeAPI.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  classDescription: "Engineering Mode API",
-  classID: Components.ID("{27e55b94-fc43-42b3-b0f0-28bebdd804f1}"),
-  contractID: "@mozilla.org/dom/engineering-mode-api;1",
-
-  // For DOMRequestHelper: must have nsISupportsWeakReference and nsIObserver.
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
-                                         Ci.nsISupportsWeakReference,
-                                         Ci.nsIObserver]),
-
-  init: function(aWindow) {
-    this.initDOMRequestHelper(aWindow, ["EngineeringMode:OnMessage",
-                                        "EngineeringMode:SetValue:Result:OK",
-                                        "EngineeringMode:SetValue:Result:KO",
-                                        "EngineeringMode:GetValue:Result:OK",
-                                        "EngineeringMode:GetValue:Result:KO"]);
-    cpmm.sendAsyncMessage("EngineeringMode:Register", null);
-  },
-
-  uninit: function() {
-    cpmm.sendAsyncMessage("EngineeringMode:Unregister", null);
-  },
-
-  // This returns a Promise<DOMString>
-  getValue: function getValue(aName) {
-    debug("getValue " + aName);
-    let promiseInit = function(aResolverId) {
-      debug("promise init called for getValue " + aName + " has resolverId " + aResolverId);
-      cpmm.sendAsyncMessage("EngineeringMode:GetValue", {
-        requestId: aResolverId,
-        name: aName
-      });
-    }.bind(this);
-
-    return this.createPromiseWithId(promiseInit);
-  },
-
-  // This returns a Promise<void>
-  setValue: function setValue(aName, aValue) {
-    debug("setValue " + aName + ' as ' + aValue );
-    let promiseInit = function(aResolverId) {
-      debug("promise init called for getValue " + aName + " has resolverId " + aResolverId);
-      cpmm.sendAsyncMessage("EngineeringMode:SetValue", {
-        requestId: aResolverId,
-        name: aName,
-        value: aValue
-      });
-    }.bind(this);
-
-    return this.createPromiseWithId(promiseInit);
-  },
-
-  set onmessage(aHandler) {
-    this.__DOM_IMPL__.setEventHandler("onmessage", aHandler);
-  },
-
-  get onmessage() {
-    return this.__DOM_IMPL__.getEventHandler("onmessage");
-  },
-
-  receiveMessage: function(aMessage) {
-    debug("receiveMessage: name: " + aMessage.name);
-    let resolver = null;
-    let data = aMessage.data;
-
-    switch (aMessage.name) {
-      case "EngineeringMode:OnMessage":
-        let detail = Cu.cloneInto(data, this._window);
-        let event = new this._window.CustomEvent("message", {"detail": detail});
-        this.__DOM_IMPL__.dispatchEvent(event);
-        break;
-      case "EngineeringMode:GetValue:Result:OK":
-      case "EngineeringMode:GetValue:Result:KO":
-        resolver = this.takePromiseResolver(data.requestId);
-        if (!resolver) {
-          return;
-        }
-        if (aMessage.name === "EngineeringMode:GetValue:Result:OK") {
-          resolver.resolve(data.value);
-        } else {
-          resolver.reject(data.reason);
-        }
-        break;
-      case "EngineeringMode:SetValue:Result:OK":
-      case "EngineeringMode:SetValue:Result:KO":
-        resolver = this.takePromiseResolver(data.requestId);
-        if (!resolver) {
-          return;
-        }
-        if (aMessage.name === "EngineeringMode:SetValue:Result:OK") {
-          resolver.resolve();
-        } else {
-          resolver.reject(data.reason);
-        }
-        break;
-    }
-  }
-
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EngineeringModeAPI]);
deleted file mode 100644
--- a/dom/engineeringmode/EngineeringModeService.js
+++ /dev/null
@@ -1,163 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const DEBUG = false;
-function debug(s) {
-  if (DEBUG) dump("-*- EngineeringModeService: " + s + "\n");
-}
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-XPCOMUtils.defineLazyServiceGetter(this, "EngineeringMode",
-                                   "@mozilla.org/b2g/engineering-mode-impl;1",
-                                   "nsIEngineeringMode");
-
-function EngineeringModeService() {
-}
-
-EngineeringModeService.prototype = {
-  classID: Components.ID("{1345a96a-7b8d-4017-a776-07d918f14d84}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
-                                         Ci.nsIEngineeringModeMessageHandler]),
-
-  observe: function(aSubject, aTopic, aData) {
-    debug("-- init");
-
-    switch(aTopic) {
-      case "profile-after-change":
-        Services.obs.addObserver(this, "xpcom-shutdown", false);
-        ppmm.addMessageListener("EngineeringMode:Register", this);
-        ppmm.addMessageListener("EngineeringMode:Unregister", this);
-        ppmm.addMessageListener("EngineeringMode:SetValue", this);
-        ppmm.addMessageListener("EngineeringMode:GetValue", this);
-        this._clients = [];
-        break;
-      case "xpcom-shutdown":
-        Services.obs.removeObserver(this, "xpcom-shutdown");
-        ppmm.removeMessageListener("EngineeringMode:Register", this);
-        ppmm.removeMessageListener("EngineeringMode:Unregister", this);
-        ppmm.removeMessageListener("EngineeringMode:SetValue", this);
-        ppmm.removeMessageListener("EngineeringMode:GetValue", this);
-        if (this._hasEngineeringModeImpl()) {
-          EngineeringMode.setMessageHandler(function(){});
-        }
-        this._clients = null;
-        break;
-    }
-  },
-
-  receiveMessage: function(aMessage) {
-    debug("receiveMessage: name: " + aMessage.name);
-
-    if (!aMessage.target.assertPermission("engineering-mode")) {
-      debug(aMessage.name + " from a content process with no 'engineering-mode' privileges.");
-      return;
-    }
-
-    switch (aMessage.name) {
-      case "EngineeringMode:Register":
-        // Lazy bind message handler until we have first client.
-        if (this._hasEngineeringModeImpl() && this._clients.length === 0) {
-          EngineeringMode.setMessageHandler(this.onMessage.bind(this));
-        }
-
-        this._clients.push(aMessage.target);
-        break;
-
-      case "EngineeringMode:Unregister":
-        let index = this._clients.indexOf(aMessage.target);
-        if (index > -1) {
-          this._clients.splice(index, 1);
-        }
-        break;
-
-      case "EngineeringMode:SetValue":
-        this.setValue(aMessage.target, aMessage.data);
-        break;
-
-      case "EngineeringMode:GetValue":
-        this.getValue(aMessage.target, aMessage.data);
-        break;
-    }
-  },
-
-  setValue: function(aTarget, aData) {
-    if (!this._hasEngineeringModeImpl()) {
-      aTarget.sendAsyncMessage("EngineeringMode:SetValue:Result:KO", {
-        requestId: aData.requestId,
-        reason: "No engineering mode implementation"
-      });
-      return;
-    }
-
-    EngineeringMode.setValue(aData.name, aData.value, {
-      onsuccess: function() {
-        aTarget.sendAsyncMessage("EngineeringMode:SetValue:Result:OK", {
-          requestId: aData.requestId
-        });
-      },
-      onerror: function(aError) {
-        aTarget.sendAsyncMessage("EngineeringMode:SetValue:Result:KO", {
-          requestId: aData.requestId,
-          reason: "Error: code " + aError
-        });
-      }
-    });
-  },
-
-  getValue: function(aTarget, aData) {
-    if (!this._hasEngineeringModeImpl()) {
-      aTarget.sendAsyncMessage("EngineeringMode:GetValue:Result:KO", {
-        requestId: aData.requestId,
-        reason: "No engineering mode implementation"
-      });
-      return;
-    }
-
-    EngineeringMode.getValue(aData.name, {
-      onsuccess: function(aValue) {
-        aTarget.sendAsyncMessage("EngineeringMode:GetValue:Result:OK", {
-          requestId: aData.requestId,
-          value: aValue
-        });
-      },
-      onerror: function(aError) {
-        aTarget.sendAsyncMessage("EngineeringMode:GetValue:Result:KO", {
-          requestId: aData.requestId,
-          reason: "Error: code " + aError
-        });
-      }
-    });
-  },
-
-  onMessage: function(aMessage) {
-    this._clients.forEach(function(aClient) {
-      aClient.sendAsyncMessage("EngineeringMode:OnMessage", {
-        data: aMessage
-      });
-    });
-  },
-
-  _hasEngineeringModeImpl: function() {
-    if (typeof Cc["@mozilla.org/b2g/engineering-mode-impl;1"] === "undefined") {
-      debug("Can not get engineering mode implementation.");
-      return false;
-    }
-    return true;
-  }
-
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([EngineeringModeService]);
deleted file mode 100644
--- a/dom/engineeringmode/moz.build
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-if CONFIG['MOZ_B2G']:
-    EXTRA_COMPONENTS += [
-        'EngineeringMode.manifest',
-        'EngineeringModeAPI.js',
-        'EngineeringModeService.js',
-    ]
-
-    XPIDL_SOURCES += [
-        'nsIEngineeringMode.idl',
-    ]
-
-    XPIDL_MODULE = 'dom_engineeringmode'
-
deleted file mode 100644
--- a/dom/engineeringmode/nsIEngineeringMode.idl
+++ /dev/null
@@ -1,29 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-[scriptable, function, uuid(82e7c515-d174-4e84-9091-e7e89617a6d9)]
-interface nsIEngineeringModeMessageHandler : nsISupports
-{
-  void handleMessage(in DOMString message);
-};
-
-[scriptable, uuid(fdae21b9-bd8c-4d01-bc6a-6c3a7b5efb27)]
-interface nsIEngineeringModeCallback : nsISupports
-{
-  void onsuccess([optional] in DOMString value);
-  void onerror(in int32_t error);
-};
-
-// Implemented by contract id @mozilla.org/b2g/engineering-mode-impl;1
-[scriptable, uuid(7251c99b-225f-4e39-8336-a7e2a087aa21)]
-interface nsIEngineeringMode : nsISupports
-{
-  void getValue(in DOMString name, in nsIEngineeringModeCallback callback);
-  void setValue(in DOMString name, in DOMString value,
-                in nsIEngineeringModeCallback callback);
-  void setMessageHandler(in nsIEngineeringModeMessageHandler handler);
-};
-
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -280,16 +280,18 @@ EventListenerManager::AddEventListenerIn
   listener->mListener = aListenerHolder;
   listener->mEventMessage = aEventMessage;
   listener->mTypeString = aTypeString;
   listener->mTypeAtom = aTypeAtom;
   listener->mFlags = aFlags;
   listener->mListenerIsHandler = aHandler;
   listener->mHandlerIsString = false;
   listener->mAllEvents = aAllEvents;
+  listener->mIsChrome = mIsMainThreadELM &&
+    nsContentUtils::LegacyIsCallerChromeOrNativeCode();
 
   // Detect the type of event listener.
   nsCOMPtr<nsIXPConnectWrappedJS> wjs;
   if (aFlags.mListenerIsJSListener) {
     MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
     listener->mListenerType = Listener::eJSEventListener;
   } else if (aListenerHolder.HasWebIDLCallback()) {
     listener->mListenerType = Listener::eWebIDLListener;
@@ -674,16 +676,25 @@ EventListenerManager::ListenerCanHandle(
     return true;
   }
   if (aEvent->mMessage == eUnidentifiedEvent) {
     if (mIsMainThreadELM) {
       return aListener->mTypeAtom == aEvent->mSpecifiedEventType;
     }
     return aListener->mTypeString.Equals(aEvent->mSpecifiedEventTypeString);
   }
+  if (!nsContentUtils::IsUnprefixedFullscreenApiEnabled() &&
+      aEvent->IsTrusted() && (aEventMessage == eFullscreenChange ||
+                              aEventMessage == eFullscreenError)) {
+    // If unprefixed Fullscreen API is not enabled, don't dispatch it
+    // to the content.
+    if (!aEvent->mFlags.mInSystemGroup && !aListener->mIsChrome) {
+      return false;
+    }
+  }
   MOZ_ASSERT(mIsMainThreadELM);
   return aListener->mEventMessage == aEventMessage;
 }
 
 void
 EventListenerManager::AddEventListenerByType(
                         const EventListenerHolder& aListenerHolder,
                         const nsAString& aType,
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -190,16 +190,17 @@ public:
       eWebIDLListener,
       eListenerTypeCount
     };
     uint8_t mListenerType;
 
     bool mListenerIsHandler : 1;
     bool mHandlerIsString : 1;
     bool mAllEvents : 1;
+    bool mIsChrome : 1;
 
     EventListenerFlags mFlags;
 
     JSEventHandler* GetJSEventHandler() const
     {
       return (mListenerType == eJSEventListener) ?
         static_cast<JSEventHandler*>(mListener.GetXPCOMCallback()) :
         nullptr;
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -366,19 +366,18 @@ EventListenerService::NotifyAboutMainThr
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mChangeListeners.IsEmpty()) {
     return;
   }
 
   if (!mPendingListenerChanges) {
     mPendingListenerChanges = nsArrayBase::Create();
-    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this,
-      &EventListenerService::NotifyPendingChanges);
-    NS_DispatchToCurrentThread(runnable);
+    NS_DispatchToCurrentThread(NewRunnableMethod(this,
+                                                 &EventListenerService::NotifyPendingChanges));
   }
 
   RefPtr<EventListenerChange> changes = mPendingListenerChangesSet.Get(aTarget);
   if (!changes) {
     changes = new EventListenerChange(aTarget);
     mPendingListenerChanges->AppendElement(changes, false);
     mPendingListenerChangesSet.Put(aTarget, changes);
   }
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -78,19 +78,17 @@ FetchDriver::Fetch(FetchDriverObserver* 
   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
                         mRequest->WasCreatedByFetchEvent());
 
   // FIXME(nsm): Deal with HSTS.
 
   MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
                      "Synchronous fetch not supported");
 
-  nsCOMPtr<nsIRunnable> r =
-    NS_NewRunnableMethod(this, &FetchDriver::ContinueFetch);
-  return NS_DispatchToCurrentThread(r);
+  return NS_DispatchToCurrentThread(NewRunnableMethod(this, &FetchDriver::ContinueFetch));
 }
 
 nsresult
 FetchDriver::ContinueFetch()
 {
   workers::AssertIsOnMainThread();
 
   nsresult rv = HttpFetch();
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -447,29 +447,41 @@ FMRadio::DisableRDS()
   return r.forget();
 }
 
 void
 FMRadio::EnableAudioChannelAgent()
 {
   NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
 
-  float volume = 0.0;
-  bool muted = true;
-  mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
-  WindowVolumeChanged(volume, muted);
+  AudioPlaybackConfig config;
+  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  WindowVolumeChanged(config.mVolume, config.mMuted);
+  WindowSuspendChanged(config.mSuspend);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
+  // TODO : Not support to change volume now, so we just close it.
   IFMRadioService::Singleton()->EnableAudio(!aMuted);
-  // TODO: what about the volume?
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FMRadio::WindowSuspendChanged(nsSuspendedTypes aSuspend)
+{
+  bool enable = (aSuspend == nsISuspendedTypes::NONE_SUSPENDED);
+  IFMRadioService::Singleton()->EnableAudio(enable);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowAudioCaptureChanged(bool aCapture)
 {
   return NS_OK;
 }
--- a/dom/html/HTMLAnchorElement.cpp
+++ b/dom/html/HTMLAnchorElement.cpp
@@ -34,16 +34,22 @@ enum {
   // Indicates that a DNS Prefetch was added to the deferral queue
   HTML_ANCHOR_DNS_PREFETCH_DEFERRED =     ANCHOR_ELEMENT_FLAG_BIT(1)
 };
 
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
 
 #undef ANCHOR_ELEMENT_FLAG_BIT
 
+// static
+const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
+  "noreferrer",
+  nullptr
+};
+
 HTMLAnchorElement::~HTMLAnchorElement()
 {
 }
 
 bool
 HTMLAnchorElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::href) ||
@@ -297,17 +303,17 @@ HTMLAnchorElement::SetTarget(const nsASt
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::target, aValue, true);
 }
 
 nsDOMTokenList*
 HTMLAnchorElement::RelList()
 {
   if (!mRelList) {
-    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel);
+    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, sSupportedRelValues);
   }
   return mRelList;
 }
 
 #define IMPL_URI_PART(_part)                                 \
   NS_IMETHODIMP                                              \
   HTMLAnchorElement::Get##_part(nsAString& a##_part)         \
   {                                                          \
--- a/dom/html/HTMLAnchorElement.h
+++ b/dom/html/HTMLAnchorElement.h
@@ -222,16 +222,18 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::shape, aValue, rv);
   }
   void Stringify(nsAString& aResult)
   {
     GetHref(aResult);
   }
 
+  static DOMTokenListSupportedToken sSupportedRelValues[];
+
 protected:
   virtual ~HTMLAnchorElement();
 
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
   RefPtr<nsDOMTokenList > mRelList;
 };
--- a/dom/html/HTMLAreaElement.cpp
+++ b/dom/html/HTMLAreaElement.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=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 "mozilla/dom/HTMLAreaElement.h"
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/HTMLAreaElementBinding.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Area)
 
 namespace mozilla {
@@ -117,17 +118,18 @@ HTMLAreaElement::GetLinkTarget(nsAString
     GetBaseTarget(aTarget);
   }
 }
 
 nsDOMTokenList* 
 HTMLAreaElement::RelList()
 {
   if (!mRelList) {
-    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel);
+    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel,
+                                  HTMLAnchorElement::sSupportedRelValues);
   }
   return mRelList;
 }
 
 nsresult
 HTMLAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -218,17 +218,17 @@ HTMLCanvasPrintState::Done()
 {
   if (!mPendingNotify && !mIsDone) {
     // The canvas needs to be invalidated for printing reftests on linux to
     // work.
     if (mCanvas) {
       mCanvas->InvalidateCanvas();
     }
     RefPtr<nsRunnableMethod<HTMLCanvasPrintState> > doneEvent =
-      NS_NewRunnableMethod(this, &HTMLCanvasPrintState::NotifyDone);
+      NewRunnableMethod(this, &HTMLCanvasPrintState::NotifyDone);
     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) {
       mPendingNotify = true;
     }
   }
 }
 
 void
 HTMLCanvasPrintState::NotifyDone()
@@ -498,17 +498,17 @@ HTMLCanvasElement::DispatchPrintCallback
     nsresult rv;
     nsCOMPtr<nsISupports> context;
     rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
     NS_ENSURE_SUCCESS(rv, rv);
   }
   mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback);
 
   RefPtr<nsRunnableMethod<HTMLCanvasElement> > renderEvent =
-        NS_NewRunnableMethod(this, &HTMLCanvasElement::CallPrintCallback);
+        NewRunnableMethod(this, &HTMLCanvasElement::CallPrintCallback);
   return NS_DispatchToCurrentThread(renderEvent);
 }
 
 void
 HTMLCanvasElement::CallPrintCallback()
 {
   ErrorResult rv;
   GetMozPrintCallback()->Call(*mPrintState, rv);
--- a/dom/html/HTMLIFrameElement.cpp
+++ b/dom/html/HTMLIFrameElement.cpp
@@ -13,16 +13,24 @@
 #include "nsStyleConsts.h"
 #include "nsContentUtils.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame)
 
 namespace mozilla {
 namespace dom {
 
+// static
+const DOMTokenListSupportedToken HTMLIFrameElement::sSupportedSandboxTokens[] = {
+#define SANDBOX_KEYWORD(string, atom, flags) string,
+#include "IframeSandboxKeywordList.h"
+#undef SANDBOX_KEYWORD
+  nullptr
+};
+
 HTMLIFrameElement::HTMLIFrameElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                      FromParser aFromParser)
   : nsGenericHTMLFrameElement(aNodeInfo, aFromParser)
 {
 }
 
 HTMLIFrameElement::~HTMLIFrameElement()
 {
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -81,17 +81,17 @@ public:
     GetHTMLAttr(nsGkAtoms::name, aName);
   }
   void SetName(const nsAString& aName, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aError);
   }
   nsDOMTokenList* Sandbox()
   {
-    return GetTokenList(nsGkAtoms::sandbox);
+    return GetTokenList(nsGkAtoms::sandbox, sSupportedSandboxTokens);
   }
   bool AllowFullscreen() const
   {
     return GetBoolAttr(nsGkAtoms::allowfullscreen);
   }
   void SetAllowFullscreen(bool aAllow, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::allowfullscreen, aAllow, aError);
@@ -196,14 +196,16 @@ protected:
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     nsRuleData* aData);
+
+  static const DOMTokenListSupportedToken sSupportedSandboxTokens[];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -588,19 +588,20 @@ HTMLImageElement::BindToTree(nsIDocument
   }
 
   if (HaveSrcsetOrInPicture()) {
     if (aDocument && !mInDocResponsiveContent) {
       aDocument->AddResponsiveContent(this);
       mInDocResponsiveContent = true;
     }
 
-    bool forceLoadEvent = HTMLPictureElement::IsPictureEnabled() &&
-      aParent && aParent->IsHTMLElement(nsGkAtoms::picture);
-    QueueImageLoadTask(forceLoadEvent);
+    // Run selection algorithm when an img element is inserted into a document
+    // in order to react to changes in the environment. See note of
+    // https://html.spec.whatwg.org/multipage/embedded-content.html#img-environment-changes
+    QueueImageLoadTask(false);
   } else if (!InResponsiveMode() &&
              HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
     // We skip loading when our attributes were set from parser land,
     // so trigger a aForce=false load now to check if things changed.
     // This isn't necessary for responsive mode, since creating the
     // image load task is asynchronous we don't need to take special
     // care to avoid doing so when being filled by the parser.
 
@@ -613,17 +614,17 @@ HTMLImageElement::BindToTree(nsIDocument
     // 1076583), but still need to delay if it is unsafe to run
     // script.
 
     // If loading is temporarily disabled, don't even launch MaybeLoadImage.
     // Otherwise MaybeLoadImage may run later when someone has reenabled
     // loading.
     if (LoadingEnabled()) {
       nsContentUtils::AddScriptRunner(
-          NS_NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
+          NewRunnableMethod(this, &HTMLImageElement::MaybeLoadImage));
     }
   }
 
   return rv;
 }
 
 void
 HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
@@ -640,26 +641,16 @@ HTMLImageElement::UnbindFromTree(bool aD
     nsIDocument* doc = GetOurOwnerDoc();
     MOZ_ASSERT(doc);
     if (doc) {
       doc->RemoveResponsiveContent(this);
       mInDocResponsiveContent = false;
     }
   }
 
-  if (GetParent() &&
-      GetParent()->IsHTMLElement(nsGkAtoms::picture) &&
-      HTMLPictureElement::IsPictureEnabled()) {
-    // Being removed from picture re-triggers selection, even if we
-    // weren't using a <source> peer
-    if (aNullParent) {
-      QueueImageLoadTask(true);
-    }
-  }
-
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
 HTMLImageElement::UpdateFormOwner()
 {
   if (!mForm) {
@@ -820,17 +811,17 @@ HTMLImageElement::CopyInnerTo(Element* a
   if (!destIsStatic) {
     // In SetAttr (called from nsGenericHTMLElement::CopyInnerTo), dest skipped
     // doing the image load because we passed in false for aNotify.  But we
     // really do want it to do the load, so set it up to happen once the cloning
     // reaches a stable state.
     if (!dest->InResponsiveMode() &&
         dest->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
       nsContentUtils::AddScriptRunner(
-        NS_NewRunnableMethod(dest, &HTMLImageElement::MaybeLoadImage));
+        NewRunnableMethod(dest, &HTMLImageElement::MaybeLoadImage));
     }
   }
 
   return NS_OK;
 }
 
 CORSMode
 HTMLImageElement::GetCORSMode()
@@ -1064,26 +1055,34 @@ HTMLImageElement::PictureSourceMediaOrTy
 
 void
 HTMLImageElement::PictureSourceAdded(nsIContent *aSourceNode)
 {
   if (!HTMLPictureElement::IsPictureEnabled()) {
     return;
   }
 
+  MOZ_ASSERT(aSourceNode == this ||
+             IsPreviousSibling(aSourceNode, this),
+             "Should not be getting notifications for non-previous-siblings");
+
   QueueImageLoadTask(true);
 }
 
 void
 HTMLImageElement::PictureSourceRemoved(nsIContent *aSourceNode)
 {
   if (!HTMLPictureElement::IsPictureEnabled()) {
     return;
   }
 
+  MOZ_ASSERT(aSourceNode == this ||
+             IsPreviousSibling(aSourceNode, this),
+             "Should not be getting notifications for non-previous-siblings");
+
   QueueImageLoadTask(true);
 }
 
 bool
 HTMLImageElement::UpdateResponsiveSource()
 {
   bool hadSelector = !!mResponsiveSelector;
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4349,17 +4349,17 @@ HTMLInputElement::BindToTree(nsIDocument
     // Our base URI may have changed; claim that our URI changed, and the
     // nsImageLoadingContent will decide whether a new image load is warranted.
     if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
       // FIXME: Bug 660963 it would be nice if we could just have
       // ClearBrokenState update our state and do it fast...
       ClearBrokenState();
       RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
       nsContentUtils::AddScriptRunner(
-        NS_NewRunnableMethod(this, &HTMLInputElement::MaybeLoadImage));
+        NewRunnableMethod(this, &HTMLInputElement::MaybeLoadImage));
     }
   }
 
   // Add radio to document if we don't have a form already (if we do it's
   // already been added into that group)
   if (aDocument && !mForm && mType == NS_FORM_INPUT_RADIO) {
     AddedToRadioGroup();
   }
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -21,16 +21,17 @@
 #include "nsIDOMEvent.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsINode.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsIURL.h"
 #include "nsPIDOMWindow.h"
 #include "nsReadableUtils.h"
 #include "nsStyleConsts.h"
+#include "nsStyleLinkElement.h"
 #include "nsUnicharUtils.h"
 
 #define LINK_ELEMENT_FLAG_BIT(n_) \
   NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
 
 // Link element specific bits
 enum {
   // Indicates that a DNS Prefetch has been requested from this Link element.
@@ -179,20 +180,20 @@ HTMLLinkElement::BindToTree(nsIDocument*
     aDocument->RegisterPendingLinkUpdate(this);
   }
 
   if (IsInComposedDoc()) {
     TryDNSPrefetchPreconnectOrPrefetch();
   }
 
   void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
-  nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, update));
+  nsContentUtils::AddScriptRunner(NewRunnableMethod(this, update));
 
   void (HTMLLinkElement::*updateImport)() = &HTMLLinkElement::UpdateImport;
-  nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, updateImport));
+  nsContentUtils::AddScriptRunner(NewRunnableMethod(this, updateImport));
 
   CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));
 
   return rv;
 }
 
 void
 HTMLLinkElement::LinkAdded()
@@ -453,21 +454,39 @@ void
 HTMLLinkElement::GetLinkTarget(nsAString& aTarget)
 {
   GetAttr(kNameSpaceID_None, nsGkAtoms::target, aTarget);
   if (aTarget.IsEmpty()) {
     GetBaseTarget(aTarget);
   }
 }
 
+static const DOMTokenListSupportedToken sSupportedRelValues[] = {
+  // Keep this in sync with ToLinkMask in nsStyleLinkElement.cpp.
+  // "import" must come first because it's conditional.
+  "import"
+  "prefetch",
+  "dns-prefetch",
+  "stylesheet",
+  "next",
+  "alternate",
+  "preconnect",
+  "icon",
+  nullptr
+};
+
 nsDOMTokenList* 
 HTMLLinkElement::RelList()
 {
   if (!mRelList) {
-    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel);
+    const DOMTokenListSupportedTokenArray relValues =
+      nsStyleLinkElement::IsImportEnabled() ?
+        sSupportedRelValues : &sSupportedRelValues[1];
+
+    mRelList = new nsDOMTokenList(this, nsGkAtoms::rel, relValues);
   }
   return mRelList;
 }
 
 already_AddRefed<nsIURI>
 HTMLLinkElement::GetHrefURI() const
 {
   return GetHrefURIForAnchors();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -837,29 +837,29 @@ void HTMLMediaElement::RunInStableState(
   nsCOMPtr<nsIRunnable> event = new nsSyncSection(this, aRunnable);
   nsContentUtils::RunInStableState(event.forget());
 }
 
 void HTMLMediaElement::QueueLoadFromSourceTask()
 {
   ChangeDelayLoadStatus(true);
   ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
-  RunInStableState(
-    NS_NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren));
+  RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLMediaElement::LoadFromSourceChildren);
+  RunInStableState(r);
 }
 
 void HTMLMediaElement::QueueSelectResourceTask()
 {
   // Don't allow multiple async select resource calls to be queued.
   if (mHaveQueuedSelectResource)
     return;
   mHaveQueuedSelectResource = true;
   ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
-  RunInStableState(
-    NS_NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper));
+  RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper);
+  RunInStableState(r);
 }
 
 NS_IMETHODIMP HTMLMediaElement::Load()
 {
   if (mIsRunningLoadMethod) {
     return NS_OK;
   }
 
@@ -1157,21 +1157,16 @@ void HTMLMediaElement::ResumeLoad(Preloa
   }
 }
 
 static bool IsAutoplayEnabled()
 {
   return Preferences::GetBool("media.autoplay.enabled");
 }
 
-static bool UseAudioChannelAPI()
-{
-  return Preferences::GetBool("media.useAudioChannelAPI");
-}
-
 void HTMLMediaElement::UpdatePreloadAction()
 {
   PreloadAction nextAction = PRELOAD_UNDEFINED;
   // If autoplay is set, or we're playing, we should always preload data,
   // as we'll need it to play.
   if ((IsAutoplayEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
       !mPaused)
   {
@@ -2239,16 +2234,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mPlayed(new TimeRanges(ToSupports(OwnerDoc()))),
     mCurrentPlayRangeStart(-1.0),
     mBegun(false),
     mLoadedDataFired(false),
     mAutoplaying(true),
     mAutoplayEnabled(true),
     mPaused(true),
     mMuted(0),
+    mAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED),
     mStatsShowing(false),
     mAllowCasting(false),
     mIsCasting(false),
     mAudioCaptured(false),
     mAudioCapturedByWindow(false),
     mPlayingBeforeSeek(false),
     mPlayingThroughTheAudioChannelBeforeSeek(false),
     mPausedForInactiveDocumentOrChannel(false),
@@ -2390,30 +2386,17 @@ HTMLMediaElement::Play(ErrorResult& aRv)
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
 }
 
 nsresult
 HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
 {
-  // Prevent media element from being auto-started by a script when
-  // media.autoplay.enabled=false
-  if (!mHasUserInteraction
-      && !IsAutoplayEnabled()
-      && !EventStateManager::IsHandlingUserInput()
-      && !aCallerIsChrome) {
-    LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
-#if defined(MOZ_WIDGET_ANDROID)
-    nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
-                                         static_cast<nsIContent*>(this),
-                                         NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
-                                         false,
-                                         false);
-#endif
+  if (!IsAllowedToPlay()) {
     return NS_OK;
   }
 
   // Play was not blocked so assume user interacted with the element.
   mHasUserInteraction = true;
 
   StopSuspendingAfterFirstFrame();
   SetPlayedOrSeeked(true);
@@ -2468,16 +2451,18 @@ HTMLMediaElement::PlayInternal(bool aCal
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
       DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
 
   mPaused = false;
   mAutoplaying = false;
+  SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   // and our preload status.
   AddRemoveSelfReference();
   UpdatePreloadAction();
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
   return NS_OK;
@@ -3181,47 +3166,47 @@ public:
   }
 
   // These notifications run on the media graph thread so we need to
   // dispatch events to the main thread.
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) override
   {
     nsCOMPtr<nsIRunnable> event;
     if (aBlocked == BLOCKED) {
-      event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
+      event = NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
-      event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
+      event = NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
   virtual void NotifyEvent(MediaStreamGraph* aGraph,
                            MediaStreamListener::MediaStreamGraphEvent event) override
   {
     if (event == EVENT_FINISHED) {
       nsCOMPtr<nsIRunnable> event =
-        NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
+        NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) override
   {
     MutexAutoLock lock(mMutex);
     nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
+      NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
   virtual void NotifyOutput(MediaStreamGraph* aGraph,
                             GraphTime aCurrentTime) override
   {
     MutexAutoLock lock(mMutex);
     if (mPendingNotifyOutput)
       return;
     mPendingNotifyOutput = true;
     nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &StreamListener::DoNotifyOutput);
+      NewRunnableMethod(this, &StreamListener::DoNotifyOutput);
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
 
 private:
   // These fields may only be accessed on the main thread
   HTMLMediaElement* mElement;
   bool mHaveCurrentData;
   bool mBlocked;
@@ -3263,17 +3248,17 @@ public:
     if (mInitialSizeFound || aQueuedMedia.GetType() != MediaSegment::VIDEO) {
       return;
     }
     const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
     for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
       if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
         mInitialSizeFound = true;
         nsCOMPtr<nsIRunnable> event =
-          NS_NewRunnableMethodWithArgs<gfx::IntSize>(
+          NewRunnableMethod<gfx::IntSize>(
               this, &StreamSizeListener::ReceivedSize,
               c->mFrame.GetIntrinsicSize());
         aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
         return;
       }
     }
   }
 
@@ -4533,22 +4518,17 @@ bool HTMLMediaElement::IsBeingDestroyed(
     docShell->IsBeingDestroyed(&isBeingDestroyed);
   }
   return isBeingDestroyed;
 }
 
 void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
 {
   bool pauseElement = NotifyOwnerDocumentActivityChangedInternal();
-  if (pauseElement && mAudioChannelAgent &&
-      // On B2G, NotifyOwnerDocumentActivityChangedInternal may return true for
-      // two reasons: the document no longer being active, or the element being
-      // paused by the audio channel.  However we are only interested in the
-      // first case here, so we need to filter out the second case.
-      (!UseAudioChannelAPI() || !ComputedMuted())) {
+  if (pauseElement && mAudioChannelAgent) {
     // If the element is being paused since we are navigating away from the
     // document, notify the audio channel agent.
     // Be careful to ignore this event during a docshell frame swap.
     auto docShell = static_cast<nsDocShell*>(OwnerDoc()->GetDocShell());
     if (!docShell) {
       return;
     }
     if (!docShell->InFrameSwap()) {
@@ -4562,24 +4542,16 @@ HTMLMediaElement::NotifyOwnerDocumentAct
 {
   nsIDocument* ownerDoc = OwnerDoc();
   if (mDecoder && !IsBeingDestroyed()) {
     mDecoder->SetElementVisibility(!ownerDoc->Hidden());
     mDecoder->NotifyOwnerActivityChanged();
   }
 
   bool pauseElement = !IsActive();
-  // Only pause the element when we start playing. If we pause without playing
-  // audio, the resource loading would be affected unexpectedly. For example,
-  // the media element is muted by default, but we don't want this behavior
-  // interrupting the loading process.
-  if (UseAudioChannelAPI() && mAudioChannelAgent) {
-    pauseElement |= ComputedMuted();
-  }
-
   SuspendOrResumeElement(pauseElement, !IsActive());
 
   if (!mPausedForInactiveDocumentOrChannel &&
       mPlayBlockedBecauseHidden &&
       !OwnerDoc()->Hidden()) {
     LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
     mPlayBlockedBecauseHidden = false;
     Play();
@@ -4617,17 +4589,17 @@ void HTMLMediaElement::AddRemoveSelfRefe
       // The observer service will hold a strong reference to us. This
       // will do to keep us alive. We need to know about shutdown so that
       // we can release our self-reference.
       nsContentUtils::RegisterShutdownObserver(this);
     } else {
       // Dispatch Release asynchronously so that we don't destroy this object
       // inside a call stack of method calls on this object
       nsCOMPtr<nsIRunnable> event =
-        NS_NewRunnableMethod(this, &HTMLMediaElement::DoRemoveSelfReference);
+        NewRunnableMethod(this, &HTMLMediaElement::DoRemoveSelfReference);
       NS_DispatchToMainThread(event);
     }
   }
 
   UpdateAudioChannelPlayingState();
 }
 
 void HTMLMediaElement::DoRemoveSelfReference()
@@ -4983,43 +4955,16 @@ NS_IMETHODIMP HTMLMediaElement::SetMozPr
 }
 
 ImageContainer* HTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
 
-nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted)
-{
-  if (mAudioChannelVolume != aVolume) {
-    mAudioChannelVolume = aVolume;
-    SetVolumeInternal();
-  }
-
-  // We have to mute this channel.
-  if (aMuted && !ComputedMuted()) {
-    SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
-    if (UseAudioChannelAPI()) {
-      DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
-    }
-  } else if (!aMuted && ComputedMuted()) {
-    SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
-    if (UseAudioChannelAPI()) {
-      DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
-    }
-  }
-
-  if (UseAudioChannelAPI()) {
-    SuspendOrResumeElement(ComputedMuted(), false);
-  }
-
-  return NS_OK;
-}
-
 bool
 HTMLMediaElement::MaybeCreateAudioChannelAgent()
 {
   if (!mAudioChannelAgent) {
     nsresult rv;
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return false;
@@ -5030,16 +4975,21 @@ HTMLMediaElement::MaybeCreateAudioChanne
                                              this);
   }
   return true;
 }
 
 bool
 HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
 {
+  // It might be resumed from remote, we should keep the audio channel agent.
+  if (IsSuspendedByAudioChannel()) {
+    return true;
+  }
+
   // Are we paused or muted
   if (mPaused || Muted()) {
     return false;
   }
 
   // If we have an error, we are not playing.
   if (mError) {
     return false;
@@ -5106,39 +5056,200 @@ void
 HTMLMediaElement::NotifyAudioChannelAgent(bool aPlaying)
 {
   // This is needed to pass nsContentUtils::IsCallerChrome().
   // AudioChannel API should not called from content but it can happen that
   // this method has some content JS in its stack.
   AutoNoJSAPI nojsapi;
 
   if (aPlaying) {
-    float volume = 0.0;
-    bool muted = true;
-    mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
-    WindowVolumeChanged(volume, muted);
+    AudioPlaybackConfig config;
+    nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    WindowVolumeChanged(config.mVolume, config.mMuted);
+    WindowSuspendChanged(config.mSuspend);
   } else {
     mAudioChannelAgent->NotifyStoppedPlaying();
     mAudioChannelAgent = nullptr;
   }
 }
 
-NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  UpdateChannelMuteState(aVolume, aMuted);
-
-  if (UseAudioChannelAPI()) {
-    mPaused.SetCanPlay(!aMuted);
+NS_IMETHODIMP
+HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("HTMLMediaElement, WindowVolumeChanged, this = %p, "
+          "aVolume = %f, aMuted = %d\n", this, aVolume, aMuted));
+
+  if (mAudioChannelVolume != aVolume) {
+    mAudioChannelVolume = aVolume;
+    SetVolumeInternal();
+  }
+
+  if (aMuted && !ComputedMuted()) {
+    SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
+  } else if (!aMuted && ComputedMuted()) {
+    SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HTMLMediaElement::WindowSuspendChanged(SuspendTypes aSuspend)
+{
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
+          "aSuspend = %d\n", this, aSuspend));
+
+  switch (aSuspend) {
+    case nsISuspendedTypes::NONE_SUSPENDED:
+      ResumeFromAudioChannel();
+      break;
+    case nsISuspendedTypes::SUSPENDED_PAUSE:
+    case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
+      PauseByAudioChannel(aSuspend);
+      break;
+    case nsISuspendedTypes::SUSPENDED_BLOCK:
+      BlockByAudioChannel();
+      break;
+    case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
+      Pause();
+      break;
+    default:
+      MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+             ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
+              "Error : unknown suspended type!\n", this));
   }
 
   return NS_OK;
 }
 
+void
+HTMLMediaElement::ResumeFromAudioChannel()
+{
+  if (!IsSuspendedByAudioChannel()) {
+    return;
+  }
+
+  switch (mAudioChannelSuspended) {
+    case nsISuspendedTypes::SUSPENDED_PAUSE:
+    case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
+      ResumeFromAudioChannelPaused(mAudioChannelSuspended);
+      break;
+    case nsISuspendedTypes::SUSPENDED_BLOCK:
+      ResumeFromAudioChannelBlocked();
+      break;
+    default:
+      MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+             ("HTMLMediaElement, ResumeFromAudioChannel, this = %p, "
+              "Error : resume without suspended!\n", this));
+  }
+}
+
+void
+HTMLMediaElement::ResumeFromAudioChannelPaused(SuspendTypes aSuspend)
+{
+  MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+             mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE);
+
+  SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+  nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
+}
+
+void
+HTMLMediaElement::ResumeFromAudioChannelBlocked()
+{
+  MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
+
+  SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+  mPaused.SetCanPlay(true);
+  SuspendOrResumeElement(false /* resume */, false);
+}
+
+void
+HTMLMediaElement::PauseByAudioChannel(SuspendTypes aSuspend)
+{
+  if (IsSuspendedByAudioChannel()) {
+    return;
+  }
+
+  SetAudioChannelSuspended(aSuspend);
+  Pause();
+  DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
+}
+
+void
+HTMLMediaElement::BlockByAudioChannel()
+{
+  if (IsSuspendedByAudioChannel()) {
+    return;
+  }
+
+  SetAudioChannelSuspended(nsISuspendedTypes::SUSPENDED_BLOCK);
+  SuspendOrResumeElement(true /* suspend */, false);
+  mPaused.SetCanPlay(false);
+}
+
+void
+HTMLMediaElement::SetAudioChannelSuspended(SuspendTypes aSuspend)
+{
+  if (mAudioChannelSuspended == aSuspend) {
+    return;
+  }
+
+  mAudioChannelSuspended = aSuspend;
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("HTMLMediaElement, SetAudioChannelSuspended, this = %p, "
+          "aSuspend = %d\n", this, aSuspend));
+}
+
+bool
+HTMLMediaElement::IsSuspendedByAudioChannel() const
+{
+  return (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+          mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+          mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
+}
+
+bool
+HTMLMediaElement::IsAllowedToPlay()
+{
+  // Prevent media element from being auto-started by a script when
+  // media.autoplay.enabled=false
+  if (!mHasUserInteraction &&
+      !IsAutoplayEnabled() &&
+      !EventStateManager::IsHandlingUserInput() &&
+      !nsContentUtils::IsCallerChrome()) {
+#if defined(MOZ_WIDGET_ANDROID)
+    nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                         static_cast<nsIContent*>(this),
+                                         NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
+                                         false,
+                                         false);
+#endif
+    return false;
+  }
+
+  // The MediaElement can't start playback until it's resumed by audio channel.
+  if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+      mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
+    return false;
+  }
+
+  return true;
+}
+
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
 bool
@@ -5470,16 +5581,22 @@ HTMLMediaElement::ComputedVolume() const
 }
 
 bool
 HTMLMediaElement::ComputedMuted() const
 {
   return (mMuted & MUTED_BY_AUDIO_CHANNEL);
 }
 
+nsSuspendedTypes
+HTMLMediaElement::ComputedSuspended() const
+{
+  return mAudioChannelSuspended;
+}
+
 bool
 HTMLMediaElement::IsCurrentlyPlaying() const
 {
   // We have playable data, but we still need to check whether data is "real"
   // current data.
   if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
       !IsPlaybackEnded()) {
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -32,16 +32,17 @@
 
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
+typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 class DecoderDoctorDiagnostics;
 class DOMMediaStream;
 class ErrorResult;
 class MediaResource;
 class MediaDecoder;
 class VideoFrameContainer;
@@ -713,19 +714,20 @@ public:
   // Returns true if the media element is being destroyed. Used in
   // dormancy checks to prevent dormant processing for an element
   // that will soon be gone.
   bool IsBeingDestroyed();
 
   IMPL_EVENT_HANDLER(mozinterruptbegin)
   IMPL_EVENT_HANDLER(mozinterruptend)
 
-  // This is for testing only
+  // These are used for testing only
   float ComputedVolume() const;
   bool ComputedMuted() const;
+  nsSuspendedTypes ComputedSuspended() const;
 
 protected:
   virtual ~HTMLMediaElement();
 
   class MediaLoadListener;
   class MediaStreamTracksAvailableCallback;
   class MediaStreamTrackListener;
   class StreamListener;
@@ -1109,19 +1111,16 @@ protected:
 #ifdef MOZ_EME
   void ReportEMETelemetry();
 #endif
   void ReportMSETelemetry();
 
   // Check the permissions for audiochannel.
   bool CheckAudioChannelPermissions(const nsAString& aType);
 
-  // This method does the check for muting/nmuting the audio channel.
-  nsresult UpdateChannelMuteState(float aVolume, bool aMuted);
-
   // Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the
   // seek target, or PrevSyncPoint if a quicker but less precise seek is
   // desired, and we'll seek to the sync point (keyframe and/or start of the
   // next block of audio samples) preceeding seek target.
   void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
 
   // A method to check if we are playing through the AudioChannel.
   bool IsPlayingThroughTheAudioChannel() const;
@@ -1146,16 +1145,48 @@ protected:
 
   // Notifies the audio channel agent when the element starts or stops playing.
   void NotifyAudioChannelAgent(bool aPlaying);
 
   // Creates the audio channel agent if needed.  Returns true if the audio
   // channel agent is ready to be used.
   bool MaybeCreateAudioChannelAgent();
 
+  /**
+   * We have different kinds of suspended cases,
+   * - SUSPENDED_PAUSE
+   * It's used when we temporary lost platform audio focus. MediaElement can
+   * only be resumed when we gain the audio focus again.
+   *
+   * - SUSPENDED_PAUSE_DISPOSABLE
+   * It's used when user press the pause botton on the remote media-control.
+   * MediaElement can be resumed by reomte media-control or via play().
+   *
+   * - SUSPENDED_BLOCK
+   * It's used to reduce the power comsuption, we won't play the auto-play
+   * audio/video in the page we have never visited before. MediaElement would
+   * be resumed when the page is active. See bug647429 for more details.
+   *
+   * - SUSPENDED_STOP_DISPOSABLE
+   * When we permanently lost platform audio focus, we shuold stop playing
+   * and stop the audio channel agent. MediaElement can only be restarted by
+   * play().
+   */
+  void PauseByAudioChannel(SuspendTypes aSuspend);
+  void BlockByAudioChannel();
+
+  void ResumeFromAudioChannel();
+  void ResumeFromAudioChannelPaused(SuspendTypes aSuspend);
+  void ResumeFromAudioChannelBlocked();
+
+  bool IsSuspendedByAudioChannel() const;
+  void SetAudioChannelSuspended(SuspendTypes aSuspend);
+
+  bool IsAllowedToPlay();
+
   class nsAsyncEventRunner;
   using nsGenericHTMLElement::DispatchEvent;
   // For nsAsyncEventRunner.
   nsresult DispatchEvent(const nsAString& aName);
 
   // The current decoder. Load() has been called on this decoder.
   // At most one of mDecoder and mSrcStream can be non-null.
   RefPtr<MediaDecoder> mDecoder;
@@ -1365,16 +1396,17 @@ protected:
   enum MutedReasons {
     MUTED_BY_CONTENT               = 0x01,
     MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
     MUTED_BY_AUDIO_CHANNEL         = 0x04,
     MUTED_BY_AUDIO_TRACK           = 0x08
   };
 
   uint32_t mMuted;
+  SuspendTypes mAudioChannelSuspended;
 
   // True if the media statistics are currently being shown by the builtin
   // video controls
   bool mStatsShowing;
 
   // The following two fields are here for the private storage of the builtin
   // video controls, and control 'casting' of the video to external devices
   // (TVs, projectors etc.)
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -275,17 +275,17 @@ HTMLObjectElement::BindToTree(nsIDocumen
   // Don't kick off load from being bound to a plugin document - the plugin
   // document will call nsObjectLoadingContent::InitializeFromChannel() for the
   // initial load.
   nsCOMPtr<nsIPluginDocument> pluginDoc = do_QueryInterface(aDocument);
 
   // If we already have all the children, start the load.
   if (mIsDoneAddingChildren && !pluginDoc) {
     void (HTMLObjectElement::*start)() = &HTMLObjectElement::StartObjectLoad;
-    nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, start));
+    nsContentUtils::AddScriptRunner(NewRunnableMethod(this, start));
   }
 
   return NS_OK;
 }
 
 void
 HTMLObjectElement::UnbindFromTree(bool aDeep,
                                   bool aNullParent)
--- a/dom/html/HTMLPictureElement.cpp
+++ b/dom/html/HTMLPictureElement.cpp
@@ -38,33 +38,66 @@ HTMLPictureElement::~HTMLPictureElement(
 NS_IMPL_ISUPPORTS_INHERITED(HTMLPictureElement, nsGenericHTMLElement,
                             nsIDOMHTMLPictureElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLPictureElement)
 
 void
 HTMLPictureElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
 {
-  // Find all img siblings after this <source> to notify them of its demise
   nsCOMPtr<nsIContent> child = GetChildAt(aIndex);
-  nsCOMPtr<nsIContent> nextSibling;
-  if (child && child->IsHTMLElement(nsGkAtoms::source)) {
-    nextSibling = child->GetNextSibling();
+
+  if (child && child->IsHTMLElement(nsGkAtoms::img)) {
+    HTMLImageElement* img = HTMLImageElement::FromContent(child);
+    if (img) {
+      img->PictureSourceRemoved(child->AsContent());
+    }
+  } else if (child && child->IsHTMLElement(nsGkAtoms::source)) {
+    // Find all img siblings after this <source> to notify them of its demise
+    nsCOMPtr<nsIContent> nextSibling = child->GetNextSibling();
+    if (nextSibling && nextSibling->GetParentNode() == this) {
+      do {
+        HTMLImageElement* img = HTMLImageElement::FromContent(nextSibling);
+        if (img) {
+          img->PictureSourceRemoved(child->AsContent());
+        }
+      } while ( (nextSibling = nextSibling->GetNextSibling()) );
+    }
   }
 
   nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify);
+}
 
-  if (nextSibling && nextSibling->GetParentNode() == this) {
-    do {
-      HTMLImageElement* img = HTMLImageElement::FromContent(nextSibling);
-      if (img) {
-        img->PictureSourceRemoved(child->AsContent());
-      }
-    } while ( (nextSibling = nextSibling->GetNextSibling()) );
+nsresult
+HTMLPictureElement::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify)
+{
+  nsresult rv = nsGenericHTMLElement::InsertChildAt(aKid, aIndex, aNotify);
+
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(aKid, rv);
+
+  if (aKid->IsHTMLElement(nsGkAtoms::img)) {
+    HTMLImageElement* img = HTMLImageElement::FromContent(aKid);
+    if (img) {
+      img->PictureSourceAdded(aKid->AsContent());
+    }
+  } else if (aKid->IsHTMLElement(nsGkAtoms::source)) {