Merge m-c to fx-team
authorWes Kocher <wkocher@mozilla.com>
Tue, 15 Oct 2013 20:32:13 -0700
changeset 164743 9988599193391944b60d3d2a600b2e3fc71d1b0f
parent 164742 63b7d4bbaab6aeafa152ffc7176f35e826595a51 (current diff)
parent 164653 9f63bbc005279333095be0000696dcf12629b16d (diff)
child 164744 c14ca6b27b3007a972bddc75fa42731b071a0466
child 164787 bdd94ab5687d16f225960dbe13159ed6eb12c341
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.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
dom/src/geolocation/nsGeolocation.cpp
testing/marionette/client/marionette/venv_mochitest.sh
testing/marionette/marionette-listener.js
testing/marionette/marionette-server.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -554,16 +554,17 @@ pref("device.storage.enabled", true);
 
 // Enable pre-installed applications
 pref("dom.webapps.useCurrentProfile", true);
 
 // Enable system message
 pref("dom.sysmsg.enabled", true);
 pref("media.plugins.enabled", false);
 pref("media.omx.enabled", true);
+pref("media.rtsp.enabled", true);
 
 // Disable printing (particularly, window.print())
 pref("dom.disable_window_print", true);
 
 // Disable window.showModalDialog
 pref("dom.disable_window_showModalDialog", true);
 
 // Enable new experimental html forms
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -559,19 +559,16 @@ var shell = {
       isActivity: (msg.type == 'activity'),
       onlyShowApp: msg.onlyShowApp,
       showApp: msg.showApp,
       target: msg.target,
       expectingSystemMessage: true,
       extra: msg.extra
     }
     this.sendCustomEvent('open-app', payload);
-    // XXX Remove once the gaia part of bug 924032 has landed.
-    payload.type = 'open-app';
-    this.sendChromeEvent(payload);
   },
 
   receiveMessage: function shell_receiveMessage(message) {
     var activities = { 'content-handler': { name: 'view', response: null },
                        'dial-handler':    { name: 'dial', response: null },
                        'mail-handler':    { name: 'new',  response: null },
                        'sms-handler':     { name: 'new',  response: null },
                        'file-picker':     { name: 'pick', response: 'file-picked' } };
@@ -964,39 +961,32 @@ var WebappsHelper = {
               url: "r",
               manifestURL: "r"
             },
             timestamp: json.timestamp,
             url: manifest.fullLaunchPath(json.startPoint),
             manifestURL: json.manifestURL
           }
           shell.sendEvent(getContentWindow(), "webapps-launch", payload);
-          // XXX Remove once the gaia side of bug 924032 lands.
-          payload.type = "webapps-launch";
-          delete payload.__exposedProps__;
-          shell.sendChromeEvent(payload);
         });
         break;
       case "webapps-ask-install":
         let id = this.registerInstaller(json);
         shell.sendChromeEvent({
           type: "webapps-ask-install",
           id: id,
           app: json.app
         });
         break;
       case "webapps-close":
         shell.sendEvent(shell.getContentWindow(), "webapps-close",
           {
             __exposedProps__: { "manifestURL": "r" },
             "manifestURL": json.manifestURL
           });
-        // XXX Remove once the gaia side of bug 924032 lands.
-        shell.sendChromeEvent({ type: "webapps-close",
-                                "manifestURL": json.manifestURL });
         break;
     }
   }
 }
 
 let IndexedDBPromptHelper = {
   _quotaPrompt: "indexedDB-quota-prompt",
   _quotaResponse: "indexedDB-quota-response",
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "8c2ad9ad3192687c13efe1eb0cd986d6214b4b5d", 
+    "revision": "e811ee9b89a2a4cbb789fb88e532113dc5105e2a", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -263,16 +263,19 @@
 @BINPATH@/components/necko_http.xpt
 @BINPATH@/components/necko_res.xpt
 @BINPATH@/components/necko_socket.xpt
 @BINPATH@/components/necko_strconv.xpt
 @BINPATH@/components/necko_viewsource.xpt
 @BINPATH@/components/necko_websocket.xpt
 @BINPATH@/components/necko_wifi.xpt
 @BINPATH@/components/necko_wyciwyg.xpt
+#ifdef MOZ_RTSP
+@BINPATH@/components/necko_rtsp.xpt
+#endif
 @BINPATH@/components/necko.xpt
 @BINPATH@/components/loginmgr.xpt
 @BINPATH@/components/parentalcontrols.xpt
 #ifdef MOZ_WEBRTC
 @BINPATH@/components/peerconnection.xpt
 #endif
 @BINPATH@/components/places.xpt
 @BINPATH@/components/plugin.xpt
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -308,17 +308,17 @@ var gPluginHandler = {
         break;
 
       case "PluginInstantiated":
       case "PluginRemoved":
         shouldShowNotification = true;
         break;
     }
 
-    // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
+    // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
     if (eventType != "PluginCrashed" && eventType != "PluginRemoved") {
       let overlay = this.getPluginUI(plugin, "main");
       if (overlay != null) {
         if (!this.isTooSmall(plugin, overlay))
           overlay.style.visibility = "visible";
 
         plugin.addEventListener("overflow", function(event) {
           overlay.style.visibility = "hidden";
@@ -984,22 +984,22 @@ var gPluginHandler = {
     // Is the <object>'s size too small to hold what we want to show?
     if (this.isTooSmall(plugin, overlay)) {
       // First try hiding the crash report submission UI.
       statusDiv.removeAttribute("status");
 
       if (this.isTooSmall(plugin, overlay)) {
         // Hide the overlay's contents. Use visibility style, so that it doesn't
         // collapse down to 0x0.
-        overlay.style.visibility = "hidden";
         isShowing = false;
       }
     }
 
     if (isShowing) {
+      overlay.style.visibility = "visible";
       // If a previous plugin on the page was too small and resulted in adding a
       // notification bar, then remove it because this plugin instance it big
       // enough to serve as in-content notification.
       hideNotificationBar();
       doc.mozNoPluginCrashedNotification = true;
     } else {
       // If another plugin on the page was large enough to show our UI, we don't
       // want to show a notification bar.
--- a/configure.in
+++ b/configure.in
@@ -207,16 +207,17 @@ if test -n "$gonkdir" ; then
 
     case "$ANDROID_VERSION" in
     15)
         GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
         MOZ_B2G_BT=1
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
         AC_SUBST(MOZ_OMX_DECODER)
+        MOZ_RTSP=1
         ;;
     18)
         GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include"
         if test -d "$gonkdir/external/bluetooth/bluez"; then
             GONK_INCLUDES+=" -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib"
             MOZ_B2G_BT=1
         fi
         MOZ_B2G_CAMERA=1
@@ -7251,16 +7252,24 @@ dnl ====================================
 dnl = Enable Radio FM for B2G (Gonk usually)
 dnl ========================================================
 if test -n "$MOZ_B2G_FM"; then
     AC_DEFINE(MOZ_B2G_FM)
 fi
 AC_SUBST(MOZ_B2G_FM)
 
 dnl ========================================================
+dnl = Enable Rtsp Protocol for B2G (Gonk usually)
+dnl ========================================================
+if test -n "$MOZ_RTSP"; then
+    AC_DEFINE(MOZ_RTSP)
+fi
+AC_SUBST(MOZ_RTSP)
+
+dnl ========================================================
 dnl = Enable Bluetooth Interface for B2G (Gonk usually)
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(b2g-bt,
 [  --enable-b2g-bt      Set compile flags necessary for compiling Bluetooth API for B2G ],
     MOZ_B2G_BT=1,
     MOZ_B2G_BT= )
 if test -n "$MOZ_B2G_BT"; then
     AC_DEFINE(MOZ_B2G_BT)
--- a/content/base/public/nsHostObjectProtocolHandler.h
+++ b/content/base/public/nsHostObjectProtocolHandler.h
@@ -10,16 +10,17 @@
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
 #include "nsIInputStream.h"
 
 #define BLOBURI_SCHEME "blob"
 #define MEDIASTREAMURI_SCHEME "mediastream"
 #define MEDIASOURCEURI_SCHEME "mediasource"
 #define FONTTABLEURI_SCHEME "moz-fonttable"
+#define RTSPURI_SCHEME "rtsp"
 
 class nsIDOMBlob;
 class nsIDOMMediaStream;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 class MediaSource;
@@ -80,16 +81,22 @@ public:
 };
 
 inline bool IsBlobURI(nsIURI* aUri)
 {
   bool isBlob;
   return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
 }
 
+inline bool IsRtspURI(nsIURI* aUri)
+{
+  bool isRtsp;
+  return NS_SUCCEEDED(aUri->SchemeIs(RTSPURI_SCHEME, &isRtsp)) && isRtsp;
+}
+
 inline bool IsMediaStreamURI(nsIURI* aUri)
 {
   bool isStream;
   return NS_SUCCEEDED(aUri->SchemeIs(MEDIASTREAMURI_SCHEME, &isStream)) && isStream;
 }
 
 inline bool IsMediaSourceURI(nsIURI* aUri)
 {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -919,16 +919,17 @@ nsContentUtils::GetParserService()
  * @return              the set of flags
  */
 uint32_t
 nsContentUtils::ParseSandboxAttributeToFlags(const nsAString& aSandboxAttrValue)
 {
   // If there's a sandbox attribute at all (and there is if this is being
   // called), start off by setting all the restriction flags.
   uint32_t out = SANDBOXED_NAVIGATION |
+                 SANDBOXED_AUXILIARY_NAVIGATION |
                  SANDBOXED_TOPLEVEL_NAVIGATION |
                  SANDBOXED_PLUGINS |
                  SANDBOXED_ORIGIN |
                  SANDBOXED_FORMS |
                  SANDBOXED_SCRIPTS |
                  SANDBOXED_AUTOMATIC_FEATURES |
                  SANDBOXED_POINTER_LOCK |
                  SANDBOXED_DOMAIN;
@@ -949,16 +950,18 @@ nsContentUtils::ParseSandboxAttributeToF
         // allow-scripts removes both SANDBOXED_SCRIPTS and
         // SANDBOXED_AUTOMATIC_FEATURES.
         out &= ~SANDBOXED_SCRIPTS;
         out &= ~SANDBOXED_AUTOMATIC_FEATURES;
       } else if (token.LowerCaseEqualsLiteral("allow-top-navigation")) {
         out &= ~SANDBOXED_TOPLEVEL_NAVIGATION;
       } else if (token.LowerCaseEqualsLiteral("allow-pointer-lock")) {
         out &= ~SANDBOXED_POINTER_LOCK;
+      } else if (token.LowerCaseEqualsLiteral("allow-popups")) {
+        out &= ~SANDBOXED_AUXILIARY_NAVIGATION;
       }
     }
   }
 
   return out;
 }
 
 #ifdef IBMBIDI
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5214,17 +5214,17 @@ nsDocument::Register(JSContext* aCx, con
     if (!protoObject) {
       rv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
   } else {
     // If a prototype is provided, we must check to ensure that it inherits
     // from HTMLElement.
     protoObject = aOptions.mPrototype;
-    if (!JS_WrapObject(aCx, protoObject.address())) {
+    if (!JS_WrapObject(aCx, &protoObject)) {
       rv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
 
     // Check the proto chain for HTMLElement prototype.
     JS::Rooted<JSObject*> protoProto(aCx);
     if (!JS_GetPrototype(aCx, protoObject, &protoProto)) {
       rv.Throw(NS_ERROR_UNEXPECTED);
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -225,24 +225,23 @@ mozilla::dom::ipc::UnpackClonedMessageDa
 
 StructuredCloneData
 mozilla::dom::ipc::UnpackClonedMessageDataForChild(const ClonedMessageData& aData)
 {
   return UnpackClonedMessageData<Child>(aData);
 }
 
 bool
-SameProcessCpowHolder::ToObject(JSContext* aCx, JSObject** aObjp)
+SameProcessCpowHolder::ToObject(JSContext* aCx, JS::MutableHandleObject aObjp)
 {
-  *aObjp = mObj;
-
   if (!mObj) {
     return true;
   }
 
+  aObjp.set(mObj);
   return JS_WrapObject(aCx, aObjp);
 }
 
 // nsIMessageListenerManager
 
 NS_IMETHODIMP
 nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
                                           nsIMessageListener* aListener)
@@ -833,17 +832,17 @@ nsFrameMessageManager::ReceiveMessage(ns
 
         JS::Rooted<JS::Value> targetv(ctx);
         JS::Rooted<JSObject*> global(ctx, JS_GetGlobalForObject(ctx, object));
         nsContentUtils::WrapNative(ctx, global, aTarget, targetv.address(),
                                    nullptr, true);
 
         JS::RootedObject cpows(ctx);
         if (aCpows) {
-          if (!aCpows->ToObject(ctx, cpows.address())) {
+          if (!aCpows->ToObject(ctx, &cpows)) {
             return NS_ERROR_UNEXPECTED;
           }
         }
 
         if (!cpows) {
           cpows = JS_NewObject(ctx, nullptr, nullptr, nullptr);
           if (!cpows) {
             return NS_ERROR_UNEXPECTED;
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -117,28 +117,28 @@ struct nsMessageListenerInfo
   nsCOMPtr<nsIMessageListener> mStrongListener;
   nsWeakPtr mWeakListener;
   nsCOMPtr<nsIAtom> mMessage;
 };
 
 class CpowHolder
 {
   public:
-    virtual bool ToObject(JSContext* cx, JSObject** objp) = 0;
+    virtual bool ToObject(JSContext* cx, JS::MutableHandleObject objp) = 0;
 };
 
 class MOZ_STACK_CLASS SameProcessCpowHolder : public CpowHolder
 {
   public:
     SameProcessCpowHolder(JSRuntime *aRuntime, JS::Handle<JSObject *> aObj)
       : mObj(aRuntime, aObj)
     {
     }
 
-    bool ToObject(JSContext* aCx, JSObject** aObjp);
+    bool ToObject(JSContext* aCx, JS::MutableHandleObject aObjp);
 
   private:
     JS::Rooted<JSObject*> mObj;
 };
 
 class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager,
                                         public nsIMessageBroadcaster,
                                         public nsIFrameScriptLoader,
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -67,22 +67,18 @@ GK_ATOM(actuate, "actuate")
 GK_ATOM(address, "address")
 GK_ATOM(after, "after")
 GK_ATOM(after_end, "after_end")
 GK_ATOM(after_start, "after_start")
 GK_ATOM(align, "align")
 GK_ATOM(alink, "alink")
 GK_ATOM(all, "all")
 GK_ATOM(allowevents, "allowevents")
-GK_ATOM(allowforms, "allow-forms")
 GK_ATOM(allownegativeassertions, "allownegativeassertions")
 GK_ATOM(allowfullscreen, "allowfullscreen")
-GK_ATOM(allowsameorigin, "allow-same-origin")
-GK_ATOM(allowscripts, "allow-scripts")
-GK_ATOM(allowtopnavigation, "allow-top-navigation")
 GK_ATOM(allowuntrusted, "allowuntrusted")
 GK_ATOM(alt, "alt")
 GK_ATOM(alternate, "alternate")
 GK_ATOM(always, "always")
 GK_ATOM(ancestor, "ancestor")
 GK_ATOM(ancestorOrSelf, "ancestor-or-self")
 GK_ATOM(_and, "and")
 GK_ATOM(any, "any")
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -3120,17 +3120,17 @@ nsObjectLoadingContent::LegacyCall(JSCon
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   JS::Rooted<JSObject*> obj(aCx, thisContent->GetWrapper());
   MOZ_ASSERT(obj, "How did we get called?");
 
   // Make sure we're not dealing with an Xray.  Our DoCall code can't handle
   // random cross-compartment wrappers, so we're going to have to wrap
   // everything up into our compartment, but that means we need to check that
   // this is not an Xray situation by hand.
-  if (!JS_WrapObject(aCx, obj.address())) {
+  if (!JS_WrapObject(aCx, &obj)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return JS::UndefinedValue();
   }
 
   if (nsDOMClassInfo::ObjectIsNativeWrapper(aCx, obj)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
     return JS::UndefinedValue();
   }
--- a/content/base/src/nsSandboxFlags.h
+++ b/content/base/src/nsSandboxFlags.h
@@ -8,18 +8,21 @@
  * HTML5 spec.
  */
 
 #ifndef nsSandboxFlags_h___
 #define nsSandboxFlags_h___
 
 /**
  * This flag prevents content from navigating browsing contexts other than
- * the sandboxed browsing context itself (or browsing contexts further
- * nested inside it), and the top-level browsing context.
+ * itself, browsing contexts nested inside it, the top-level browsing context
+ * and browsing contexts that it has opened.
+ * As it is always on for sandboxed browsing contexts, it is used implicitly
+ * within the code by checking that the overall flags are non-zero.
+ * It is only uesd directly when the sandbox flags are initially set up.
  */
 const unsigned long SANDBOXED_NAVIGATION  = 0x1;
 
 /**
  * This flag prevents content from navigating their top-level browsing
  * context.
  */
 const unsigned long SANDBOXED_TOPLEVEL_NAVIGATION = 0x2;
@@ -60,9 +63,16 @@ const unsigned long SANDBOXED_AUTOMATIC_
  * This flag blocks the document from acquiring pointerlock.
  */
 const unsigned long SANDBOXED_POINTER_LOCK = 0x80;
 
 /**
  * This flag blocks the document from changing document.domain.
  */
 const unsigned long SANDBOXED_DOMAIN = 0x100;
+
+/**
+ * This flag prevents content from creating new auxiliary browsing contexts,
+ * e.g. using the target attribute, the window.open() method, or the
+ * showModalDialog() method.
+ */
+const unsigned long SANDBOXED_AUXILIARY_NAVIGATION = 0x200;
 #endif
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2367,16 +2367,18 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
       RefPtr<ScaledFont> scaledFont =
         gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
 
       if (!scaledFont) {
         // This can occur when something switched DirectWrite off.
         return;
       }
 
+      RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
+
       GlyphBuffer buffer;
 
       std::vector<Glyph> glyphBuf;
 
       for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
         Glyph newGlyph;
         if (glyphs[i].IsSimpleGlyph()) {
           newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
@@ -2438,17 +2440,18 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
       Rect bounds = mCtx->mTarget->GetTransform().
         TransformBounds(Rect(mBoundingBox.x, mBoundingBox.y,
                              mBoundingBox.width, mBoundingBox.height));
       if (mOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL) {
         AdjustedTarget(mCtx, &bounds)->
           FillGlyphs(scaledFont, buffer,
                      CanvasGeneralPattern().
                        ForStyle(mCtx, CanvasRenderingContext2D::STYLE_FILL, mCtx->mTarget),
-                     DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
+                     DrawOptions(mState->globalAlpha, mCtx->UsedOperation()),
+                     renderingOptions);
       } else if (mOp == CanvasRenderingContext2D::TEXT_DRAW_OPERATION_STROKE) {
         // stroke glyphs one at a time to avoid poor CoreGraphics performance
         // when stroking a path with a very large number of points
         buffer.mGlyphs = &glyphBuf.front();
         buffer.mNumGlyphs = 1;
         const ContextState& state = *mState;
         AdjustedTarget target(mCtx, &bounds);
         const StrokeOptions strokeOpts(state.lineWidth, state.lineJoin,
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -531,16 +531,23 @@ public:
   }
 
   void RemoveTextTrack(TextTrack* aTextTrack) {
     if (mTextTracks) {
       mTextTracks->RemoveTextTrack(*aTextTrack);
     }
   }
 
+  /**
+   * A public wrapper for FinishDecoderSetup()
+   */
+  nsresult FinishDecoderSetup(MediaDecoder* aDecoder, MediaResource* aStream) {
+    return FinishDecoderSetup(aDecoder, aStream, nullptr, nullptr);
+  }
+
 protected:
   class MediaLoadListener;
   class StreamListener;
 
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
   virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
 
   class WakeLockBoolWrapper {
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -2500,17 +2500,29 @@ nsresult HTMLMediaElement::InitializeDec
 
   nsRefPtr<MediaResource> resource = MediaResource::Create(decoder, aChannel);
   if (!resource)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // stream successfully created, the stream now owns the channel.
   mChannel = nullptr;
 
-  return FinishDecoderSetup(decoder, resource, aListener, nullptr);
+  // We postpone the |FinishDecoderSetup| function call until we get
+  // |OnConnected| signal from MediaStreamController which is held by
+  // RtspMediaResource.
+  if (DecoderTraits::DecoderWaitsForOnConnected(mimeType)) {
+    decoder->SetResource(resource);
+    mDecoder = decoder;
+    if (aListener) {
+      *aListener = nullptr;
+    }
+    return NS_OK;
+  } else {
+    return FinishDecoderSetup(decoder, resource, aListener, nullptr);
+  }
 }
 
 nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
                                               MediaResource* aStream,
                                               nsIStreamListener** aListener,
                                               MediaDecoder* aCloneDonor)
 {
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
--- a/content/html/content/src/HTMLUnknownElement.cpp
+++ b/content/html/content/src/HTMLUnknownElement.cpp
@@ -19,17 +19,17 @@ HTMLUnknownElement::WrapNode(JSContext *
     HTMLUnknownElementBinding::Wrap(aCx, aScope, this));
   if (obj && Substring(NodeName(), 0, 2).LowerCaseEqualsLiteral("x-")) {
     // If we have a registered x-tag then we fix the prototype.
     JSAutoCompartment ac(aCx, obj);
     nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
     JS::Rooted<JSObject*> prototype(aCx);
     document->GetCustomPrototype(LocalName(), &prototype);
     if (prototype) {
-      NS_ENSURE_TRUE(JS_WrapObject(aCx, prototype.address()), nullptr);
+      NS_ENSURE_TRUE(JS_WrapObject(aCx, &prototype), nullptr);
       NS_ENSURE_TRUE(JS_SetPrototype(aCx, obj, prototype), nullptr);
     }
   }
   return obj;
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLUnknownElement)
 
--- a/content/html/content/test/file_iframe_sandbox_a_if4.html
+++ b/content/html/content/test/file_iframe_sandbox_a_if4.html
@@ -7,21 +7,23 @@
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 </head>
 
 <script type="text/javascript">
 function doStuff() {
   try {
     window.parent.ok_wrapper(false, "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with its parent");
   } catch(e) {
+    window.parent.parent.postMessage({type: "ok", ok: true, desc: "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with its parent"}, "*");
   }
 
   try {
     window.parent.parent.ok_wrapper(false, "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with the top level");
   } catch(e) {
+    window.parent.parent.postMessage({type: "ok", ok: true, desc: "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with the top level"}, "*");
   }
 }
 </script>
 
 <body onLoad="doStuff()">
   I am not sandboxed but contained within a sandboxed document with 'allow-scripts'
 </body>
 </html>
--- a/content/html/content/test/file_iframe_sandbox_b_if3.html
+++ b/content/html/content/test/file_iframe_sandbox_b_if3.html
@@ -8,17 +8,17 @@
 <script>
   function ok(result, message) {
     window.parent.postMessage({ok: result, desc: message}, "*");
   }
 
   function testXHR() {
   var xhr = new XMLHttpRequest();
 
-  xhr.open("GET", "file_iframe_sandbox_if1.html");
+  xhr.open("GET", "file_iframe_sandbox_b_if1.html");
 
   xhr.onreadystatechange = function (oEvent) {
     var result = false;
     if (xhr.readyState == 4) {
       if (xhr.status == 0) {
         result = true;
       }
       ok(result, "XHR should be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
--- a/content/html/content/test/file_iframe_sandbox_c_if4.html
+++ b/content/html/content/test/file_iframe_sandbox_c_if4.html
@@ -1,44 +1,45 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-
 </head>
 <script type="text/javascript">
   function ok(result, desc) {
     window.parent.ok_wrapper(result, desc);
   }
 
   function doStuff() {
-    // try to open a new window via target="_blank", window.open(), and showModalDialog()
+    // try to open a new window via target="_blank", target="BC341604", window.open(), and showModalDialog()
     // the window we try to open closes itself once it opens
     sendMouseEvent({type:'click'}, 'target_blank');
+    sendMouseEvent({type:'click'}, 'target_BC341604');
 
     var threw = false;
     try {
       window.open("about:blank");
     } catch (error) {
       threw = true;
     }
-    
+
     ok(threw, "window.open threw a JS exception and was not allowed");
 
     threw = false;
     try {
       window.showModalDialog("about:blank");
     } catch(error) {
       threw = true;
     }
-    
+
     ok(threw, "window.showModalDialog threw a JS exception and was not allowed");
   }
 </script>
 <body onLoad="doStuff()">
   I am sandboxed but with "allow-scripts allow-same-origin"
 
   <a href="file_iframe_sandbox_open_window_fail.html" target="_blank" id="target_blank">open window</a>
+  <a href="file_iframe_sandbox_open_window_fail.html" target="BC341604" id="target_BC341604">open window</a>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_close.html
@@ -0,0 +1,3 @@
+<script>
+  self.close();
+</script>
--- a/content/html/content/test/file_iframe_sandbox_d_if10.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if10.html
@@ -3,15 +3,15 @@
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 </head>
 <script type="application/javascript">
 function doTest() {
-  window.parent.postMessage({test:'if_10'}, "*");
+  window.parent.postMessage({type: "if_10"}, "*");
 }
 </script>
 <body onload='doTest()'>
   I am sandboxed with 'allow-scripts'
 </body>
 </html>
--- a/content/html/content/test/file_iframe_sandbox_d_if14.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if14.html
@@ -9,23 +9,23 @@
 
 <script type="text/javascript">
   var test20Context = "Test 20: Navigate another window (not opened by us): ";
 
   function doTest() {
     // Try to navigate auxiliary browsing context (window) not opened by us.
     // We should not be able to do this as we are sandboxed.
     sendMouseEvent({type:'click'}, 'navigate_window');
-    window.parent.postMessage("test attempted", "*");
+    window.parent.postMessage({type: "attempted"}, "*");
 
     // Try to navigate auxiliary browsing context (window) not opened by us, using window.open().
     // We should not be able to do this as we are sandboxed.
     try {
       window.open("file_iframe_sandbox_window_navigation_fail.html?" + escape(test20Context), "window_to_navigate2");
-      window.parent.postMessage("test attempted", "*");
+      window.parent.postMessage({type: "attempted"}, "*");
     } catch(error) {
       window.parent.postMessage({ok: true, desc: test20Context + "as expected, error thrown during window.open(..., \"window_to_navigate2\")"}, "*");
     }
   }
 </script>
 
 <body onload="doTest()">
   I am sandboxed but with "allow-scripts allow-same-origin allow-top-navigation".
--- a/content/html/content/test/file_iframe_sandbox_d_if16.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if16.html
@@ -4,17 +4,17 @@
   <meta charset="utf-8">
   <title>Test for Bug 838692</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 </head>
 
 <script type="application/javascript">
 function doTest() {
-  window.parent.parent.postMessage("test attempted", "*");
+  window.parent.parent.postMessage({type: "attempted"}, "*");
   sendMouseEvent({type:'click'}, 'anchor');
 }
 </script>
 
 <body onload="doTest()">
   I am sandboxed with 'allow-same-origin allow-scripts'
 
   <a href="file_iframe_sandbox_navigation_fail.html?Test 16: Navigate parent/ancestor by name:%20" target='if_parent' id='anchor'>
--- a/content/html/content/test/file_iframe_sandbox_d_if20.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if20.html
@@ -7,17 +7,17 @@
 </head>
 
 <script type="application/javascript">
   var testContext = "Test 19: navigate _parent with window.open(): ";
 
   function doTest() {
     try {
       window.open("file_iframe_sandbox_navigation_fail.html?" + escape(testContext), "_parent");
-      window.parent.parent.postMessage("test attempted", "*");
+      window.parent.parent.postMessage({type: "attempted"}, "*");
     } catch(error) {
       window.parent.parent.postMessage({ok: true, desc: testContext + "as expected, error thrown during window.open(..., \"_parent\")"}, "*");
     }
   }
 </script>
 
 <body onload="doTest()">
   I am sandboxed with 'allow-scripts'
--- a/content/html/content/test/file_iframe_sandbox_d_if22.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if22.html
@@ -7,17 +7,17 @@
 </head>
 
 <script type="application/javascript">
   var testContext = "Test 21: navigate parent by name with window.open(): ";
 
   function doTest() {
     try {
       window.open("file_iframe_sandbox_navigation_fail.html?" + escape(testContext), "if_parent2");
-      window.parent.parent.postMessage("test attempted", "*");
+      window.parent.parent.postMessage({type: "attempted"}, "*");
     } catch(error) {
       window.parent.parent.postMessage({ok: true, desc: testContext + "as expected, error thrown during window.open(..., \"if_parent2\")"}, "*");
     }
   }
 </script>
 
 <body onload="doTest()">
   I am sandboxed with 'allow-same-origin allow-scripts'
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_d_if23.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 838692</title>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+  var test27Context = "Test 27: navigate opened window by name with anchor: ";
+  var test28Context = "Test 28: navigate opened window by name with window.open(): ";
+
+  var windowsToClose = new Array();
+
+  function closeWindows() {
+    for (var i = 0; i < windowsToClose.length; i++) {
+      windowsToClose[i].close();
+    }
+  }
+
+  // Add message listener to forward messages on to parent
+  window.addEventListener("message", receiveMessage, false);
+
+  function receiveMessage(event) {
+    switch (event.data.type) {
+      case "closeWindows":
+        closeWindows();
+        break;
+      default:
+        window.parent.postMessage(event.data, "*");
+    }
+  }
+
+  function doTest() {
+    try {
+      windowsToClose.push(window.open("about:blank", "test27window"));
+      var test27Anchor = document.getElementById("test27Anchor");
+      test27Anchor.href = "file_iframe_sandbox_window_navigation_pass.html?" + escape(test27Context);
+      sendMouseEvent({type:"click"}, "test27Anchor");
+      window.parent.postMessage({type: "attempted"}, "*");
+    } catch(error) {
+      window.parent.postMessage({ok: false, desc: test27Context + "error thrown during window.open(): " + error}, "*");
+    }
+
+    try {
+      windowsToClose.push(window.open("about:blank", "test28window"));
+      window.open("file_iframe_sandbox_window_navigation_pass.html?" + escape(test28Context), "test28window");
+      window.parent.postMessage({type: "attempted"}, "*");
+    } catch(error) {
+      window.parent.postMessage({ok: false, desc: test28Context + "error thrown during window.open(): " + error}, "*");
+    }
+  }
+</script>
+
+<body onload="doTest()">
+  I am sandboxed with 'allow-scripts allow-popups'
+
+  <a id="test27Anchor" target="test27window">Test 27 anchor</a>
+</body>
+</html>
--- a/content/html/content/test/file_iframe_sandbox_d_if4.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if4.html
@@ -3,17 +3,17 @@
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 </head>
 <script type="application/javascript">
 function doTest() {
-  window.parent.parent.postMessage("test attempted", "*");
+  window.parent.parent.postMessage({type: "attempted"}, "*");
   sendMouseEvent({type:'click'}, 'anchor');
 }
 </script>
 <body onload="doTest()">
   I am sandboxed with 'allow-scripts'
 
   <a href="file_iframe_sandbox_navigation_fail.html" target='_parent' id='anchor'>
 </body>
--- a/content/html/content/test/file_iframe_sandbox_d_if5.html
+++ b/content/html/content/test/file_iframe_sandbox_d_if5.html
@@ -4,17 +4,17 @@
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 </head>
 <script type="application/javascript">
 function doTest() {
   sendMouseEvent({type:'click'}, 'anchor');
-  window.parent.postMessage("test attempted", "*");
+  window.parent.postMessage({type: "attempted"}, "*");
 }
 </script>
 <body onload="doTest()">
   I am sandboxed with 'allow-scripts allow-same-origin'
 
   <a href="file_iframe_sandbox_navigation_fail.html?Test 4: Navigate sibling iframe by name:%20" target='if_sibling' id='anchor'>
 </body>
 </html>
--- a/content/html/content/test/file_iframe_sandbox_e_if6.html
+++ b/content/html/content/test/file_iframe_sandbox_e_if6.html
@@ -5,17 +5,17 @@
   <title>Test for Bug 341604</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 </head>
 <script type="application/javascript">
 function doTest() {
   document.getElementById('anchor').href = "file_iframe_sandbox_top_navigation_fail.html" + location.search;
-  window.top.opener.postMessage("test attempted", "*");
+  window.top.opener.postMessage({type: "attempted"}, "*");
   sendMouseEvent({type:'click'}, 'anchor');
 }
 </script>
 <body onload="doTest()">
   I am sandboxed with 'allow-scripts'
 
   <a target='_top' id='anchor'>
 </body>
--- a/content/html/content/test/file_iframe_sandbox_e_if8.html
+++ b/content/html/content/test/file_iframe_sandbox_e_if8.html
@@ -5,17 +5,17 @@
   <title>Tests for Bug 838692</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 </head>
 
 <script>
   function doTest() {
     // Try to navigate top using its name (e_if7).  We should not be able to do this as allow-top-navigation is not specified.
-    window.top.opener.postMessage("test attempted", "*");
+    window.top.opener.postMessage({type: "attempted"}, "*");
     sendMouseEvent({type:'click'}, 'navigate_top');
   }
 </script>
 
 <body onload="doTest()">
   I am sandboxed but with "allow-scripts"
 
   <a href="file_iframe_sandbox_top_navigation_fail.html?Test 15: Navigate top by name:%20" target="e_if7" id="navigate_top">navigate top</a>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_h_if1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Bug 766282</title>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+  function ok(result, desc) {
+    window.parent.ok_wrapper(result, desc);
+  }
+
+  function doStuff() {
+    // Try to open a new window via target="_blank", target="BC766282" and window.open().
+    // The window we try to open closes itself once it opens.
+    sendMouseEvent({type:'click'}, 'target_blank');
+    sendMouseEvent({type:'click'}, 'target_BC766282');
+
+    try {
+      window.open("file_iframe_sandbox_open_window_pass.html");
+    } catch(e) {
+      ok(false, "Test 3: iframes sandboxed with allow-popups, should be able to open windows");
+    }
+  }
+</script>
+<body onLoad="doStuff()">
+  I am sandboxed but with "allow-popups allow-scripts allow-same-origin"
+
+  <a href="file_iframe_sandbox_open_window_pass.html" target="_blank" id="target_blank">open window</a>
+  <a href="file_iframe_sandbox_open_window_pass.html?BC766282" target="BC766282" id="target_BC766282">open window</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_j_if1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  function doStuff() {
+    // Open a new window via showModalDialog().
+    try {
+      window.showModalDialog("file_iframe_sandbox_k_if5.html");
+    } catch(e) {
+      window.parent.ok_wrapper(false, "iframes sandboxed with allow-popups should be able to open a modal dialog");
+    }
+
+    // Open a new window via showModalDialog().
+    try {
+      window.showModalDialog("file_iframe_sandbox_k_if7.html");
+    } catch(e) {
+      window.parent.ok_wrapper(false, "iframes sandboxed with allow-popups should be able to open a modal dialog");
+    }
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I am sandboxed with "allow-scripts allow-popups allow-same-origin allow-forms allow-top-navigation".
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_j_if2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  function doSubOpens() {
+    // Open a new window showModalDialog().
+    try {
+      window.showModalDialog("file_iframe_sandbox_k_if9.html");
+    } catch(e) {
+      window.parent.ok_wrapper(false, "iframes sandboxed with allow-popups should be able to open a modal dialog");
+    }
+  }
+
+  window.doSubOpens = doSubOpens;
+</script>
+
+<body>
+  I am sandboxed but with "allow-scripts allow-popups allow-same-origin".
+  After my initial load, "allow-same-origin" is removed and then I open file_iframe_sandbox_k_if9.html,
+  which attemps to call a function in my parent.
+  This should succeed since the new sandbox flags shouldn't have taken affect on me until I'm reloaded.
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_j_if3.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+  function ok(result, desc) {
+    window.parent.ok_wrapper(result, desc);
+  }
+
+  function doStuff() {
+    // Try to open a new window via showModalDialog().
+    // The window we try to open closes itself once it opens.
+    try {
+      window.showModalDialog("file_iframe_sandbox_open_window_pass.html");
+    } catch(e) {
+      ok(false, "iframes sandboxed with allow-popups should be able to open a modal dialog");
+    }
+  }
+</script>
+<body onLoad="doStuff()">
+  I am sandboxed but with "allow-popups allow-scripts allow-same-origin"
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if1.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+  var windowsToClose = new Array();
+
+  function closeWindows() {
+    for (var i = 0; i < windowsToClose.length; i++) {
+      windowsToClose[i].close();
+    }
+    window.open("file_iframe_sandbox_close.html", "blank_if2");
+    window.open("file_iframe_sandbox_close.html", "BC766282_if2");
+  }
+
+  // Add message listener to forward messages on to parent
+  window.addEventListener("message", receiveMessage, false);
+
+  function receiveMessage(event) {
+    switch (event.data.type) {
+      case "closeWindows":
+        closeWindows();
+        break;
+    }
+  }
+
+  function doStuff() {
+    // Open a new window via target="_blank", target="BC766282_if2" and window.open().
+    sendMouseEvent({type:'click'}, 'target_blank_if2');
+    sendMouseEvent({type:'click'}, 'target_BC766282_if2');
+
+    windowsToClose.push(window.open("file_iframe_sandbox_k_if2.html"));
+  }
+</script>
+<body onLoad="doStuff()">
+  I am navigated to from file_iframe_sandbox_k_if8.html.
+  This was opened in an iframe with "allow-scripts allow-popups allow-same-origin".
+  However allow-same-origin was removed from the iframe before navigating to me,
+  so I should only have "allow-scripts allow-popups" in force.
+  <a href="file_iframe_sandbox_k_if2.html" target="_blank" id="target_blank_if2">open window</a>
+  <a href="file_iframe_sandbox_k_if2.html" target="BC766282_if2" id="target_BC766282_if2">open window</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if2.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  if (window.name == "") {
+    window.name = "blank_if2";
+  }
+
+  function ok(result, message) {
+    window.opener.parent.postMessage({type: "ok", ok: result, desc: message}, "*");
+  }
+
+  function doStuff() {
+    // Check that sandboxed forms browsing context flag copied by attempting to submit a form.
+    document.getElementById('a_form').submit();
+    window.opener.parent.postMessage({type: "attempted"}, "*");
+
+    // Check that sandboxed origin browsing context flag copied by attempting to access cookies.
+    try {
+      var foo = document.cookie;
+      ok(false, "Sandboxed origin browsing context flag NOT copied to new auxiliary browsing context.");
+    } catch(error) {
+      ok(true, "Sandboxed origin browsing context flag copied to new auxiliary browsing context.");
+    }
+
+    // Check that sandboxed top-level navigation browsing context flag copied.
+    // if_3 tries to navigate this document.
+    var if_3 = document.getElementById('if_3');
+    if_3.src = "file_iframe_sandbox_k_if3.html";
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I am not sandboxed directly, but opened from a sandboxed document with 'allow-scripts allow-popups'
+
+  <form method="get" action="file_iframe_sandbox_window_form_fail.html" id="a_form">
+    First name: <input type="text" name="firstname">
+    Last name: <input type="text" name="lastname">
+    <input type="submit" id="a_button">
+  </form>
+
+  <iframe id="if_3" src="about:blank" height="10" width="10"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if3.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+  function doTest() {
+    sendMouseEvent({type:'click'}, 'anchor');
+    window.parent.opener.parent.postMessage({type: "attempted"}, "*");
+  }
+</script>
+<body onload="doTest()">
+  I am sandboxed with 'allow-scripts allow-popups'
+
+  <a href="file_iframe_sandbox_window_top_navigation_fail.html" target='_top' id='anchor'>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  function doStuff() {
+    // Open a new window via target="_blank", target="BC766282_if5" and window.open().
+    sendMouseEvent({type:'click'}, 'target_blank_if5');
+    sendMouseEvent({type:'click'}, 'target_BC766282_if5');
+
+    window.open("file_iframe_sandbox_k_if5.html");
+
+    // Open a new window via target="_blank", target="BC766282_if7" and window.open().
+    sendMouseEvent({type:'click'}, 'target_blank_if7');
+    sendMouseEvent({type:'click'}, 'target_BC766282_if7');
+
+    window.open("file_iframe_sandbox_k_if7.html");
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I am sandboxed with "allow-scripts allow-popups allow-same-origin allow-forms allow-top-navigation".
+  <a href="file_iframe_sandbox_k_if5.html" target="_blank" id="target_blank_if5">open window</a>
+  <a href="file_iframe_sandbox_k_if5.html" target="BC766282_if5" id="target_BC766282_if5">open window</a>
+
+  <a href="file_iframe_sandbox_k_if7.html" target="_blank" id="target_blank_if7">open window</a>
+  <a href="file_iframe_sandbox_k_if7.html" target="BC766282_if7" id="target_BC766282_if7">open window</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if5.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  function doStuff() {
+    // Check that sandboxed origin browsing context flag NOT set by attempting to access cookies.
+    try {
+      var foo = document.cookie;
+      window.opener.parent.ok_wrapper(true, "Sandboxed origin browsing context flag NOT set on new auxiliary browsing context.");
+    } catch(error) {
+      window.opener.parent.ok_wrapper(false, "Sandboxed origin browsing context flag set on new auxiliary browsing context.");
+    }
+
+    // Check that sandboxed top-level navigation browsing context flag NOT set.
+    // if_6 tries to navigate this document.
+    var if_6 = document.getElementById('if_6');
+    if_6.src = "file_iframe_sandbox_k_if6.html";
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I am not sandboxed directly, but opened from a sandboxed document with at least
+  'allow-scripts allow-popups allow-same-origin allow-top-navigation'
+
+  <iframe id="if_6" src="about:blank" height="10" width="10"></iframe>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if6.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="application/javascript">
+  function doTest() {
+    sendMouseEvent({type:'click'}, 'anchor');
+  }
+</script>
+
+<body onload="doTest()">
+  I am sandboxed with at least 'allow-scripts allow-popups allow-top-navigation'
+
+  <a href="file_iframe_sandbox_window_top_navigation_pass.html" target='_top' id='anchor'>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if7.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  function doStuff() {
+    // Check that sandboxed forms browsing context flag NOT set by attempting to submit a form.
+    document.getElementById('a_form').submit();
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I am not sandboxed directly, but opened from a sandboxed document with at least
+  'allow-scripts allow-popups allow-forms allow-same-origin'
+
+  <form method="get" action="file_iframe_sandbox_window_form_pass.html" id="a_form">
+    First name: <input type="text" name="firstname">
+    Last name: <input type="text" name="lastname">
+    <input type="submit" id="a_button">
+  </form>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if8.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+  function doSubOpens() {
+    // Open a new window via target="_blank", target="BC766282_if9" and window.open().
+    sendMouseEvent({type:'click'}, 'target_blank_if9');
+    sendMouseEvent({type:'click'}, 'target_BC766282_if9');
+
+    window.open("file_iframe_sandbox_k_if9.html");
+
+    sendMouseEvent({type:'click'}, 'target_if1');
+  }
+
+  window.doSubOpens = doSubOpens;
+</script>
+
+<body>
+  I am sandboxed but with "allow-scripts allow-popups allow-same-origin".
+  After my initial load, "allow-same-origin" is removed and then I open file_iframe_sandbox_k_if9.html
+  in 3 different ways, which attemps to call a function in my parent.
+  This should succeed since the new sandbox flags shouldn't have taken affect on me until I'm reloaded.
+  <a href="file_iframe_sandbox_k_if9.html" target="_blank" id="target_blank_if9">open window</a>
+  <a href="file_iframe_sandbox_k_if9.html" target="BC766282_if9" id="target_BC766282_if9">open window</a>
+
+  Now navigate to file_iframe_sandbox_k_if1.html to do tests for a sandbox opening a window
+  when only "allow-scripts allow-popups" are specified.
+  <a href="file_iframe_sandbox_k_if1.html" id="target_if1">navigate to if1</a>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_k_if9.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  function doStuff() {
+    window.opener.parent.ok_wrapper(true, "A window opened from within a sandboxed document should inherit the flags of the document, not of the docshell/sandbox attribute.");
+    self.close();
+  }
+</script>
+
+<body onload='doStuff()'>
+  I'm a window opened from the sandboxed document of file_iframe_sandbox_k_if8.html.
+  I should be able to call ok_wrapper in main test page directly because I should be same-origin with it.
+</body>
+</html>
--- a/content/html/content/test/file_iframe_sandbox_open_window_fail.html
+++ b/content/html/content/test/file_iframe_sandbox_open_window_fail.html
@@ -1,21 +1,19 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 
 <body onLoad="doStuff()">
   I should NOT be opened by a sandboxed iframe via any method
 </body>
 </html>
 
 <script>
   function doStuff() {
-    console.log("file_iframe_sandbox_window_open_fail.html");
     window.opener.ok(false, "sandboxed documents should NOT be able to open windows");
     self.close();
   }
-</script>
\ No newline at end of file
+</script>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_open_window_pass.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onLoad="doStuff()">
+  I should be opened by a sandboxed iframe via any method when "allow-popups" is specified.
+</body>
+</html>
+
+<script>
+  function doStuff() {
+    // Check that the browsing context's (window's) name is as expected.
+    var expectedName = location.search.substring(1);
+    if (expectedName == window.name) {
+      window.opener.ok(true, "sandboxed documents should be able to open windows when \"allow-popups\" is specified");
+    } else {
+      window.opener.ok(false, "window opened with \"allow-popups\", but expected name was " + expectedName + " and actual was " + window.name);
+    }
+    self.close();
+  }
+</script>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_window_form_fail.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onLoad="doStuff()">
+  I should NOT be loaded by a form submit from a window opened from a sandbox without 'allow-forms'.
+</body>
+</html>
+
+<script>
+  function doStuff() {
+    window.opener.parent.postMessage({ok: false, desc: "documents sandboxed without allow-forms should NOT be able to submit forms"}, "*");
+
+    self.close();
+  }
+</script>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_window_form_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  function doStuff() {
+    window.opener.parent.ok_wrapper(true, "Sandboxed forms browsing context flag NOT set on new auxiliary browsing context.");
+
+    self.close();
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I should be loaded by a form submit from a window opened from a sandbox with 'allow-forms allow-same-origin'.
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_window_navigation_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+function doStuff() {
+  var testContext = unescape(location.search.substring(1));
+  window.opener.postMessage({type: "ok", ok: true, desc: testContext + "a permitted sandboxed document should be able to navigate a window it has opened.", addToAttempted: false}, "*");
+  window.close();
+}
+</script>
+
+<body onLoad="doStuff()">
+PASS
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_window_top_navigation_fail.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+  function doStuff() {
+    window.opener.parent.postMessage({ok: false, desc:  "Sandboxed top-level navigation browsing context flag NOT copied to new auxiliary browsing context."}, "*");
+
+    // Check that when no browsing context returned by "target='_top'", a new browsing context isn't opened by mistake.
+    try {
+      window.opener.parent.opener.parent.postMessage({ok: false, desc:  "An attempt at top navigation without 'allow-top-navigation' should not have opened a new browsing context."}, "*");
+    } catch (error) {
+    }
+
+    self.close();
+  }
+</script>
+<body onLoad="doStuff()">
+FAIL
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_window_top_navigation_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 766282</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  function doStuff() {
+    window.opener.parent.ok_wrapper(true, "Sandboxed top-level navigation browsing context flag NOT copied to new auxiliary browsing context.");
+
+    self.close();
+  }
+</script>
+
+<body onLoad="doStuff()">
+  I am navigated to from a window opened from a sandbox with allow-top-navigation.
+</body>
+</html>
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -64,31 +64,33 @@ support-files =
   file_iframe_sandbox_c_if1.html
   file_iframe_sandbox_c_if2.html
   file_iframe_sandbox_c_if3.html
   file_iframe_sandbox_c_if4.html
   file_iframe_sandbox_c_if5.html
   file_iframe_sandbox_c_if6.html
   file_iframe_sandbox_c_if7.html
   file_iframe_sandbox_c_if8.html
+  file_iframe_sandbox_close.html
   file_iframe_sandbox_d_if1.html
   file_iframe_sandbox_d_if10.html
   file_iframe_sandbox_d_if11.html
   file_iframe_sandbox_d_if12.html
   file_iframe_sandbox_d_if13.html
   file_iframe_sandbox_d_if14.html
   file_iframe_sandbox_d_if15.html
   file_iframe_sandbox_d_if16.html
   file_iframe_sandbox_d_if17.html
   file_iframe_sandbox_d_if18.html
   file_iframe_sandbox_d_if19.html
   file_iframe_sandbox_d_if2.html
   file_iframe_sandbox_d_if20.html
   file_iframe_sandbox_d_if21.html
   file_iframe_sandbox_d_if22.html
+  file_iframe_sandbox_d_if23.html
   file_iframe_sandbox_d_if3.html
   file_iframe_sandbox_d_if4.html
   file_iframe_sandbox_d_if5.html
   file_iframe_sandbox_d_if6.html
   file_iframe_sandbox_d_if7.html
   file_iframe_sandbox_d_if8.html
   file_iframe_sandbox_d_if9.html
   file_iframe_sandbox_e_if1.html
@@ -109,24 +111,43 @@ support-files =
   file_iframe_sandbox_e_if9.html
   file_iframe_sandbox_f_if1.html
   file_iframe_sandbox_f_if2.html
   file_iframe_sandbox_f_if2.html^headers^
   file_iframe_sandbox_fail.js
   file_iframe_sandbox_form_fail.html
   file_iframe_sandbox_form_pass.html
   file_iframe_sandbox_g_if1.html
+  file_iframe_sandbox_h_if1.html
+  file_iframe_sandbox_j_if1.html
+  file_iframe_sandbox_j_if2.html
+  file_iframe_sandbox_j_if3.html
+  file_iframe_sandbox_k_if1.html
+  file_iframe_sandbox_k_if2.html
+  file_iframe_sandbox_k_if3.html
+  file_iframe_sandbox_k_if4.html
+  file_iframe_sandbox_k_if5.html
+  file_iframe_sandbox_k_if6.html
+  file_iframe_sandbox_k_if7.html
+  file_iframe_sandbox_k_if8.html
+  file_iframe_sandbox_k_if9.html
   file_iframe_sandbox_navigation_fail.html
   file_iframe_sandbox_navigation_pass.html
   file_iframe_sandbox_navigation_start.html
   file_iframe_sandbox_open_window_fail.html
+  file_iframe_sandbox_open_window_pass.html
   file_iframe_sandbox_pass.js
   file_iframe_sandbox_top_navigation_fail.html
   file_iframe_sandbox_top_navigation_pass.html
+  file_iframe_sandbox_window_form_fail.html
+  file_iframe_sandbox_window_form_pass.html
   file_iframe_sandbox_window_navigation_fail.html
+  file_iframe_sandbox_window_navigation_pass.html
+  file_iframe_sandbox_window_top_navigation_pass.html
+  file_iframe_sandbox_window_top_navigation_fail.html
   file_iframe_sandbox_worker.js
   file_srcdoc-2.html
   file_srcdoc.html
   form_submit_server.sjs
   image.png
   image-allow-credentials.png
   image-allow-credentials.png^headers^
   nnc_lockup.gif
@@ -356,19 +377,22 @@ support-files =
 [test_formSubmission2.html]
 [test_formelements.html]
 [test_fullscreen-api.html]
 [test_hidden.html]
 [test_html_attributes_reflection.html]
 [test_htmlcollection.html]
 [test_iframe_sandbox_general.html]
 [test_iframe_sandbox_inheritance.html]
+[test_iframe_sandbox_modal.html]
 [test_iframe_sandbox_navigation.html]
 [test_iframe_sandbox_navigation2.html]
 [test_iframe_sandbox_plugins.html]
+[test_iframe_sandbox_popups.html]
+[test_iframe_sandbox_popups_inheritance.html]
 [test_iframe_sandbox_same_origin.html]
 [test_iframe_sandbox_workers.html]
 [test_img_attributes_reflection.html]
 [test_li_attributes_reflection.html]
 [test_link_attributes_reflection.html]
 [test_map_attributes_reflection.html]
 [test_meta_attributes_reflection.html]
 [test_mod_attributes_reflection.html]
--- a/content/html/content/test/test_iframe_sandbox_general.html
+++ b/content/html/content/test/test_iframe_sandbox_general.html
@@ -1,234 +1,239 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=341604
-Implement HTML5 sandbox attribute for IFRAMEs - general tests
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 341604</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<script type="application/javascript">
-
-SimpleTest.expectAssertions(1);
-
-/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs - general tests **/
-
-SimpleTest.waitForExplicitFinish();
-
-// a postMessage handler that is used by sandboxed iframes without
-// 'allow-same-origin' to communicate pass/fail back to this main page.
-// it expects to be called with an object like {ok: true/false, desc:
-// <description of the test> which it then forwards to ok()
-window.addEventListener("message", receiveMessage, false);
-
-function receiveMessage(event)
-{
-  ok_wrapper(event.data.ok, event.data.desc);
-}
-
-var completedTests = 0;
-var passedTests = 0;
-
-function ok_wrapper(result, desc) {
-  ok(result, desc);
-
-  completedTests++;
-
-  if (result) {
-    passedTests++;
-  }
-
-  if (completedTests == 23) {
-    is(passedTests, 23, "There are 23 general tests that should pass");
-    SimpleTest.finish();
-  }
-}
-
-function doTest() {
-  // passes if good
-  // 1) test that inline scripts (<script>) can run in an iframe sandboxed with "allow-scripts"
-  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
-
-  // passes if good
-  // 2) test that <script src=...> can run in an iframe sandboxed with "allow-scripts"
-  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
-
-  // passes if good
-  // 3) test that script in an event listener (body onload) can run in an iframe sandboxed with "allow-scripts"
-  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
-
-  // passes if good
-  // 4) test that script in an javascript:url can run in an iframe sandboxed with "allow-scripts"
-  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
-
-  // fails if bad
-  // 5) test that inline scripts cannot run in an iframe sandboxed without "allow-scripts"
-  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
-
-  // fails if bad
-  // 6) test that <script src=...> cannot run in an iframe sandboxed without "allow-scripts"
-  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
-
-  // fails if bad
-  // 7) test that script in an event listener (body onload) cannot run in an iframe sandboxed without "allow-scripts"
-  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
-
-  // fails if bad
-  // 8) test that script in an event listener (img onerror) cannot run in an iframe sandboxed without "allow-scripts"
-  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
-  
-  // fails if bad
-  // 9) test that script in an javascript:url cannot run in an iframe sandboxed without "allow-scripts"
-  // (done in file_iframe_sandbox_c_if_5.html which has sandbox='allow-same-origin')
-  var if_w = document.getElementById('if_5').contentWindow;
-  sendMouseEvent({type:'click'}, 'a_link', if_w);
-
-  // passes if good
-  // 10) test that a new iframe has sandbox attribute
-  var ifr = document.createElement("iframe");
-  ok_wrapper("sandbox" in ifr, "a new iframe should have a sandbox attribute");
-
-  // passes if good
-  // 11) test that the sandbox attribute's default value is an empty string
-  ok_wrapper(ifr.sandbox === "", "default sandbox attribute should be an empty string");
-
-  // passes if good
-  // 12) test that a sandboxed iframe with 'allow-forms' can submit forms
-  // (done in file_iframe_sandbox_c_if3.html which has 'allow-forms' and 'allow-scripts')
-
-  // fails if bad
-  // 13) test that a sandboxed iframe without 'allow-forms' can NOT submit forms
-  // (done in file_iframe_sandbox_c_if1.html which only has 'allow-scripts')
-
-  // fails if bad
-  // 14) test that a sandboxed iframe can't open a new window using the target.attribute
-  // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
-  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
-  // function that calls window.parent.ok_wrapper
-
-  // passes if good
-  // 15) test that a sandboxed iframe can't open a new window using window.open
-  // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
-  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
-  // function that calls window.parent.ok_wrapper
-
-  // passes if good
-  // 16) test that a sandboxed iframe can't open a new window using window.ShowModalDialog
-  // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
-  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
-  // function that calls window.parent.ok_wrapper
-  
-  // passes twice if good
-  // 17) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
-  // is separated with two spaces 
-  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "  allow-scripts  allow-same-origin  "
-  
-  // passes twice if good
-  // 18) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
-  // is separated with tabs
-  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x09;allow-scripts&#x09;allow-same-origin&#x09;"
-  
-  // passes twice if good
-  // 19) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
-  // is separated with line feeds
-  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0a;allow-scripts&#x0a;allow-same-origin&#x0a;"
-  
-  // passes twice if good
-  // 20) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
-  // is separated with form feeds
-  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0c;allow-scripts&#x0c;allow-same-origin&#x0c;"
-  
-  // passes twice if good
-  // 21) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
-  // is separated with carriage returns
-  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0d;allow-scripts&#x0d;allow-same-origin&#x0d;"
-
-  // fails if bad
-  // 22) test that an iframe with sandbox="" does NOT have script in a src attribute created by a javascript: 
-  // URL executed
-  // done by this page, see if_7
-
-  // passes if good
-  // 23) test that an iframe with sandbox="allow-scripts" DOES have script in a src attribute created by a javascript: 
-  // URL executed
-  // done by this page, see if_8
-
-  // fails if bad
-  // 24) test that an iframe with sandbox="", starting out with a document already loaded, does NOT have script in a newly
-  // set src attribute created by a javascript: URL executed
-  // done by this page, see if_9
-
-  // passes if good
-  // 25) test that an iframe with sandbox="allow-scripts", starting out with a document already loaded, DOES have script
-  // in a newly set src attribute created by a javascript: URL executed
-  // done by this page, see if_10
-
-  // passes if good or fails if bad
-  // 26) test that an sandboxed document without 'allow-same-origin' can NOT access indexedDB
-  // done via file_iframe_sandbox_c_if7.html, which has sandbox='allow-scripts'
-
-  // passes if good or fails if bad
-  // 26) test that an sandboxed document without 'allow-same-origin' can access indexedDB
-  // done via file_iframe_sandbox_c_if8.html, which has sandbox='allow-scripts allow-same-origin'
-}
-
-addLoadEvent(doTest);
-
-var started_if_9 = false;
-var started_if_10 = false;
-
-function start_if_9() {
-  if (started_if_9)
-    return;
-
-  started_if_9 = true;
-  sendMouseEvent({type:'click'}, 'a_button');
-}
-
-function start_if_10() {
-  if (started_if_10)
-    return;
-
-  started_if_10 = true;
-  sendMouseEvent({type:'click'}, 'a_button2');
-}
-
-function do_if_9() {
-  var if_9 = document.getElementById('if_9');
-  if_9.src = 'javascript:"<html><script>window.parent.ok_wrapper(false, \'an iframe sandboxed without allow-scripts should not execute script in a javascript URL in a newly set src attribute\');<\/script><\/html>"';
-}
-
-function do_if_10() {
-  var if_10 = document.getElementById('if_10');
-  if_10.src = 'javascript:"<html><script>window.parent.ok_wrapper(true, \'an iframe sandboxed with allow-scripts should execute script in a javascript URL in a newly set src attribute\');<\/script><\/html>"';
-}
-</script>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
-<p id="display"></p>
-<div id="content">
-<iframe sandbox="allow-same-origin allow-scripts" id="if_1" src="file_iframe_sandbox_c_if1.html" height="10" width="10"></iframe>
-<iframe sandbox="" id="if_2" src="file_iframe_sandbox_c_if2.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-forms allow-scripts" id="if_3" src="file_iframe_sandbox_c_if3.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin allow-scripts" id="if_4" src="file_iframe_sandbox_c_if4.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin" id="if_5" src="file_iframe_sandbox_c_if5.html" height="10" width="10"></iframe>
-<iframe sandbox="  allow-same-origin  allow-scripts  " id="if_6_a" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
-<iframe sandbox="&#x09;allow-same-origin&#x09;allow-scripts&#x09;" id="if_6_b" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
-<iframe sandbox="&#x0a;allow-same-origin&#x0a;allow-scripts&#x0a;" id="if_6_c" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
-<iframe sandbox="&#x0c;allow-same-origin&#x0c;allow-scripts&#x0c;" id="if_6_d" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
-<iframe sandbox="&#x0d;allow-same-origin&#x0d;allow-scripts&#x0d;" id="if_6_e" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin" id='if_7' src="javascript:'<html><script>window.parent.ok_wrapper(false, \'an iframe sandboxed without allow-scripts should not execute script in a javascript URL in its src attribute\');<\/script><\/html>';" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin allow-scripts" id='if_8' src="javascript:'<html><script>window.parent.ok_wrapper(true, \'an iframe sandboxed without allow-scripts should execute script in a javascript URL in its src attribute\');<\/script><\/html>';" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin" onload='start_if_9()' id='if_9' src="about:blank" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin allow-scripts" onload='start_if_10()' id='if_10' src="about:blank" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts" id='if_11' src="file_iframe_sandbox_c_if7.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin allow-scripts" id='if_12' src="file_iframe_sandbox_c_if8.html" height="10" width="10"></iframe>
-<input type='button' id="a_button" onclick='do_if_9()'>
-<input type='button' id="a_button2" onclick='do_if_10()'>
-</div>
-</body>
-</html>
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs - general tests
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Bug 341604 and Bug 766282</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs - general tests **/
+
+SimpleTest.expectAssertions(0, 1);
+SimpleTest.waitForExplicitFinish();
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+  ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+  ok(result, desc);
+
+  completedTests++;
+
+  if (result) {
+    passedTests++;
+  }
+
+  if (completedTests == 23) {
+    is(passedTests, completedTests, "There are " + completedTests + " general tests that should pass");
+    SimpleTest.finish();
+  }
+}
+
+function doTest() {
+  // passes if good
+  // 1) test that inline scripts (<script>) can run in an iframe sandboxed with "allow-scripts"
+  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+  // passes if good
+  // 2) test that <script src=...> can run in an iframe sandboxed with "allow-scripts"
+  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+  // passes if good
+  // 3) test that script in an event listener (body onload) can run in an iframe sandboxed with "allow-scripts"
+  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+  // passes if good
+  // 4) test that script in an javascript:url can run in an iframe sandboxed with "allow-scripts"
+  // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+  // fails if bad
+  // 5) test that inline scripts cannot run in an iframe sandboxed without "allow-scripts"
+  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+  // fails if bad
+  // 6) test that <script src=...> cannot run in an iframe sandboxed without "allow-scripts"
+  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+  // fails if bad
+  // 7) test that script in an event listener (body onload) cannot run in an iframe sandboxed without "allow-scripts"
+  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+  // fails if bad
+  // 8) test that script in an event listener (img onerror) cannot run in an iframe sandboxed without "allow-scripts"
+  // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+  // fails if bad
+  // 9) test that script in an javascript:url cannot run in an iframe sandboxed without "allow-scripts"
+  // (done in file_iframe_sandbox_c_if_5.html which has sandbox='allow-same-origin')
+  var if_w = document.getElementById('if_5').contentWindow;
+  sendMouseEvent({type:'click'}, 'a_link', if_w);
+
+  // passes if good
+  // 10) test that a new iframe has sandbox attribute
+  var ifr = document.createElement("iframe");
+  ok_wrapper("sandbox" in ifr, "a new iframe should have a sandbox attribute");
+
+  // passes if good
+  // 11) test that the sandbox attribute's default value is an empty string
+  ok_wrapper(ifr.sandbox === "", "default sandbox attribute should be an empty string");
+
+  // passes if good
+  // 12) test that a sandboxed iframe with 'allow-forms' can submit forms
+  // (done in file_iframe_sandbox_c_if3.html which has 'allow-forms' and 'allow-scripts')
+
+  // fails if bad
+  // 13) test that a sandboxed iframe without 'allow-forms' can NOT submit forms
+  // (done in file_iframe_sandbox_c_if1.html which only has 'allow-scripts')
+
+  // fails if bad
+  // 14) test that a sandboxed iframe can't open a new window using the target.attribute
+  // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+  // function that calls window.parent.ok_wrapper
+
+  // passes if good
+  // 15) test that a sandboxed iframe can't open a new window using window.open
+  // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+  // function that calls window.parent.ok_wrapper
+
+  // passes if good
+  // 16) test that a sandboxed iframe can't open a new window using window.ShowModalDialog
+  // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+  // function that calls window.parent.ok_wrapper
+
+  // passes twice if good
+  // 17) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+  // is separated with two spaces
+  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "  allow-scripts  allow-same-origin  "
+
+  // passes twice if good
+  // 18) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+  // is separated with tabs
+  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x09;allow-scripts&#x09;allow-same-origin&#x09;"
+
+  // passes twice if good
+  // 19) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+  // is separated with line feeds
+  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0a;allow-scripts&#x0a;allow-same-origin&#x0a;"
+
+  // passes twice if good
+  // 20) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+  // is separated with form feeds
+  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0c;allow-scripts&#x0c;allow-same-origin&#x0c;"
+
+  // passes twice if good
+  // 21) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+  // is separated with carriage returns
+  // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0d;allow-scripts&#x0d;allow-same-origin&#x0d;"
+
+  // fails if bad
+  // 22) test that an iframe with sandbox="" does NOT have script in a src attribute created by a javascript:
+  // URL executed
+  // done by this page, see if_7
+
+  // passes if good
+  // 23) test that an iframe with sandbox="allow-scripts" DOES have script in a src attribute created by a javascript:
+  // URL executed
+  // done by this page, see if_8
+
+  // fails if bad
+  // 24) test that an iframe with sandbox="", starting out with a document already loaded, does NOT have script in a newly
+  // set src attribute created by a javascript: URL executed
+  // done by this page, see if_9
+
+  // passes if good
+  // 25) test that an iframe with sandbox="allow-scripts", starting out with a document already loaded, DOES have script
+  // in a newly set src attribute created by a javascript: URL executed
+  // done by this page, see if_10
+
+  // passes if good or fails if bad
+  // 26) test that an sandboxed document without 'allow-same-origin' can NOT access indexedDB
+  // done via file_iframe_sandbox_c_if7.html, which has sandbox='allow-scripts'
+
+  // passes if good or fails if bad
+  // 27) test that an sandboxed document with 'allow-same-origin' can access indexedDB
+  // done via file_iframe_sandbox_c_if8.html, which has sandbox='allow-scripts allow-same-origin'
+
+  // fails if bad
+  // 28) Test that a sandboxed iframe can't open a new window using the target.attribute for a
+  // non-existing browsing context (BC341604).
+  // This is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+  // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+  // function that calls window.parent.ok_wrapper.
+}
+
+addLoadEvent(doTest);
+
+var started_if_9 = false;
+var started_if_10 = false;
+
+function start_if_9() {
+  if (started_if_9)
+    return;
+
+  started_if_9 = true;
+  sendMouseEvent({type:'click'}, 'a_button');
+}
+
+function start_if_10() {
+  if (started_if_10)
+    return;
+
+  started_if_10 = true;
+  sendMouseEvent({type:'click'}, 'a_button2');
+}
+
+function do_if_9() {
+  var if_9 = document.getElementById('if_9');
+  if_9.src = 'javascript:"<html><script>window.parent.ok_wrapper(false, \'an iframe sandboxed without allow-scripts should not execute script in a javascript URL in a newly set src attribute\');<\/script><\/html>"';
+}
+
+function do_if_10() {
+  var if_10 = document.getElementById('if_10');
+  if_10.src = 'javascript:"<html><script>window.parent.ok_wrapper(true, \'an iframe sandboxed with allow-scripts should execute script in a javascript URL in a newly set src attribute\');<\/script><\/html>"';
+}
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-same-origin allow-scripts" id="if_1" src="file_iframe_sandbox_c_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="" id="if_2" src="file_iframe_sandbox_c_if2.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-forms allow-scripts" id="if_3" src="file_iframe_sandbox_c_if3.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id="if_4" src="file_iframe_sandbox_c_if4.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_5" src="file_iframe_sandbox_c_if5.html" height="10" width="10"></iframe>
+<iframe sandbox="  allow-same-origin  allow-scripts  " id="if_6_a" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x09;allow-same-origin&#x09;allow-scripts&#x09;" id="if_6_b" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x0a;allow-same-origin&#x0a;allow-scripts&#x0a;" id="if_6_c" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x0c;allow-same-origin&#x0c;allow-scripts&#x0c;" id="if_6_d" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x0d;allow-same-origin&#x0d;allow-scripts&#x0d;" id="if_6_e" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id='if_7' src="javascript:'<html><script>window.parent.ok_wrapper(false, \'an iframe sandboxed without allow-scripts should not execute script in a javascript URL in its src attribute\');<\/script><\/html>';" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id='if_8' src="javascript:'<html><script>window.parent.ok_wrapper(true, \'an iframe sandboxed without allow-scripts should execute script in a javascript URL in its src attribute\');<\/script><\/html>';" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" onload='start_if_9()' id='if_9' src="about:blank" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" onload='start_if_10()' id='if_10' src="about:blank" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id='if_11' src="file_iframe_sandbox_c_if7.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id='if_12' src="file_iframe_sandbox_c_if8.html" height="10" width="10"></iframe>
+<input type='button' id="a_button" onclick='do_if_9()'>
+<input type='button' id="a_button2" onclick='do_if_10()'>
+</div>
+</body>
+</html>
--- a/content/html/content/test/test_iframe_sandbox_inheritance.html
+++ b/content/html/content/test/test_iframe_sandbox_inheritance.html
@@ -1,168 +1,205 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=341604
-Implement HTML5 sandbox attribute for IFRAMEs - inheritance tests
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 341604</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<script type="application/javascript">
-/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
-/** Inheritance Tests **/
-
-// Assertion failure in docshell/shistory/src/nsSHEntry.cpp (currently line 625).
-// Bug 901876 raised.
-SimpleTest.expectAssertions(1);
-SimpleTest.waitForExplicitFinish();
-  
-// A postMessage handler that is used by sandboxed iframes without
-// 'allow-same-origin' to communicate pass/fail back to this main page.
-// It expects to be called with an object like {ok: true/false, desc:
-// <description of the test> which it then forwards to ok().
-window.addEventListener("message", receiveMessage, false);
-
-var completedTests = 0;
-var passedTests = 0;
-
-function receiveMessage(event)
-{
-  ok_wrapper(event.data.ok, event.data.desc);
-}
-
-function ok_wrapper(result, desc) {
-  ok(result, desc);
-
-  completedTests++;
-
-  if (result) {
-	  passedTests++;
-  } 
-
-  if (completedTests == 13) {
-    is(passedTests, completedTests, "there should be " + completedTests + " passed inheritance tests");
-    SimpleTest.finish();
-  }
-}
-
-function doTest() {
-  // fails if bad
-  // 1) an iframe with no sandbox attribute inside an iframe that has sandbox = ""
-  // should not be able to execute scripts (cannot ever loosen permissions)
-  // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if1.html)
-
-  // fails if bad
-  // 2) an iframe with sandbox = "allow-scripts" inside an iframe that has sandbox = ""
-  // should not be able to execute scripts (cannot ever loosen permissions)
-  // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if1.html)
-
-  // fails if bad
-  // 3) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
-  // should not be same origin with the top window
-  // (done by file_iframe_sandbox_a_if4.html contained within file_iframe_sandbox_a_if3.html)
-
-  // fails if bad
-  // 4) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
-  // should not be same origin with its parent
-  // (done by file_iframe_sandbox_a_if4.html contained within file_iframe_sandbox_a_if3.html)
-
-  // passes if good
-  // 5) an iframe with 'allow-same-origin' and 'allow-scripts' inside an iframe with 'allow-same-origin'
-  // and 'allow-scripts' should be same origin with the top window
-  // (done by file_iframe_sandbox_a_if6.html contained within file_iframe_sandbox_a_if5.html)
-
-  // passes if good
-  // 6) an iframe with 'allow-same-origin' and 'allow-scripts' inside an iframe with 'allow-same-origin'
-  // and 'allow-scripts' should be same origin with its parent
-  // (done by file_iframe_sandbox_a_if6.html contained within file_iframe_sandbox_a_if5.html)
-
-  // passes if good
-  // 7) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
-  // should be able to execute scripts
-  // (done by file_iframe_sandbox_a_if7.html contained within file_iframe_sandbox_a_if3.html)
-
-  // fails if bad
-  // 8) an iframe with sandbox="" inside an iframe that has allow-scripts should not be able
-  // to execute scripts
-  // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if3.html)
-
-  // passes if good
-  // 9) make sure that changing the sandbox flags on an iframe (if_8) doesn't affect
-  // the sandboxing of subloads of content within that iframe
-  var if_8 = document.getElementById('if_8');
-  if_8.sandbox = 'allow-scripts';
-  if_8.contentWindow.doSubload();
-
-  // passes if good
-  // 10) a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
-  // origin with this document
-  // done by file_iframe_sandbox_a_if11.html which is contained with file_iframe_sandbox_a_if10.html
-
-  // passes if good
-  // 11) a <frame> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
-  // origin with its parent frame or this document
-  // done by file_iframe_sandbox_a_if12.html which is contained with file_iframe_sandbox_a_if11.html
-
-  // passes if good, fails if bad
-  // 12) An <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
-  // origin with this document
-  // Done by file_iframe_sandbox_a_if14.html which is contained within file_iframe_sandbox_a_if13.html
-
-  // passes if good, fails if bad
-  // 13) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
-  // origin with its parent frame or this document
-  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
-
-  // passes if good, fails if bad
-  // 14) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
-  // origin with its parent frame or this document
-  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
-  // which is contained within file_iframe_sandbox_a_if10.html
-
-  // passes if good
-  // 15) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
-  // should be able to submit forms.
-  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
-
-  // passes if good
-  // 16) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
-  // should be able to submit forms.
-  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
-  // which is contained within file_iframe_sandbox_a_if10.html
-
-  // fails if bad
-  // 17) An <object> inside an <iframe> sandboxed with 'allow-same-origin'
-  // should not be able to run scripts.
-  // Done by iframe "if_no_scripts" using a data: load.
-
-  // passes if good
-  // 18) An <object> inside an <iframe> sandboxed with 'allow-scripts allow-same-origin'
-  // should be able to run scripts and be same origin with this document.
-  // Done by iframe "if_scripts" using a data: load.
-
-  // passes if good, fails if bad
-  // 19) Make sure that the parent's document's sandboxing flags are copied when
-  // changing the sandbox flags on an iframe inside an iframe.
-  // Done in file_iframe_sandbox_a_if17.html and file_iframe_sandbox_a_if18.html
-}
-
-addLoadEvent(doTest);
-</script>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
-<p id="display"></p>
-<div id="content">
-<iframe sandbox="" id="if_1" src="file_iframe_sandbox_a_if1.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts" id="if_3" src="file_iframe_sandbox_a_if3.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts allow-same-origin" id="if_5" src="file_iframe_sandbox_a_if5.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts allow-same-origin" id="if_8" src="file_iframe_sandbox_a_if8.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts allow-forms" id="if_10" src="file_iframe_sandbox_a_if10.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts allow-forms" id="if_13" src="file_iframe_sandbox_a_if13.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin" id="if_no_scripts" src="data:text/html,<object%20data='data:text/html,<script>parent.parent.ok_wrapper(false, &quot;an object inside an iframe sandboxed with only allow-same-origin should not be able to run scripts&quot;)</script>'></object>" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts allow-same-origin" id="if_scripts" src="data:text/html,<object%20data='data:text/html,<script>parent.parent.ok_wrapper(true, &quot;an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe&quot;)</script>'></object>" height="10" width="10"></iframe>
-<iframe sandbox="allow-same-origin" id="if_19" src="data:text/html,<iframe%20data='data:text/html,<script>parent.parent.ok_wrapper(true, &quot;an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe&quot;)</script>'></object>" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts" id="if_17" src="file_iframe_sandbox_a_if17.html" height="10" width="10"></iframe>
-</div>
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs - inheritance tests
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 341604</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
+/** Inheritance Tests **/
+
+// Assertion failure in docshell/shistory/src/nsSHEntry.cpp (currently line 625).
+// Bug 901876 raised.
+SimpleTest.expectAssertions(1);
+SimpleTest.waitForExplicitFinish();
+
+// A postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// It expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok().
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+  switch (event.data.type) {
+    case "attempted":
+      testAttempted();
+      break;
+    case "ok":
+      ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      break;
+    default:
+      // allow for old style message
+      if (event.data.ok != undefined) {
+        ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      }
+  }
+}
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 15;
+var totalTestsToAttempt = 19;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+  ok(result, desc);
+
+  if (result) {
+    passedTests++;
+  }
+
+  if (addToAttempted) {
+    testAttempted();
+  }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+  attemptedTests++;
+  if (attemptedTests == totalTestsToAttempt) {
+    // Make sure all tests have had a chance to complete.
+    setTimeout(function() {finish();}, 1000);
+  }
+}
+
+var finishCalled = false;
+
+function finish() {
+  if (!finishCalled) {
+    finishCalled = true;
+    is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " inheritance tests that should pass");
+
+    SimpleTest.finish();
+  }
+}
+
+function doTest() {
+  // fails if bad
+  // 1) an iframe with no sandbox attribute inside an iframe that has sandbox = ""
+  // should not be able to execute scripts (cannot ever loosen permissions)
+  // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if1.html)
+  testAttempted();
+
+  // fails if bad
+  // 2) an iframe with sandbox = "allow-scripts" inside an iframe that has sandbox = ""
+  // should not be able to execute scripts (cannot ever loosen permissions)
+  // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if1.html)
+  testAttempted();
+
+  // passes if good and fails if bad
+  // 3) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
+  // should not be same origin with the top window
+  // (done by file_iframe_sandbox_a_if4.html contained within file_iframe_sandbox_a_if3.html)
+
+  // passes if good and fails if bad
+  // 4) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
+  // should not be same origin with its parent
+  // (done by file_iframe_sandbox_a_if4.html contained within file_iframe_sandbox_a_if3.html)
+
+  // passes if good
+  // 5) an iframe with 'allow-same-origin' and 'allow-scripts' inside an iframe with 'allow-same-origin'
+  // and 'allow-scripts' should be same origin with the top window
+  // (done by file_iframe_sandbox_a_if6.html contained within file_iframe_sandbox_a_if5.html)
+
+  // passes if good
+  // 6) an iframe with 'allow-same-origin' and 'allow-scripts' inside an iframe with 'allow-same-origin'
+  // and 'allow-scripts' should be same origin with its parent
+  // (done by file_iframe_sandbox_a_if6.html contained within file_iframe_sandbox_a_if5.html)
+
+  // passes if good
+  // 7) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
+  // should be able to execute scripts
+  // (done by file_iframe_sandbox_a_if7.html contained within file_iframe_sandbox_a_if3.html)
+
+  // fails if bad
+  // 8) an iframe with sandbox="" inside an iframe that has allow-scripts should not be able
+  // to execute scripts
+  // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if3.html)
+  testAttempted();
+
+  // passes if good
+  // 9) make sure that changing the sandbox flags on an iframe (if_8) doesn't affect
+  // the sandboxing of subloads of content within that iframe
+  var if_8 = document.getElementById('if_8');
+  if_8.sandbox = 'allow-scripts';
+  if_8.contentWindow.doSubload();
+
+  // passes if good
+  // 10) a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with this document
+  // done by file_iframe_sandbox_a_if11.html which is contained with file_iframe_sandbox_a_if10.html
+
+  // passes if good
+  // 11) a <frame> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with its parent frame or this document
+  // done by file_iframe_sandbox_a_if12.html which is contained with file_iframe_sandbox_a_if11.html
+
+  // passes if good, fails if bad
+  // 12) An <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with this document
+  // Done by file_iframe_sandbox_a_if14.html which is contained within file_iframe_sandbox_a_if13.html
+
+  // passes if good, fails if bad
+  // 13) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with its parent frame or this document
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
+
+  // passes if good, fails if bad
+  // 14) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with its parent frame or this document
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
+  // which is contained within file_iframe_sandbox_a_if10.html
+
+  // passes if good
+  // 15) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
+  // should be able to submit forms.
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
+
+  // passes if good
+  // 16) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
+  // should be able to submit forms.
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
+  // which is contained within file_iframe_sandbox_a_if10.html
+
+  // fails if bad
+  // 17) An <object> inside an <iframe> sandboxed with 'allow-same-origin'
+  // should not be able to run scripts.
+  // Done by iframe "if_no_scripts" using a data: load.
+  testAttempted();
+
+  // passes if good
+  // 18) An <object> inside an <iframe> sandboxed with 'allow-scripts allow-same-origin'
+  // should be able to run scripts and be same origin with this document.
+  // Done by iframe "if_scripts" using a data: load.
+
+  // passes if good, fails if bad
+  // 19) Make sure that the parent's document's sandboxing flags are copied when
+  // changing the sandbox flags on an iframe inside an iframe.
+  // Done in file_iframe_sandbox_a_if17.html and file_iframe_sandbox_a_if18.html
+}
+
+addLoadEvent(doTest);
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="" id="if_1" src="file_iframe_sandbox_a_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_3" src="file_iframe_sandbox_a_if3.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_5" src="file_iframe_sandbox_a_if5.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_8" src="file_iframe_sandbox_a_if8.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-forms" id="if_10" src="file_iframe_sandbox_a_if10.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-forms" id="if_13" src="file_iframe_sandbox_a_if13.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_no_scripts" src="data:text/html,<object%20data='data:text/html,<script>parent.parent.ok_wrapper(false, &quot;an object inside an iframe sandboxed with only allow-same-origin should not be able to run scripts&quot;)</script>'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_scripts" src="data:text/html,<object%20data='data:text/html,<script>parent.parent.ok_wrapper(true, &quot;an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe&quot;)</script>'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_19" src="data:text/html,<iframe%20data='data:text/html,<script>parent.parent.ok_wrapper(true, &quot;an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe&quot;)</script>'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_17" src="file_iframe_sandbox_a_if17.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_iframe_sandbox_modal.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=766282
+implement allow-popups directive for iframe sandbox
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Bug 766282</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+// A postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+  switch (event.data.type) {
+    case "attempted":
+      testAttempted();
+      break;
+    case "ok":
+      ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      break;
+    default:
+      // allow for old style message
+      if (event.data.ok != undefined) {
+        ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      }
+  }
+}
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 5;
+var totalTestsToAttempt = 5;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+  ok(result, desc);
+
+  if (result) {
+    passedTests++;
+  }
+
+  if (addToAttempted) {
+    testAttempted();
+  }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+  attemptedTests++;
+  if (attemptedTests == totalTestsToAttempt) {
+    // Make sure all tests have had a chance to complete.
+    setTimeout(function() {finish();}, 1000);
+  }
+}
+
+var finishCalled = false;
+
+function finish() {
+  if (!finishCalled) {
+    finishCalled = true;
+    is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " modal tests that should pass");
+
+    SimpleTest.finish();
+  }
+}
+
+function doTest() {
+  // passes if good and fails if bad
+  // 1) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+  // allow-same-origin" should not have its origin sandbox flag set and be able to access
+  // document.cookie. (Done by file_iframe_sandbox_k_if5.html opened from
+  // file_iframe_sandbox_j_if1.html) using showModalDialog.)
+
+  // passes if good
+  // 2) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+  // allow-top-navigation" should not have its top-level navigation sandbox flag set and be able to
+  // navigate top. (Done by file_iframe_sandbox_k_if5.html (and if6) opened from
+  // file_iframe_sandbox_j_if1.html) using showModalDialog.)
+
+  // passes if good
+  // 3) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+  // all-forms" should not have its forms sandbox flag set and be able to submit forms.
+  // (Done by file_iframe_sandbox_k_if7.html opened from
+  // file_iframe_sandbox_j_if1.html) using showModalDialog.)
+
+  // passes if good
+  // 4) Make sure that the sandbox flags copied to a new browsing context are taken from the
+  // current active document not the browsing context (iframe / docShell).
+  // This is done by removing allow-same-origin and calling doSubOpens from file_iframe_sandbox_j_if2.html,
+  // which opens file_iframe_sandbox_k_if9.html using showModalDialog.
+  var if_2 = document.getElementById('if_2');
+  if_2.sandbox = 'allow-scripts allow-popups';
+  if_2.contentWindow.doSubOpens();
+
+  // passes if good
+  // 5) Test that a sandboxed iframe with "allow-popups" can open a new window using window.ShowModalDialog.
+  // This is done via file_iframe_sandbox_j_if3.html which is sandboxed with "allow-popups allow-scripts
+  // allow-same-origin". The window it attempts to open calls window.opener.ok(true, ...) and
+  // file_iframe_j_if3.html has an ok() function that calls window.parent.ok_wrapper.
+}
+
+addLoadEvent(doTest);
+</script>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=766282">Mozilla Bug 766282</a> - implement allow-popups directive for iframe sandbox
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-scripts allow-popups allow-same-origin allow-forms allow-top-navigation" id="if_1" src="file_iframe_sandbox_j_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-popups allow-same-origin" id="if_2" src="file_iframe_sandbox_j_if2.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-popups allow-same-origin allow-scripts" id="if_3" src="file_iframe_sandbox_j_if3.html" height="10" width="10"></iframe>
+</div>
--- a/content/html/content/test/test_iframe_sandbox_navigation.html
+++ b/content/html/content/test/test_iframe_sandbox_navigation.html
@@ -21,30 +21,36 @@ SimpleTest.waitForExplicitFinish();
 // 'allow-same-origin'/other windows to communicate pass/fail back to this main page.
 // it expects to be called with an object like {ok: true/false, desc:
 // <description of the test> which it then forwards to ok()
 window.addEventListener("message", receiveMessage, false);
 
 var testPassesReceived = 0;
 
 function receiveMessage(event) {
-  // this message is part of if_10's test
-  if (event.data.test == 'if_10') {
-    doIf10TestPart2();
-  } else if (event.data == "test attempted") {
-    testAttempted();
-  } else {
-    ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+  switch (event.data.type) {
+    case "attempted":
+      testAttempted();
+      break;
+    case "ok":
+      ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      break;
+    case "if_10":
+      doIf10TestPart2();
+      break;
+    default:
+      // allow for old style message
+      if (event.data.ok != undefined) {
+        ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      }
   }
 }
 
 // Open windows for tests to attempt to navigate later.
 var windowsToClose = new Array();
-windowsToClose.push(window.open("about:blank", "window_to_navigate"));
-windowsToClose.push(window.open("about:blank", "window_to_navigate2"));
 
 var attemptedTests = 0;
 var passedTests = 0;
 var totalTestsToPass = 7;
 var totalTestsToAttempt = 13;
 
 function ok_wrapper(result, desc, addToAttempted = true) {
   ok(result, desc);
@@ -70,30 +76,32 @@ function testAttempted() {
 
 var finishCalled = false;
 
 function finish() {
   if (!finishCalled) {
     finishCalled = true;
     is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " navigation tests that should pass");
 
-    for (var i = 0; i < windowsToClose.length; i++) {
-      windowsToClose[i].close();
-    }
+    closeWindows();
 
     SimpleTest.finish();
   }
 }
 
 function checkTestsFinished() {
   // If our own finish() has not been called, probably failed due to a timeout, so close remaining windows.
   if (!finishCalled) {
-    for (var i = 0; i < windowsToClose.length; i++) {
-      windowsToClose[i].close();
-    }
+    closeWindows();
+  }
+}
+
+function closeWindows() {
+  for (var i = 0; i < windowsToClose.length; i++) {
+    windowsToClose[i].close();
   }
 }
 
 function doTest() {
   // passes if good
   // 1) A sandboxed iframe is allowed to navigate itself
   // (done by file_iframe_sandbox_d_if1.html which has 'allow-scripts' and navigates to
   // file_iframe_sandbox_navigation_pass.html).
--- a/content/html/content/test/test_iframe_sandbox_navigation2.html
+++ b/content/html/content/test/test_iframe_sandbox_navigation2.html
@@ -21,32 +21,41 @@ SimpleTest.waitForExplicitFinish();
 // 'allow-same-origin'/other windows to communicate pass/fail back to this main page.
 // it expects to be called with an object like {ok: true/false, desc:
 // <description of the test> which it then forwards to ok()
 window.addEventListener("message", receiveMessage, false);
 
 var testPassesReceived = 0;
 
 function receiveMessage(event) {
-  if (event.data == "test attempted") {
-    testAttempted();
-  } else {
-    ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+  switch (event.data.type) {
+    case "attempted":
+      testAttempted();
+      break;
+    case "ok":
+      ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      break;
+    default:
+      // allow for old style message
+      if (event.data.ok != undefined) {
+        ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      }
   }
 }
 
 // Open windows for tests to attempt to navigate later.
 var windowsToClose = new Array();
 windowsToClose.push(window.open("about:blank", "window_to_navigate"));
 windowsToClose.push(window.open("about:blank", "window_to_navigate2"));
+var iframesWithWindowsToClose = new Array();
 
 var attemptedTests = 0;
 var passedTests = 0;
-var totalTestsToPass = 10;
-var totalTestsToAttempt = 13;
+var totalTestsToPass = 12;
+var totalTestsToAttempt = 15;
 
 function ok_wrapper(result, desc, addToAttempted = true) {
   ok(result, desc);
 
   if (result) {
     passedTests++;
   }
 
@@ -163,25 +172,39 @@ function doTest() {
   window.open("file_iframe_sandbox_e_if13.html");
 
   // passes if good, fails if bad
   // 26) iframe with sandbox='allow-scripts' should not be able to navigate top using its real name
   // (not with _top e.g. window.open(..., "topname")) as allow-top-navigation is not specified.
   // file_iframe_sandbox_e_if15.html contains file_iframe_sandbox_e_if16.html, which
   // attempts to navigate top by name using window.open().
   window.open("file_iframe_sandbox_e_if15.html");
+
+  // passes if good
+  // 27) iframe with sandbox='allow-scripts allow-popups' should be able to
+  // navigate a window, that it has opened, using it's name.
+  // file_iframe_sandbox_d_if23.html in if_23 opens a window and then attempts
+  // to navigate it using it's name in the target of an anchor.
+  iframesWithWindowsToClose.push("if_23");
+
+  // passes if good, fails if bad
+  // 28) iframe with sandbox='allow-scripts allow-popups' should be able to
+  // navigate a window, that it has opened, using window.open(..., "<name>").
+  // file_iframe_sandbox_d_if23.html in if_23 opens a window and then attempts
+  // to navigate it using it's name in the target of window.open().
 }
 
 addLoadEvent(doTest);
 </script>
 <body onunload="checkTestsFinished()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
 <p id="display"></p>
 <div id="content">
 <iframe sandbox="allow-same-origin allow-scripts allow-top-navigation" id="if_14" src="file_iframe_sandbox_d_if14.html" height="10" width="10"></iframe>
 <iframe id="if_15" name="if_parent" src="file_iframe_sandbox_d_if15.html" height="10" width="10"></iframe>
 <iframe sandbox="allow-scripts" id="if_17" src="file_iframe_sandbox_d_if17.html" height="10" width="10"></iframe>
 <iframe sandbox="allow-scripts" id="if_18" src="file_iframe_sandbox_d_if18.html" height="10" width="10"></iframe>
 <iframe sandbox="allow-scripts" id="if_19" src="file_iframe_sandbox_d_if19.html" height="10" width="10"></iframe>
 <iframe id="if_21" name="if_parent2" src="file_iframe_sandbox_d_if21.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-popups" id="if_23" src="file_iframe_sandbox_d_if23.html" height="10" width="10"></iframe>
 </div>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_iframe_sandbox_popups.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=766282
+implement allow-popups directive for iframe sandbox
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Bug 766282</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+  ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+  ok(result, desc);
+
+  completedTests++;
+
+  if (result) {
+    passedTests++;
+  }
+
+  if (completedTests == 3) {
+    is(passedTests, completedTests, "There are " + completedTests + " popups tests that should pass");
+    SimpleTest.finish();
+  }
+}
+
+function doTest() {
+  // passes if good
+  // 1) Test that a sandboxed iframe with "allow-popups" can open a new window using the target.attribute.
+  // This is done via file_iframe_sandbox_h_if1.html which is sandboxed with "allow-popups allow-scripts allow-same-origin".
+  // The window it attempts to open calls window.opener.ok(true, ...) and file_iframe_h_if1.html has an ok()
+  // function that calls window.parent.ok_wrapper.
+
+  // passes if good
+  // 2) Test that a sandboxed iframe with "allow-popups" can open a new window using window.open.
+  // This is done via file_iframe_sandbox_h_if1.html which is sandboxed with "allow-popups allow-scripts allow-same-origin".
+  // The window it attempts to open calls window.opener.ok(true, ...) and file_iframe_h_if1.html has an ok()
+  // function that calls window.parent.ok_wrapper.
+
+  // passes if good, fails if bad
+  // 3) Test that a sandboxed iframe with "allow-popups" can open a new window using the target.attribute
+  // for a non-existing browsing context (BC766282).
+  // This is done via file_iframe_sandbox_h_if1.html which is sandboxed with "allow-popups allow-scripts allow-same-origin".
+  // The window it attempts to open calls window.opener.ok(true, ...) and file_iframe_h_if1.html has an ok()
+  // function that calls window.parent.ok_wrapper.
+}
+
+addLoadEvent(doTest);
+
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=766282">Mozilla Bug 766282</a> - implement allow-popups directive for iframe sandbox
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-popups allow-same-origin allow-scripts" id="if1" src="file_iframe_sandbox_h_if1.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_iframe_sandbox_popups_inheritance.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=766282
+Implement HTML5 sandbox allow-popuos directive for IFRAMEs - inheritance tests
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests for Bug 766282</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+
+SimpleTest.expectAssertions(0, 5);
+SimpleTest.waitForExplicitFinish();
+
+// A postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+  switch (event.data.type) {
+    case "attempted":
+      testAttempted();
+      break;
+    case "ok":
+      ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      break;
+    default:
+      // allow for old style message
+      if (event.data.ok != undefined) {
+        ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+      }
+  }
+}
+
+var iframesWithWindowsToClose = new Array();
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 15;
+var totalTestsToAttempt = 21;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+  ok(result, desc);
+
+  if (result) {
+    passedTests++;
+  }
+
+  if (addToAttempted) {
+    testAttempted();
+  }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+  attemptedTests++;
+  if (attemptedTests == totalTestsToAttempt) {
+    // Make sure all tests have had a chance to complete.
+    setTimeout(function() {finish();}, 1000);
+  }
+}
+
+var finishCalled = false;
+
+function finish() {
+  if (!finishCalled) {
+    finishCalled = true;
+    is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " inheritance tests that should pass");
+
+    closeWindows();
+
+    SimpleTest.finish();
+  }
+}
+
+function checkTestsFinished() {
+  // If our own finish() has not been called, probably failed due to a timeout, so close remaining windows.
+  if (!finishCalled) {
+    closeWindows();
+  }
+}
+
+function closeWindows() {
+  for (var i = 0; i < iframesWithWindowsToClose.length; i++) {
+    document.getElementById(iframesWithWindowsToClose[i]).contentWindow.postMessage({type: "closeWindows"}, "*");
+  }
+}
+
+function doTest() {
+  // passes if good and fails if bad
+  // 1,2,3) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+  // allow-same-origin" should not have its origin sandbox flag set and be able to access document.cookie.
+  // (Done by file_iframe_sandbox_k_if5.html opened from file_iframe_sandbox_k_if4.html)
+  // This is repeated for 3 different ways of opening the window,
+  // see file_iframe_sandbox_k_if4.html for details.
+
+  // passes if good
+  // 4,5,6) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+  // allow-top-navigation" should not have its top-level navigation sandbox flag set and be able to
+  // navigate top. (Done by file_iframe_sandbox_k_if5.html (and if6) opened from
+  // file_iframe_sandbox_k_if4.html).  This is repeated for 3 different ways of opening the window,
+  // see file_iframe_sandbox_k_if4.html for details.
+
+  // passes if good
+  // 7,8,9) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+  // all-forms" should not have its forms sandbox flag set and be able to submit forms.
+  // (Done by file_iframe_sandbox_k_if7.html opened from file_iframe_sandbox_k_if4.html)
+  // This is repeated for 3 different ways of opening the window,
+  // see file_iframe_sandbox_k_if4.html for details.
+
+  // passes if good
+  // 10,11,12) Make sure that the sandbox flags copied to a new browsing context are taken from the
+  // current active document not the browsing context (iframe / docShell).
+  // This is done by removing allow-same-origin and calling doSubOpens from file_iframe_sandbox_k_if8.html,
+  // which opens file_iframe_sandbox_k_if9.html in 3 different ways.
+  // It then navigates to file_iframe_sandbox_k_if1.html to run tests 13 - 21 below.
+  var if_8_1 = document.getElementById('if_8_1');
+  if_8_1.sandbox = 'allow-scripts allow-popups';
+  if_8_1.contentWindow.doSubOpens();
+
+  // passes if good and fails if bad
+  // 13,14,15) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups"
+  // should have its origin sandbox flag set and not be able to access document.cookie.
+  // This is done by file_iframe_sandbox_k_if8.html navigating to file_iframe_sandbox_k_if1.html
+  // after allow-same-origin has been removed from iframe if_8_1.  file_iframe_sandbox_k_if1.html
+  // opens file_iframe_sandbox_k_if2.html in 3 different ways to perform the tests.
+  iframesWithWindowsToClose.push("if_8_1");
+
+  // fails if bad
+  // 16,17,18) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups"
+  // should have its forms sandbox flag set and not be able to submit forms.
+  // This is done by file_iframe_sandbox_k_if2.html, see test 10 for details of how this is opened.
+
+  // fails if bad
+  // 19,20,21) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups"
+  // should have its top-level navigation sandbox flag set and not be able to navigate top.
+  // This is done by file_iframe_sandbox_k_if2.html, see test 10 for details of how this is opened.
+}
+
+addLoadEvent(doTest);
+</script>
+
+<body onunload="checkTestsFinished()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=766282">Mozilla Bug 766282</a> - Implement HTML5 sandbox allow-popups directive for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-scripts allow-popups allow-same-origin allow-forms allow-top-navigation" id="if_4" src="file_iframe_sandbox_k_if4.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-popups allow-same-origin" id="if_8_1" src="file_iframe_sandbox_k_if8.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
--- a/content/media/DecoderTraits.cpp
+++ b/content/media/DecoderTraits.cpp
@@ -40,16 +40,20 @@
 #include "MediaPluginHost.h"
 #endif
 #ifdef MOZ_OMX_DECODER
 #include "MediaOmxDecoder.h"
 #include "MediaOmxReader.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #endif
+#ifdef MOZ_RTSP
+#include "RtspOmxDecoder.h"
+#include "RtspOmxReader.h"
+#endif
 #ifdef MOZ_DASH
 #include "DASHDecoder.h"
 #endif
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #include "WMFReader.h"
 #endif
 #ifdef MOZ_DIRECTSHOW
@@ -238,16 +242,39 @@ static char const *const gH264Codecs[9] 
 };
 
 static char const *const gMpegAudioCodecs[2] = {
   "mp3",          // MP3
   nullptr
 };
 #endif
 
+#ifdef MOZ_RTSP
+static const char* const gRtspTypes[2] = {
+    "RTSP",
+    nullptr
+};
+
+static bool
+IsRtspSupportedType(const nsACString& aMimeType)
+{
+  return MediaDecoder::IsRtspEnabled() &&
+    CodecListContains(gRtspTypes, aMimeType);
+}
+#endif
+
+/* static */
+bool DecoderTraits::DecoderWaitsForOnConnected(const nsACString& aMimeType) {
+#ifdef MOZ_RTSP
+  return CodecListContains(gRtspTypes, aMimeType);
+#else
+  return false;
+#endif
+}
+
 #ifdef MOZ_MEDIA_PLUGINS
 static bool
 IsMediaPluginsType(const nsACString& aType)
 {
   if (!MediaDecoder::IsMediaPluginsEnabled()) {
     return false;
   }
 
@@ -482,16 +509,21 @@ DecoderTraits::CreateDecoder(const nsACS
       }
       if (principal->GetAppStatus() < nsIPrincipal::APP_STATUS_PRIVILEGED) {
         return nullptr;
       }
     }
     decoder = new MediaOmxDecoder();
   }
 #endif
+#ifdef MOZ_RTSP
+  if (IsRtspSupportedType(aType)) {
+    decoder = new RtspOmxDecoder();
+  }
+#endif
 #ifdef MOZ_MEDIA_PLUGINS
   if (MediaDecoder::IsMediaPluginsEnabled() && GetMediaPluginHost()->FindDecoder(aType, NULL)) {
     decoder = new MediaPluginDecoder(aType);
   }
 #endif
 #ifdef MOZ_WEBM
   if (IsWebMType(aType)) {
     decoder = new WebMDecoder();
--- a/content/media/DecoderTraits.h
+++ b/content/media/DecoderTraits.h
@@ -52,14 +52,18 @@ public:
   // if we were unable to create the reader.
   static MediaDecoderReader* CreateReader(const nsACString& aType,
                                           AbstractMediaDecoder* aDecoder);
 
   // Returns true if MIME type aType is supported in video documents,
   // or false otherwise. Not all platforms support all MIME types, and
   // vice versa.
   static bool IsSupportedInVideoDocument(const nsACString& aType);
+
+  // Returns true if we should not start decoder until we receive
+  // OnConnected signal. (currently RTSP only)
+  static bool DecoderWaitsForOnConnected(const nsACString& aType);
 };
 
 }
 
 #endif
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1167,39 +1167,48 @@ void MediaDecoder::ChangeState(PlayState
   if (mDecodedStream) {
     bool blockForPlayState = aState != PLAY_STATE_PLAYING;
     if (mDecodedStream->mHaveBlockedForPlayState != blockForPlayState) {
       mDecodedStream->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1);
       mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
     }
   }
   mPlayState = aState;
-  if (mDecoderStateMachine) {
-    switch (aState) {
-    case PLAY_STATE_PLAYING:
-      mDecoderStateMachine->Play();
-      break;
-    case PLAY_STATE_SEEKING:
-      mDecoderStateMachine->Seek(mRequestedSeekTime);
-      mRequestedSeekTime = -1.0;
-      break;
-    default:
-      /* No action needed */
-      break;
-    }
-  }
+
+  ApplyStateToStateMachine(mPlayState);
 
   if (aState!= PLAY_STATE_LOADING) {
     mIsDormant = false;
     mIsExitingDormant = false;
   }
 
   GetReentrantMonitor().NotifyAll();
 }
 
+void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+
+  if (mDecoderStateMachine) {
+    switch (aState) {
+      case PLAY_STATE_PLAYING:
+        mDecoderStateMachine->Play();
+        break;
+      case PLAY_STATE_SEEKING:
+        mDecoderStateMachine->Seek(mRequestedSeekTime);
+        mRequestedSeekTime = -1.0;
+        break;
+      default:
+        /* No action needed */
+        break;
+    }
+  }
+}
+
 void MediaDecoder::PlaybackPositionChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mShuttingDown)
     return;
 
   double lastTime = mCurrentTime;
 
@@ -1692,16 +1701,25 @@ MediaDecoder::IsWaveEnabled()
 #ifdef MOZ_WEBM
 bool
 MediaDecoder::IsWebMEnabled()
 {
   return Preferences::GetBool("media.webm.enabled");
 }
 #endif
 
+#ifdef MOZ_RTSP
+bool
+MediaDecoder::IsRtspEnabled()
+{
+  //Currently the Rtsp decoded by omx.
+  return (Preferences::GetBool("media.rtsp.enabled", false) && IsOmxEnabled());
+}
+#endif
+
 #ifdef MOZ_GSTREAMER
 bool
 MediaDecoder::IsGStreamerEnabled()
 {
   return Preferences::GetBool("media.gstreamer.enabled");
 }
 #endif
 
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -678,16 +678,20 @@ public:
    * thread.
    ******/
 
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this object's monitor of the
   // change. Call on the main thread only.
   void ChangeState(PlayState aState);
 
+  // Called by |ChangeState|, to update the state machine.
+  // Call on the main thread only and the lock must be obtained.
+  virtual void ApplyStateToStateMachine(PlayState aState);
+
   // May be called by the reader to notify this decoder that the metadata from
   // the media file has been read. Call on the decode thread only.
   void OnReadMetadataCompleted() MOZ_OVERRIDE { }
 
   // Called when the metadata from the media file has been loaded by the
   // state machine. Call on the main thread only.
   void MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags);
 
@@ -762,16 +766,19 @@ public:
 
 #ifdef MOZ_WAVE
   static bool IsWaveEnabled();
 #endif
 
 #ifdef MOZ_WEBM
   static bool IsWebMEnabled();
 #endif
+#ifdef MOZ_RTSP
+  static bool IsRtspEnabled();
+#endif
 
 #ifdef MOZ_GSTREAMER
   static bool IsGStreamerEnabled();
 #endif
 
 #ifdef MOZ_OMX_DECODER
   static bool IsOmxEnabled();
 #endif
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/DebugOnly.h"
 
 #include "MediaResource.h"
+#include "RtspMediaResource.h"
 
 #include "mozilla/Mutex.h"
 #include "nsDebug.h"
 #include "MediaDecoder.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsIFile.h"
 #include "nsIFileChannel.h"
@@ -1690,16 +1691,18 @@ MediaResource::Create(MediaDecoder* aDec
 
   nsAutoCString contentType;
   aChannel->GetContentType(contentType);
 
   nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
   nsRefPtr<MediaResource> resource;
   if (fc || IsBlobURI(uri)) {
     resource = new FileMediaResource(aDecoder, aChannel, uri, contentType);
+  } else if (IsRtspURI(uri)) {
+    resource = new RtspMediaResource(aDecoder, aChannel, uri, contentType);
   } else {
     resource = new ChannelMediaResource(aDecoder, aChannel, uri, contentType);
   }
   return resource.forget();
 }
 
 void BaseMediaResource::MoveLoadsToBackground() {
   NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -7,16 +7,17 @@
 #define MediaResource_h_
 
 #include "mozilla/Mutex.h"
 #ifdef MOZ_DASH
 #include "mozilla/ReentrantMonitor.h"
 #endif
 #include "nsIChannel.h"
 #include "nsIURI.h"
+#include "nsIStreamingProtocolController.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "MediaCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/TimeStamp.h"
 #include "nsThreadUtils.h"
 
@@ -177,16 +178,17 @@ public:
 };
 
 inline MediaByteRange::MediaByteRange(TimestampedMediaByteRange& aByteRange)
   : mStart(aByteRange.mStart), mEnd(aByteRange.mEnd)
 {
   NS_ASSERTION(mStart < mEnd, "Range should end after start!");
 }
 
+class RtspMediaResource;
 
 /**
  * Provides a thread-safe, seek/read interface to resources
  * loaded from a URI. Uses MediaCache to cache data received over
  * Necko's async channel API, thus resolving the mismatch between clients
  * that need efficient random access to the data and protocols that do not
  * support efficient random access, such as HTTP.
  *
@@ -386,16 +388,28 @@ public:
 
   // Notify that the last data byte range was loaded.
   virtual void NotifyLastByteRange() { }
 
   // Returns the content type of the resource. This is copied from the
   // nsIChannel when the MediaResource is created. Safe to call from
   // any thread.
   virtual const nsCString& GetContentType() const = 0;
+
+  // Get the RtspMediaResource pointer if this MediaResource really is a
+  // RtspMediaResource. For calling Rtsp specific functions.
+  virtual RtspMediaResource* GetRtspPointer() {
+    return nullptr;
+  }
+
+  // Return true if the stream is a live stream
+  virtual bool IsRealTime() {
+    return false;
+  }
+
 protected:
   virtual ~MediaResource() {};
 };
 
 class BaseMediaResource : public MediaResource {
 public:
   virtual nsIURI* URI() const { return mURI; }
   virtual void MoveLoadsToBackground();
new file mode 100644
--- /dev/null
+++ b/content/media/RtspMediaResource.cpp
@@ -0,0 +1,599 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/DebugOnly.h"
+
+#include "RtspMediaResource.h"
+
+#include "MediaDecoder.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Preferences.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIStreamingProtocolService.h"
+#include "nsServiceManagerUtils.h"
+
+#ifdef PR_LOGGING
+PRLogModuleInfo* gRtspMediaResourceLog;
+#define LOG(msg, ...) PR_LOG(gRtspMediaResourceLog, PR_LOG_DEBUG, \
+                             (msg, ##__VA_ARGS__))
+// Debug logging macro with object pointer and class name.
+#define RTSPMLOG(msg, ...) \
+        LOG("%p [RtspMediaResource]: " msg, this, ##__VA_ARGS__)
+#else
+#define LOG(msg, ...)
+#define RTSPMLOG(msg, ...)
+#endif
+
+namespace mozilla {
+
+/* class RtspTrackBuffer: a ring buffer implementation for audio/video track
+ * un-decoded data.
+ * The ring buffer is divided into BUFFER_SLOT_NUM slots,
+ * and each slot's size is fixed(mSlotSize).
+ * Even though the ring buffer is divided into fixed size slots, it still can
+ * store the data which size is larger than one slot size.
+ * */
+#define BUFFER_SLOT_NUM 8192
+#define BUFFER_SLOT_DEFAULT_SIZE 256
+#define BUFFER_SLOT_MAX_SIZE 8192
+#define BUFFER_SLOT_INVALID -1
+#define BUFFER_SLOT_EMPTY 0
+
+struct BufferSlotData {
+  int32_t mLength;
+  uint64_t mTime;
+};
+
+class RtspTrackBuffer
+{
+public:
+  RtspTrackBuffer(const char *aMonitor, int32_t aTrackIdx, uint32_t aSlotSize)
+  : mMonitor(aMonitor)
+  , mSlotSize(aSlotSize)
+  , mTotalBufferSize(BUFFER_SLOT_NUM * mSlotSize)
+  , mFrameType(0)
+  , mIsStarted(false) {
+    MOZ_COUNT_CTOR(RtspTrackBuffer);
+#ifdef PR_LOGGING
+    mTrackIdx = aTrackIdx;
+#endif
+    MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM);
+    mRingBuffer = new uint8_t[mTotalBufferSize];
+    Reset();
+  };
+  ~RtspTrackBuffer() {
+    MOZ_COUNT_DTOR(RtspTrackBuffer);
+    mRingBuffer = nullptr;
+  };
+  void Start() {
+    MonitorAutoLock monitor(mMonitor);
+    mIsStarted = true;
+  }
+  void Stop() {
+    MonitorAutoLock monitor(mMonitor);
+    mIsStarted = false;
+  }
+
+  // Read the data from mRingBuffer[mConsumerIdx*mSlotSize] into aToBuffer.
+  // If the aToBufferSize is smaller than mBufferSlotDataLength[mConsumerIdx],
+  // early return and set the aFrameSize to notify the reader the aToBuffer
+  // doesn't have enough space. The reader must realloc the aToBuffer if it
+  // wishes to read the data.
+  nsresult ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize,
+                      uint32_t& aReadCount, uint64_t& aFrameTime,
+                      uint32_t& aFrameSize);
+  // Write the data from aFromBuffer into mRingBuffer[mProducerIdx*mSlotSize].
+  void WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
+                   uint64_t aFrameTime, uint32_t aFrameType);
+  // Reset the mProducerIdx, mConsumerIdx, mBufferSlotDataLength[],
+  // mBufferSlotDataTime[].
+  void Reset();
+
+  // We should call SetFrameType first then reset().
+  // If we call reset() first, the queue may still has some "garbage" frame
+  // from another thread's |OnMediaDataAvailable| before |SetFrameType|.
+  void ResetWithFrameType(uint32_t aFrameType) {
+    SetFrameType(aFrameType);
+    Reset();
+  }
+
+private:
+  // The FrameType is sync to nsIStreamingProtocolController.h
+  void SetFrameType(uint32_t aFrameType) {
+    MonitorAutoLock monitor(mMonitor);
+    mFrameType = mFrameType | aFrameType;
+  }
+
+  // A monitor lock to prevent racing condition.
+  Monitor mMonitor;
+#ifdef PR_LOGGING
+  // Indicate the track number for Rtsp.
+  int32_t mTrackIdx;
+#endif
+  // mProducerIdx: A slot index that we store data from
+  // nsIStreamingProtocolController.
+  // mConsumerIdx: A slot index that we read when decoder need(from OMX decoder).
+  int32_t mProducerIdx;
+  int32_t mConsumerIdx;
+
+  // Because each slot's size is fixed, we need an array to record the real
+  // data length and data time stamp.
+  // The value in mBufferSlotData[index].mLength represents:
+  // -1(BUFFER_SLOT_INVALID): The index of slot data is invalid, mConsumerIdx
+  //                          should go forward.
+  // 0(BUFFER_SLOT_EMPTY): The index slot is empty. mConsumerIdx should wait here.
+  // positive value: The index slot contains valid data and the value is data size.
+  BufferSlotData mBufferSlotData[BUFFER_SLOT_NUM];
+
+  // The ring buffer pointer.
+  nsAutoArrayPtr<uint8_t> mRingBuffer;
+  // Each slot's size.
+  uint32_t mSlotSize;
+  // Total mRingBuffer's total size.
+  uint32_t mTotalBufferSize;
+  // A flag that that indicate the incoming data should be dropped or stored.
+  // When we are seeking, the incoming data should be dropped.
+  // Bit definition in |nsIStreamingProtocolController.h|
+  uint32_t mFrameType;
+
+  // Set true/false when |Start()/Stop()| is called.
+  bool mIsStarted;
+};
+
+nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize,
+                                     uint32_t& aReadCount, uint64_t& aFrameTime,
+                                     uint32_t& aFrameSize)
+{
+  MonitorAutoLock monitor(mMonitor);
+  RTSPMLOG("ReadBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d "
+           "mBufferSlotData[mConsumerIdx].mLength %d"
+           ,mTrackIdx ,mProducerIdx ,mConsumerIdx
+           ,mBufferSlotData[mConsumerIdx].mLength);
+  // Reader should skip the slots with mLength==BUFFER_SLOT_INVALID.
+  // The loop ends when
+  // 1. Read data successfully
+  // 2. Fail to read data due to aToBuffer's space
+  // 3. No data in this buffer
+  // 4. mIsStarted is not set
+  while (1) {
+    if (mBufferSlotData[mConsumerIdx].mLength > 0) {
+      // Check the aToBuffer space is enough for data copy.
+      if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) {
+        aFrameSize = mBufferSlotData[mConsumerIdx].mLength;
+        break;
+      }
+      uint32_t slots = (mBufferSlotData[mConsumerIdx].mLength / mSlotSize) + 1;
+      // we have data, copy to aToBuffer
+      MOZ_ASSERT(mBufferSlotData[mConsumerIdx].mLength <=
+                 (int32_t)((BUFFER_SLOT_NUM - mConsumerIdx) * mSlotSize));
+      memcpy(aToBuffer,
+             (void *)(&mRingBuffer[mSlotSize * mConsumerIdx]),
+             mBufferSlotData[mConsumerIdx].mLength);
+
+      aFrameSize = aReadCount = mBufferSlotData[mConsumerIdx].mLength;
+      aFrameTime = mBufferSlotData[mConsumerIdx].mTime;
+      RTSPMLOG("DataLength %d, data time %lld"
+               ,mBufferSlotData[mConsumerIdx].mLength
+               ,mBufferSlotData[mConsumerIdx].mTime);
+      // After reading the data, we set current index of mBufferSlotDataLength
+      // to BUFFER_SLOT_EMPTY to indicate these slots are free.
+      for (uint32_t i = mConsumerIdx; i < mConsumerIdx + slots; ++i) {
+        mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY;
+        mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY;
+      }
+      mConsumerIdx = (mConsumerIdx + slots) % BUFFER_SLOT_NUM;
+      break;
+    } else if (mBufferSlotData[mConsumerIdx].mLength == BUFFER_SLOT_INVALID) {
+      mConsumerIdx = (mConsumerIdx + 1) % BUFFER_SLOT_NUM;
+      RTSPMLOG("BUFFER_SLOT_INVALID move forward");
+    } else {
+      // No data, and disconnected.
+      if (!mIsStarted) {
+        return NS_ERROR_FAILURE;
+      }
+      // No data, the decode thread is blocked here until we receive
+      // OnMediaDataAvailable. The OnMediaDataAvailable will call WriteBuffer()
+      // to wake up the decode thread.
+      RTSPMLOG("monitor.Wait()");
+      monitor.Wait();
+    }
+  }
+  return NS_OK;
+}
+
+/* When we perform a WriteBuffer, we check mIsStarted and aFrameType first.
+ * These flags prevent "garbage" frames from being written into the buffer.
+ *
+ * After writing the data into the buffer, we check to see if we wrote over a
+ * slot, and update mConsumerIdx if necessary.
+ * This ensures that the decoder will get the "oldest" data available in the
+ * buffer.
+ *
+ * If the incoming data is larger than one slot size (isMultipleSlots), we do
+ * |mBufferSlotData[].mLength = BUFFER_SLOT_INVALID;| for other slots except the
+ * first slot, in order to notify the reader that some slots are unavailable.
+ *
+ * If the incoming data is isMultipleSlots and crosses the end of
+ * BUFFER_SLOT_NUM, returnToHead is set to true and the data will continue to
+ * be written from head(index 0).
+ *
+ * MEDIASTREAM_FRAMETYPE_DISCONTINUITY currently is used when we are seeking.
+ * */
+void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
+                                  uint64_t aFrameTime, uint32_t aFrameType)
+{
+  MonitorAutoLock monitor(mMonitor);
+  if (!mIsStarted) {
+    RTSPMLOG("mIsStarted is false");
+    return;
+  }
+  if (mTotalBufferSize < aWriteCount) {
+    RTSPMLOG("mTotalBufferSize < aWriteCount, incoming data is too large");
+    return;
+  }
+  // Checking the incoming data's frame type.
+  // If we receive MEDIASTREAM_FRAMETYPE_DISCONTINUITY, clear the mFrameType
+  // imply the RtspTrackBuffer is ready for receive data.
+  if (aFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) {
+    mFrameType = mFrameType & (~MEDIASTREAM_FRAMETYPE_DISCONTINUITY);
+    RTSPMLOG("Clear mFrameType");
+    return;
+  }
+  // Checking current buffer frame type.
+  // If the MEDIASTREAM_FRAMETYPE_DISCONTINUNITY bit is set, imply the
+  // RtspTrackBuffer can't receive data now. So we drop the frame until we
+  // receive MEDIASTREAM_FRAMETYPE_DISCONTINUNITY.
+  if (mFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) {
+    RTSPMLOG("Return because the mFrameType is set");
+    return;
+  }
+  // The flag is true if the incoming data is larger than one slot size.
+  bool isMultipleSlots = false;
+  // The flag is true if the incoming data is larger than remainder free slots
+  bool returnToHead = false;
+  // Calculate how many slots the incoming data needed.
+  int32_t slots = 1;
+  int32_t i;
+  RTSPMLOG("WriteBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d",
+           mTrackIdx, mProducerIdx,mConsumerIdx);
+  if (aWriteCount > mSlotSize) {
+    isMultipleSlots = true;
+    slots = (aWriteCount / mSlotSize) + 1;
+  }
+  if (isMultipleSlots &&
+      (aWriteCount > (BUFFER_SLOT_NUM - mProducerIdx) * mSlotSize)) {
+    returnToHead = true;
+  }
+  RTSPMLOG("slots %d isMultipleSlots %d returnToHead %d",
+           slots, isMultipleSlots, returnToHead);
+  if (returnToHead) {
+    // Clear the rest index of mBufferSlotData[].mLength
+    for (i = mProducerIdx; i < BUFFER_SLOT_NUM; ++i) {
+      mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID;
+    }
+    // We wrote one or more slots that the decode thread has not yet read.
+    // So the mConsumerIdx returns to the head of slot buffer and moves forward
+    // to the oldest slot.
+    if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots) {
+      mConsumerIdx = 0;
+      for (i = mConsumerIdx; i < BUFFER_SLOT_NUM; ++i) {
+        if (mBufferSlotData[i].mLength > 0) {
+          mConsumerIdx = i;
+          break;
+        }
+      }
+    }
+    mProducerIdx = 0;
+  }
+
+  memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount);
+
+  if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots
+      && mBufferSlotData[mConsumerIdx].mLength > 0) {
+    // Wrote one or more slots that the decode thread has not yet read.
+    RTSPMLOG("overwrite!! %d time %lld"
+             ,mTrackIdx,mBufferSlotData[mConsumerIdx].mTime);
+    mBufferSlotData[mProducerIdx].mLength = aWriteCount;
+    mBufferSlotData[mProducerIdx].mTime = aFrameTime;
+    // Clear the mBufferSlotDataLength except the start slot.
+    if (isMultipleSlots) {
+      for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) {
+        mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID;
+      }
+    }
+    mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM;
+    // Move the mConsumerIdx forward to ensure that the decoder reads the
+    // oldest data available.
+    mConsumerIdx = mProducerIdx;
+  } else {
+    // Normal case, the writer doesn't take over the reader.
+    mBufferSlotData[mProducerIdx].mLength = aWriteCount;
+    mBufferSlotData[mProducerIdx].mTime = aFrameTime;
+    // Clear the mBufferSlotData[].mLength except the start slot.
+    if (isMultipleSlots) {
+      for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) {
+        mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID;
+      }
+    }
+    mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM;
+  }
+
+  mMonitor.NotifyAll();
+}
+
+void RtspTrackBuffer::Reset() {
+  MonitorAutoLock monitor(mMonitor);
+  mProducerIdx = 0;
+  mConsumerIdx = 0;
+  for (uint32_t i = 0; i < BUFFER_SLOT_NUM; ++i) {
+    mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY;
+    mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY;
+  }
+  mMonitor.NotifyAll();
+}
+
+RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder,
+    nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType)
+  : BaseMediaResource(aDecoder, aChannel, aURI, aContentType)
+  , mIsConnected(false)
+  , mRealTime(false)
+{
+  nsCOMPtr<nsIStreamingProtocolControllerService> mediaControllerService =
+    do_GetService(MEDIASTREAMCONTROLLERSERVICE_CONTRACTID);
+  MOZ_ASSERT(mediaControllerService);
+  if (mediaControllerService) {
+    mediaControllerService->Create(mChannel,
+                                   getter_AddRefs(mMediaStreamController));
+    MOZ_ASSERT(mMediaStreamController);
+    mListener = new Listener(this);
+    mMediaStreamController->AsyncOpen(mListener);
+  }
+#ifdef PR_LOGGING
+  if (!gRtspMediaResourceLog) {
+    gRtspMediaResourceLog = PR_NewLogModule("RtspMediaResource");
+  }
+#endif
+}
+
+RtspMediaResource::~RtspMediaResource()
+{
+  RTSPMLOG("~RtspMediaResource");
+  if (mListener) {
+    // Kill its reference to us since we're going away
+    mListener->Revoke();
+  }
+}
+
+NS_IMPL_ISUPPORTS2(RtspMediaResource::Listener,
+                   nsIInterfaceRequestor, nsIStreamingProtocolListener);
+
+nsresult
+RtspMediaResource::Listener::OnMediaDataAvailable(uint8_t aTrackIdx,
+                                                  const nsACString &data,
+                                                  uint32_t length,
+                                                  uint32_t offset,
+                                                  nsIStreamingProtocolMetaData *meta)
+{
+  if (!mResource)
+    return NS_OK;
+  return mResource->OnMediaDataAvailable(aTrackIdx, data, length, offset, meta);
+}
+
+nsresult
+RtspMediaResource::Listener::OnConnected(uint8_t aTrackIdx,
+                                         nsIStreamingProtocolMetaData *meta)
+{
+  if (!mResource)
+    return NS_OK;
+  return mResource->OnConnected(aTrackIdx, meta);
+}
+
+nsresult
+RtspMediaResource::Listener::OnDisconnected(uint8_t aTrackIdx, uint32_t reason)
+{
+  if (!mResource)
+    return NS_OK;
+  return mResource->OnDisconnected(aTrackIdx, reason);
+}
+
+nsresult
+RtspMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
+{
+  return QueryInterface(aIID, aResult);
+}
+
+nsresult
+RtspMediaResource::ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize,
+                                      uint32_t aTrackIdx, uint32_t& aBytes,
+                                      uint64_t& aTime, uint32_t& aFrameSize)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+  NS_ASSERTION(aTrackIdx < mTrackBuffer.Length(),
+               "ReadTrack index > mTrackBuffer");
+  MOZ_ASSERT(aBuffer);
+
+  return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes,
+                                             aTime, aFrameSize);
+}
+
+nsresult
+RtspMediaResource::OnMediaDataAvailable(uint8_t aTrackIdx,
+                                        const nsACString &data,
+                                        uint32_t length,
+                                        uint32_t offset,
+                                        nsIStreamingProtocolMetaData *meta)
+{
+  uint64_t time;
+  uint32_t frameType;
+  meta->GetTimeStamp(&time);
+  meta->GetFrameType(&frameType);
+  if (mRealTime) {
+    time = 0;
+  }
+  mTrackBuffer[aTrackIdx]->WriteBuffer(data.BeginReading(), length, time,
+                                       frameType);
+  return NS_OK;
+}
+
+nsresult
+RtspMediaResource::OnConnected(uint8_t aTrackIdx,
+                               nsIStreamingProtocolMetaData *meta)
+{
+  if (mIsConnected) {
+    return NS_OK;
+  }
+
+  uint8_t tracks;
+  mMediaStreamController->GetTotalTracks(&tracks);
+  uint64_t duration = 0;
+  for (int i = 0; i < tracks; ++i) {
+    nsCString rtspTrackId("RtspTrack");
+    rtspTrackId.AppendInt(i);
+    nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
+    mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
+    MOZ_ASSERT(trackMeta);
+    trackMeta->GetDuration(&duration);
+
+    // Here is a heuristic to estimate the slot size.
+    // For video track, calculate the width*height.
+    // For audio track, use the BUFFER_SLOT_DEFAULT_SIZE because the w*h is 0.
+    // Finally clamp them into (BUFFER_SLOT_DEFAULT_SIZE,BUFFER_SLOT_MAX_SIZE)
+    uint32_t w, h;
+    uint32_t slotSize;
+    trackMeta->GetWidth(&w);
+    trackMeta->GetHeight(&h);
+    slotSize = clamped((int32_t)(w * h), BUFFER_SLOT_DEFAULT_SIZE,
+                       BUFFER_SLOT_MAX_SIZE);
+    mTrackBuffer.AppendElement(new RtspTrackBuffer(rtspTrackId.get(),
+                                                   i, slotSize));
+    mTrackBuffer[i]->Start();
+  }
+
+  // If the duration is 0, imply the stream is live stream.
+  if (duration) {
+    // Not live stream.
+    mRealTime = false;
+    bool seekable = true;
+    mDecoder->SetInfinite(false);
+    mDecoder->SetTransportSeekable(seekable);
+    mDecoder->SetDuration(duration);
+  } else {
+    // Live stream.
+    // Check the preference "media.realtime_decoder.enabled".
+    if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) {
+      // Give up, report error to media element.
+      nsCOMPtr<nsIRunnable> event =
+        NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
+      NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+      return NS_ERROR_FAILURE;
+    } else {
+      mRealTime = true;
+      bool seekable = false;
+      mDecoder->SetInfinite(true);
+      mDecoder->SetTransportSeekable(seekable);
+      mDecoder->SetMediaSeekable(seekable);
+    }
+  }
+  // Fires an initial progress event and sets up the stall counter so stall events
+  // fire if no download occurs within the required time frame.
+  mDecoder->Progress(false);
+
+  MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
+  NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
+  HTMLMediaElement* element = owner->GetMediaElement();
+  NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
+
+  element->FinishDecoderSetup(mDecoder, this);
+  mIsConnected = true;
+
+  return NS_OK;
+}
+
+nsresult
+RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, uint32_t aReason)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
+
+  for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
+    mTrackBuffer[i]->Stop();
+    mTrackBuffer[i]->Reset();
+  }
+
+  if (aReason == (uint32_t)NS_ERROR_CONNECTION_REFUSED) {
+    mDecoder->NetworkError();
+  }
+  return NS_OK;
+}
+
+void RtspMediaResource::Suspend(bool aCloseImmediately)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
+
+  MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
+  NS_ENSURE_TRUE_VOID(owner);
+  HTMLMediaElement* element = owner->GetMediaElement();
+  NS_ENSURE_TRUE_VOID(element);
+
+  mMediaStreamController->Suspend();
+  element->DownloadSuspended();
+}
+
+void RtspMediaResource::Resume()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
+
+  MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
+  NS_ENSURE_TRUE_VOID(owner);
+  HTMLMediaElement* element = owner->GetMediaElement();
+  NS_ENSURE_TRUE_VOID(element);
+
+  if (mChannel) {
+    element->DownloadResumed();
+  }
+  mMediaStreamController->Resume();
+}
+
+nsresult RtspMediaResource::Open(nsIStreamListener **aStreamListener)
+{
+  return NS_OK;
+}
+
+nsresult RtspMediaResource::Close()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  mMediaStreamController->Stop();
+  return NS_OK;
+}
+
+already_AddRefed<nsIPrincipal> RtspMediaResource::GetCurrentPrincipal()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+  nsCOMPtr<nsIPrincipal> principal;
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  if (!secMan || !mChannel)
+    return nullptr;
+  secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
+  return principal.forget();
+}
+
+nsresult RtspMediaResource::SeekTime(int64_t aOffset)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+
+  RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]",
+           aOffset, mDecoder);
+  // Clear buffer and raise the frametype flag.
+  for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
+    mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY);
+  }
+
+  return mMediaStreamController->Seek(aOffset);
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/media/RtspMediaResource.h
@@ -0,0 +1,233 @@
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 !defined(RtspMediaResource_h_)
+#define RtspMediaResource_h_
+
+#include "MediaResource.h"
+
+namespace mozilla {
+
+class RtspTrackBuffer;
+
+/* RtspMediaResource
+ * RtspMediaResource provides an interface to deliver and control RTSP media
+ * data to RtspDecoder.
+ *
+ * RTSP Flow Start vs HTTP Flow Start:
+ * For HTTP (and files stored on disk), once the channel is created and response
+ * data is available, HTMLMediaElement::MediaLoadListener::OnStartRequest is
+ * called. (Note, this is an asynchronous call following channel->AsyncOpen).
+ * The decoder and MediaResource are set up to talk to each other:
+ * InitializeDecoderForChannel and FinishDecoderSetup.
+ * RtspMediaResource is different from this, in that FinishDecoderSetup is
+ * postponed until after the initial connection with the server is made.
+ * RtspController, owned by RtspMediaResource, provides the interface to setup
+ * the connection, and calls RtspMediaResource::Listener::OnConnected
+ * (from nsIStreamingProtocolListener). FinishDecoderSetup is then called to
+ * connect RtspMediaResource with RtspDecoder and allow HTMLMediaElement to
+ * request playback etc.
+ *
+ * Playback:
+ * When the user presses play/pause, HTMLMediaElement::Play/::Pause is called,
+ * subsequently making calls to the decoder state machine. Upon these state
+ * changes, the decoder is told to start reading and decoding data. This causes
+ * the nsIStreamingMediaController object to send play/pause commands to the
+ * server.
+ * Data is then delivered to the host and eventually written to the
+ * RtspTrackBuffer objects. Note that RtspMediaResource does not know about the
+ * play or pause state. It only knows about the data written into its buffers.
+ *
+ * Data Structures and Flow:
+ * Unlike HTTP, RTSP provides separate streams for audio and video.
+ * As such, it creates two RtspTrackBuffer objects for the audio and video data.
+ * Data is read using the function ReadFrameFromTrack. These buffer objects are
+ * ring buffers, implying that data from the network may be discarded if the
+ * decoder cannot read at a high enough rate.
+ *
+ * Data is delivered via RtspMediaResource::Listener::OnMediaDataAvailable.
+ * This Listener implements nsIStreamingProtocolListener, and writes the data to
+ * the appropriate RtspTrackBuffer. The decoder then reads the data by calling
+ * RtspMediaResource::ReadFrameFromTrack. Note that the decoder and decode
+ * thread will be blocked until data is available in one of the two buffers.
+ *
+ * Seeking:
+ * Since the frame data received after seek is not continuous with existing
+ * frames in RtspTrackBuffer, the buffer must be cleared. If we don't clear the
+ * old frame data in RtspTrackBuffer, the decoder's behavior will be
+ * unpredictable. So we add |mFrameType| in RtspTrackBuffer to do this:
+ * When we are seeking, the mFrameType flag is set, and RtspTrackBuffer will
+ * drop the incoming data until the RTSP server completes the seek operation.
+ * Note: seeking for RTSP is carried out based on sending the seek time to the
+ * server, unlike HTTP in which the seek time is converted to a byte offset.
+ * Thus, RtspMediaResource has a SeekTime function which should be called
+ * instead of Seek.
+ * */
+class RtspMediaResource : public BaseMediaResource
+{
+public:
+  RtspMediaResource(MediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI,
+                    const nsACString& aContentType);
+  virtual ~RtspMediaResource();
+
+  // The following methods can be called on any thread.
+
+  // Get the RtspMediaResource pointer if this MediaResource is a
+  // RtspMediaResource. For calling Rtsp specific functions.
+  virtual RtspMediaResource* GetRtspPointer() MOZ_OVERRIDE MOZ_FINAL {
+    return this;
+  }
+
+  // Returns the nsIStreamingProtocolController in the RtspMediaResource.
+  // RtspMediaExtractor: request it to get mime type for creating decoder.
+  // RtspOmxDecoder: request it to send play/pause commands to RTSP server.
+  // The lifetime of mMediaStreamController is controlled by RtspMediaResource
+  // because the RtspMediaExtractor and RtspOmxDecoder won't hold the reference.
+  nsIStreamingProtocolController* GetMediaStreamController() {
+    return mMediaStreamController;
+  }
+
+  virtual bool IsRealTime() MOZ_OVERRIDE {
+    return mRealTime;
+  }
+
+  // The following methods can be called on any thread except main thread.
+
+  // Read data from track.
+  // Parameters:
+  //   aToBuffer, aToBufferSize: buffer pointer and buffer size.
+  //   aReadCount: output actual read bytes.
+  //   aFrameTime: output frame time stamp.
+  //   aFrameSize: actual data size in track.
+  nsresult ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize,
+                              uint32_t aTrackIdx, uint32_t& aBytes,
+                              uint64_t& aTime, uint32_t& aFrameSize);
+
+  // Seek to the given time offset
+  nsresult SeekTime(int64_t aOffset);
+
+  // dummy
+  virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
+                          uint32_t aCount, uint32_t* aBytes)  MOZ_OVERRIDE{
+    return NS_ERROR_FAILURE;
+  }
+  // dummy
+  virtual void     SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE {}
+  // dummy
+  virtual void     SetPlaybackRate(uint32_t aBytesPerSecond) MOZ_OVERRIDE {}
+  // dummy
+  virtual nsresult Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
+  MOZ_OVERRIDE {
+    return NS_OK;
+  }
+  // dummy
+  virtual nsresult Seek(int32_t aWhence, int64_t aOffset) MOZ_OVERRIDE {
+    return NS_OK;
+  }
+  // dummy
+  virtual void     StartSeekingForMetadata() MOZ_OVERRIDE {}
+  // dummy
+  virtual void     EndSeekingForMetadata() MOZ_OVERRIDE {}
+  // dummy
+  virtual int64_t  Tell() MOZ_OVERRIDE { return 0; }
+
+  // Any thread
+  virtual void    Pin() MOZ_OVERRIDE {}
+  virtual void    Unpin() MOZ_OVERRIDE {}
+
+  // dummy
+  virtual bool    IsSuspendedByCache() MOZ_OVERRIDE { return false; }
+
+  virtual bool    IsSuspended() MOZ_OVERRIDE { return false; }
+  virtual bool    IsTransportSeekable() MOZ_OVERRIDE { return true; }
+  // dummy
+  virtual double  GetDownloadRate(bool* aIsReliable) MOZ_OVERRIDE { return 0; }
+
+  virtual int64_t GetLength() MOZ_OVERRIDE {
+    if (mRealTime) {
+      return -1;
+    }
+    return 0;
+  }
+
+  // dummy
+  virtual int64_t GetNextCachedData(int64_t aOffset) MOZ_OVERRIDE { return 0; }
+  // dummy
+  virtual int64_t GetCachedDataEnd(int64_t aOffset) MOZ_OVERRIDE { return 0; }
+  // dummy
+  virtual bool    IsDataCachedToEndOfResource(int64_t aOffset) MOZ_OVERRIDE {
+    return false;
+  }
+  // dummy
+  nsresult GetCachedRanges(nsTArray<MediaByteRange>& aRanges) MOZ_OVERRIDE {
+    return NS_ERROR_FAILURE;
+  }
+
+  // The following methods can be called on main thread only.
+
+  virtual nsresult Open(nsIStreamListener** aStreamListener) MOZ_OVERRIDE;
+  virtual nsresult Close() MOZ_OVERRIDE;
+  virtual void     Suspend(bool aCloseImmediately) MOZ_OVERRIDE;
+  virtual void     Resume() MOZ_OVERRIDE;
+  virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE;
+  virtual bool     CanClone() MOZ_OVERRIDE {
+    return false;
+  }
+  virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder)
+  MOZ_OVERRIDE {
+    return nullptr;
+  }
+  // dummy
+  virtual nsresult ReadFromCache(char* aBuffer, int64_t aOffset,
+                                 uint32_t aCount) MOZ_OVERRIDE {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Listener implements nsIStreamingProtocolListener as
+  // mMediaStreamController's callback function.
+  // It holds RtspMediaResource reference to notify the connection status and
+  // data arrival. The Revoke function releases the reference when
+  // RtspMediaResource is destroyed.
+  class Listener MOZ_FINAL : public nsIInterfaceRequestor,
+                             public nsIStreamingProtocolListener
+  {
+  public:
+    Listener(RtspMediaResource* aResource) : mResource(aResource) {}
+    ~Listener() {}
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIINTERFACEREQUESTOR
+    NS_DECL_NSISTREAMINGPROTOCOLLISTENER
+
+    void Revoke() { mResource = nullptr; }
+
+  private:
+    nsRefPtr<RtspMediaResource> mResource;
+  };
+  friend class Listener;
+
+protected:
+  // Main thread access only.
+  // These are called on the main thread by Listener.
+  NS_DECL_NSISTREAMINGPROTOCOLLISTENER
+
+  nsRefPtr<Listener> mListener;
+
+private:
+  // These two members are created at |RtspMediaResource::OnConnected|.
+  nsCOMPtr<nsIStreamingProtocolController> mMediaStreamController;
+  nsTArray<nsAutoPtr<RtspTrackBuffer>> mTrackBuffer;
+
+  // A flag that indicates the |RtspMediaResource::OnConnected| has already been
+  // called.
+  bool mIsConnected;
+  // live stream
+  bool mRealTime;
+};
+
+} // namespace mozilla
+
+#endif
+
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -77,16 +77,17 @@ EXPORTS += [
     'MediaDecoderOwner.h',
     'MediaDecoderReader.h',
     'MediaDecoderStateMachine.h',
     'MediaMetadataManager.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaSegment.h',
     'MediaStreamGraph.h',
+    'RtspMediaResource.h',
     'SharedBuffer.h',
     'StreamBuffer.h',
     'TimeVarying.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
@@ -124,16 +125,17 @@ CPP_SOURCES += [
     'MediaCache.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaStreamGraph.cpp',
     'MediaStreamTrack.cpp',
+    'RtspMediaResource.cpp',
     'StreamBuffer.cpp',
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'TextTrackRegion.cpp',
     'TextTrackRegionList.cpp',
     'VideoFrameContainer.cpp',
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -71,28 +71,47 @@ void MediaOmxReader::ReleaseMediaResourc
 
 void MediaOmxReader::ReleaseDecoder()
 {
   if (mOmxDecoder.get()) {
     mOmxDecoder->ReleaseDecoder();
   }
 }
 
+nsresult MediaOmxReader::InitOmxDecoder()
+{
+  if (!mOmxDecoder.get()) {
+    //register sniffers, if they are not registered in this process.
+    DataSource::RegisterDefaultSniffers();
+    mDecoder->GetResource()->SetReadMode(MediaCacheStream::MODE_METADATA);
+
+    sp<DataSource> dataSource = new MediaStreamSource(mDecoder->GetResource(), mDecoder);
+    dataSource->initCheck();
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+    mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
+    if (!mOmxDecoder->Init(extractor)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
+
 nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
                                       MetadataTags** aTags)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   *aTags = nullptr;
 
-  if (!mOmxDecoder.get()) {
-    mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
-    if (!mOmxDecoder->Init()) {
-      return NS_ERROR_FAILURE;
-    }
+  // Initialize the internal OMX Decoder.
+  nsresult rv = InitOmxDecoder();
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
   if (!mOmxDecoder->TryLoad()) {
     return NS_ERROR_FAILURE;
   }
 
   if (IsWaitingMediaResources()) {
     return NS_OK;
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -21,24 +21,33 @@ namespace dom {
   class TimeRanges;
 }
 
 class AbstractMediaDecoder;
 
 class MediaOmxReader : public MediaDecoderReader
 {
   nsCString mType;
-  android::sp<android::OmxDecoder> mOmxDecoder;
   bool mHasVideo;
   bool mHasAudio;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
   int64_t mVideoSeekTimeUs;
   int64_t mAudioSeekTimeUs;
   int32_t mSkipCount;
+
+protected:
+  android::sp<android::OmxDecoder> mOmxDecoder;
+
+  // Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
+  // on decode thread. It create and initialize the OMX decoder including
+  // setting up custom extractor. The extractor provide the essential
+  // information used for creating OMX decoder such as video/audio codec.
+  virtual nsresult InitOmxDecoder();
+
 public:
   MediaOmxReader(AbstractMediaDecoder* aDecoder);
   ~MediaOmxReader();
 
   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
   virtual nsresult ResetDecode();
 
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset);
--- a/content/media/omx/MediaOmxStateMachine.h
+++ b/content/media/omx/MediaOmxStateMachine.h
@@ -3,18 +3,19 @@
 #include "MediaDecoderStateMachine.h"
 
 namespace mozilla {
 
 class MediaOmxStateMachine : public MediaDecoderStateMachine
 {
 public:
   MediaOmxStateMachine(MediaDecoder *aDecoder,
-                       MediaDecoderReader *aReader)
-    : MediaDecoderStateMachine(aDecoder, aReader) { }
+                       MediaDecoderReader *aReader,
+                       bool aRealTime = false)
+    : MediaDecoderStateMachine(aDecoder, aReader, aRealTime) { }
 
 protected:
   // Due to a bug in the OMX.qcom.video.decoder.mpeg4 decoder, we can't own too
   // many video buffers before shutting down the decoder. When we release these
   // buffers, they asynchronously signal to OMXCodec that we have returned
   // ownership of the buffer.
   // If this signal happens while the OMXCodec is shutting down, OMXCodec will
   // crash. If the OMXCodec shuts down before all buffers are returned,
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -312,46 +312,23 @@ static sp<IOMX> sOMX = nullptr;
 static sp<IOMX> GetOMX()
 {
   if(sOMX.get() == nullptr) {
     sOMX = new OMX;
     }
   return sOMX;
 }
 
-bool OmxDecoder::Init() {
+bool OmxDecoder::Init(sp<MediaExtractor>& extractor) {
 #ifdef PR_LOGGING
   if (!gOmxDecoderLog) {
     gOmxDecoderLog = PR_NewLogModule("OmxDecoder");
   }
 #endif
 
-  //register sniffers, if they are not registered in this process.
-  DataSource::RegisterDefaultSniffers();
-
-  sp<DataSource> dataSource = new MediaStreamSource(mResource, mDecoder);
-  if (dataSource->initCheck()) {
-    NS_WARNING("Initializing DataSource for OMX decoder failed");
-    return false;
-  }
-
-  mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
-
-  sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
-  if (extractor == nullptr) {
-    NS_WARNING("Could not create MediaExtractor");
-    return false;
-  }
-
-  const char* extractorMime;
-  sp<MetaData> meta = extractor->getMetaData();
-  if (meta->findCString(kKeyMIMEType, &extractorMime) && !strcasecmp(extractorMime, AUDIO_MP3)) {
-    mIsMp3 = true;
-  }
-
   ssize_t audioTrackIndex = -1;
   ssize_t videoTrackIndex = -1;
 
   for (size_t i = 0; i < extractor->countTracks(); ++i) {
     sp<MetaData> meta = extractor->getTrackMetaData(i);
 
     int32_t bitRate;
     if (!meta->findInt32(kKeyBitRate, &bitRate))
--- a/content/media/omx/OmxDecoder.h
+++ b/content/media/omx/OmxDecoder.h
@@ -1,15 +1,16 @@
 #include <stagefright/foundation/ABase.h>
 #include <stagefright/foundation/AHandlerReflector.h>
 #include <stagefright/foundation/ALooper.h>
 #include <stagefright/MediaSource.h>
 #include <stagefright/DataSource.h>
 #include <stagefright/MediaSource.h>
 #include <utils/RefBase.h>
+#include <stagefright/MediaExtractor.h>
 
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
 #include "GrallocImages.h"
 #include "MP3FrameParser.h"
 #include "MPAPI.h"
 #include "MediaResource.h"
 #include "AbstractMediaDecoder.h"
@@ -167,17 +168,26 @@ class OmxDecoder : public OMXCodecProxy:
 
 public:
   OmxDecoder(MediaResource *aResource, AbstractMediaDecoder *aDecoder);
   ~OmxDecoder();
 
   // MediaResourceManagerClient::EventListener
   virtual void statusChanged();
 
-  bool Init();
+  // The MediaExtractor provides essential information for creating OMXCodec
+  // instance. Such as video/audio codec, we can retrieve them through the
+  // MediaExtractor::getTrackMetaData().
+  // In general cases, the extractor is created by a sp<DataSource> which
+  // connect to a MediaResource like ChannelMediaResource.
+  // Data is read from the MediaResource to create a suitable extractor which
+  // extracts data from a container.
+  // Note: RTSP requires a custom extractor because it doesn't have a container.
+  bool Init(sp<MediaExtractor>& extractor);
+
   bool TryLoad();
   bool IsDormantNeeded();
   bool IsWaitingMediaResources();
   bool AllocateMediaResources();
   void ReleaseMediaResources();
   bool SetVideoFormat();
   bool SetAudioFormat();
 
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspOmxDecoder.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "RtspMediaResource.h"
+#include "RtspOmxDecoder.h"
+#include "RtspOmxReader.h"
+#include "MediaOmxStateMachine.h"
+
+namespace mozilla {
+
+MediaDecoder* RtspOmxDecoder::Clone()
+{
+  return new RtspOmxDecoder();
+}
+
+MediaDecoderStateMachine* RtspOmxDecoder::CreateStateMachine()
+{
+  return new MediaOmxStateMachine(this, new RtspOmxReader(this),
+                                  mResource->IsRealTime());
+}
+
+void RtspOmxDecoder::ApplyStateToStateMachine(PlayState aState)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  GetReentrantMonitor().AssertCurrentThreadIn();
+
+  MediaDecoder::ApplyStateToStateMachine(aState);
+
+
+  // Send play/pause commands here through the nsIStreamingProtocolController
+  // except seek command. We need to clear the decoded/un-decoded buffer data
+  // before sending seek command. So the seek calling path to controller is:
+  // mDecoderStateMachine::Seek-> RtspOmxReader::Seek-> RtspResource::SeekTime->
+  // controller->Seek(). RtspOmxReader::Seek will clear the decoded buffer and
+  // the RtspResource::SeekTime will clear the un-decoded buffer.
+
+  RtspMediaResource* rtspResource = mResource->GetRtspPointer();
+  MOZ_ASSERT(rtspResource);
+
+  nsIStreamingProtocolController* controller =
+    rtspResource->GetMediaStreamController();
+  if (mDecoderStateMachine) {
+    switch (aState) {
+      case PLAY_STATE_PLAYING:
+        if (controller) {
+          controller->Play();
+        }
+        break;
+      case PLAY_STATE_PAUSED:
+        if (controller) {
+          controller->Pause();
+        }
+        break;
+      default:
+        /* No action needed */
+        break;
+    }
+  }
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspOmxDecoder.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 !defined(RtspOmxDecoder_h_)
+#define RtspOmxDecoder_h_
+
+#include "base/basictypes.h"
+#include "MediaDecoder.h"
+
+namespace mozilla {
+
+/* RtspOmxDecoder is a subclass of MediaDecoder but not a subclass of
+ * MediaOmxDecoder. Because the MediaOmxDecoder doesn't extend any functionality
+ * for MediaDecoder.
+ * It creates the RtspOmxReader for the MediaDecoderStateMachine and override
+ * the ApplyStateToStateMachine to send rtsp play/pause command to rtsp server.
+ *
+ * */
+class RtspOmxDecoder : public MediaDecoder
+{
+public:
+  RtspOmxDecoder()
+  : MediaDecoder() {
+    MOZ_COUNT_CTOR(RtspOmxDecoder);
+  }
+
+  ~RtspOmxDecoder() {
+    MOZ_COUNT_DTOR(RtspOmxDecoder);
+  }
+
+  virtual MediaDecoder* Clone() MOZ_OVERRIDE;
+  virtual MediaDecoderStateMachine* CreateStateMachine() MOZ_OVERRIDE;
+  // Called by |ChangeState|, override it to send the Rtsp play/pause commands
+  // through |nsIStreamingProtocolController|.
+  // Call on the main thread only and the lock must be obtained.
+  virtual void ApplyStateToStateMachine(PlayState aState) MOZ_OVERRIDE;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspOmxReader.cpp
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "RtspOmxReader.h"
+
+#include "AbstractMediaDecoder.h"
+#include "MediaDecoderStateMachine.h"
+#include "MPAPI.h"
+#include "mozilla/dom/TimeRanges.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "OmxDecoder.h"
+#include "RtspMediaResource.h"
+#include "RtspOmxDecoder.h"
+#include "VideoUtils.h"
+
+#include <stagefright/MediaExtractor.h>
+#include <stagefright/MediaBufferGroup.h>
+#include <stagefright/MetaData.h>
+
+#define FRAME_DEFAULT_SIZE 1024
+
+using namespace android;
+
+namespace mozilla {
+
+/* class RtspMediaSource : implements MediaSource for OMX.
+ * The decoder thread will trigger the MediaDecodeStateMachine to read a/v frame.
+ * Then RtspOmxReader calls OMX decoder to decode a/v frame. Finally the code
+ * path run into the read() here, it reads un-decoded frame data from mResource
+ * and construct a MediaBuffer for output to OMX decoder.
+ * */
+class RtspMediaSource : public android::MediaSource {
+public:
+  RtspMediaSource(RtspMediaResource *aRtspMediaResource,
+                  ssize_t aTrackIdx,
+                  uint32_t aFrameMaxSize,
+                  const sp<MetaData>& aMeta)
+  : mRtspResource(aRtspMediaResource)
+  , mFormat(aMeta)
+  , mTrackIdx(aTrackIdx)
+  , mMonitor("RtspMediaSource.mMonitor")
+  , mIsStarted(false)
+  , mGroup(nullptr)
+  , mBuffer(nullptr)
+  , mFrameMaxSize(aFrameMaxSize) {
+    MOZ_COUNT_CTOR(RtspMediaSource);
+  };
+  virtual ~RtspMediaSource() {
+    MOZ_COUNT_DTOR(RtspMediaSource);
+  }
+  virtual status_t start(MetaData *params = nullptr) MOZ_FINAL MOZ_OVERRIDE;
+  virtual status_t stop() MOZ_FINAL MOZ_OVERRIDE;
+  virtual sp<MetaData> getFormat() MOZ_FINAL MOZ_OVERRIDE {
+    ReentrantMonitorAutoEnter mon(mMonitor);
+    return mFormat;
+  };
+  virtual status_t read(MediaBuffer **buffer,
+                        const ReadOptions *options = nullptr) MOZ_FINAL MOZ_OVERRIDE ;
+private:
+  nsRefPtr<RtspMediaResource> mRtspResource;
+  sp<MetaData> mFormat;
+  uint32_t mTrackIdx;
+  ReentrantMonitor mMonitor;
+  bool mIsStarted;
+
+  // mGroup owns the mBuffer. mFrameMaxSize is the mBuffer size.
+  // mBuffer is the input buffer for omx decoder.
+  nsAutoPtr<MediaBufferGroup> mGroup;
+  MediaBuffer* mBuffer;
+  uint32_t mFrameMaxSize;
+};
+
+status_t RtspMediaSource::start(MetaData *params)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  if (!mIsStarted) {
+    // RtspMediaSource relinquish the ownership of MediaBuffer |buf| to mGroup.
+    mGroup = new MediaBufferGroup();
+    MediaBuffer* buf = new MediaBuffer(mFrameMaxSize);
+    mGroup->add_buffer(buf);
+    mIsStarted = true;
+  }
+  return OK;
+}
+
+status_t RtspMediaSource::stop()
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  if (mIsStarted) {
+    if (mBuffer) {
+      mBuffer->release();
+      mBuffer = nullptr;
+    }
+    mGroup = nullptr;
+    mIsStarted = false;
+  }
+  return OK;
+}
+
+status_t RtspMediaSource::read(MediaBuffer **out, const ReadOptions *options)
+{
+  ReentrantMonitorAutoEnter mon(mMonitor);
+  NS_ENSURE_TRUE(mIsStarted, MEDIA_ERROR_BASE);
+  NS_ENSURE_TRUE(out, MEDIA_ERROR_BASE);
+  *out = nullptr;
+
+  // Video/audio track's initial frame size is FRAME_DEFAULT_SIZE.
+  // We need to realloc the mBuffer if the mBuffer doesn't have enough space
+  // for next ReadFrameFromTrack function. (actualFrameSize > mFrameMaxSize)
+  status_t err;
+  uint32_t readCount;
+  uint32_t actualFrameSize;
+  uint64_t time;
+  nsresult rv;
+
+  while (1) {
+    err = mGroup->acquire_buffer(&mBuffer);
+    NS_ENSURE_TRUE(err == OK, err);
+
+    rv = mRtspResource->ReadFrameFromTrack((uint8_t *)mBuffer->data(),
+                                           mFrameMaxSize, mTrackIdx, readCount,
+                                           time, actualFrameSize);
+    NS_ENSURE_SUCCESS(rv, ERROR_IO);
+    if (actualFrameSize > mFrameMaxSize) {
+      // release mGroup and mBuffer
+      stop();
+      // re-construct mGroup and mBuffer
+      mFrameMaxSize = actualFrameSize;
+      err = start();
+      NS_ENSURE_TRUE(err == OK, err);
+    } else {
+      // ReadFrameFromTrack success, break the while loop.
+      break;
+    }
+  }
+  mBuffer->set_range(0, readCount);
+  if (NS_SUCCEEDED(rv)) {
+    mBuffer->meta_data()->clear();
+    // fill the meta data
+    mBuffer->meta_data()->setInt64(kKeyTime, time);
+    *out = mBuffer;
+    mBuffer = nullptr;
+    return OK;
+  }
+
+  return ERROR_END_OF_STREAM;
+}
+
+
+// RtspExtractor is a custom extractor for Rtsp stream, whereas the other
+// XXXExtractors are made for container media content.
+// The extractor is used for |OmxDecoder::Init|, it provides the essential
+// information for creating OMXCodec instance.
+// For example, the |getTrackMetaData| returns metadata that includes the
+// codec type.
+class RtspExtractor: public MediaExtractor
+{
+public:
+  virtual size_t countTracks() MOZ_FINAL MOZ_OVERRIDE;
+  virtual sp<android::MediaSource> getTrack(size_t index)
+    MOZ_FINAL MOZ_OVERRIDE;
+  virtual sp<MetaData> getTrackMetaData(
+    size_t index, uint32_t flag = 0) MOZ_FINAL MOZ_OVERRIDE;
+
+  RtspExtractor(RtspMediaResource *aResource)
+    : mRtspResource(aResource) {
+    MOZ_COUNT_CTOR(RtspExtractor);
+    MOZ_ASSERT(aResource);
+    mController = mRtspResource->GetMediaStreamController();
+    MOZ_ASSERT(mController);
+  }
+  virtual ~RtspExtractor() MOZ_OVERRIDE {
+    MOZ_COUNT_DTOR(RtspExtractor);
+  }
+private:
+  // mRtspResource is a pointer to RtspMediaResource. When |getTrack| is called
+  // we use mRtspResource to construct a RtspMediaSource.
+  RtspMediaResource* mRtspResource;
+  // Through the mController in mRtspResource, we can get the essential
+  // information for the extractor.
+  nsRefPtr<nsIStreamingProtocolController> mController;
+};
+
+size_t RtspExtractor::countTracks()
+{
+  uint8_t tracks = 0;
+  if (mController) {
+    mController->GetTotalTracks(&tracks);
+  }
+  return size_t(tracks);
+}
+
+sp<android::MediaSource> RtspExtractor::getTrack(size_t index)
+{
+  NS_ENSURE_TRUE(index < countTracks(), nullptr);
+
+  sp<MetaData> meta = getTrackMetaData(index);
+  sp<android::MediaSource> source = new RtspMediaSource(mRtspResource,
+                                                        index,
+                                                        FRAME_DEFAULT_SIZE,
+                                                        meta);
+  return source;
+}
+
+sp<MetaData> RtspExtractor::getTrackMetaData(size_t index, uint32_t flag)
+{
+  NS_ENSURE_TRUE(index < countTracks(), nullptr);;
+
+  sp<MetaData> meta = new MetaData();
+  nsCOMPtr<nsIStreamingProtocolMetaData> rtspMetadata;
+  mController->GetTrackMetaData(index, getter_AddRefs(rtspMetadata));
+
+  if (rtspMetadata) {
+    // Convert msMeta into meta.
+    // The getter function of nsIStreamingProtocolMetaData will initialize the
+    // metadata values to 0 before setting them.
+    nsCString mime;
+    rtspMetadata->GetMimeType(mime);
+    meta->setCString(kKeyMIMEType, mime.get());
+    uint32_t temp32;
+    rtspMetadata->GetWidth(&temp32);
+    meta->setInt32(kKeyWidth, temp32);
+    rtspMetadata->GetHeight(&temp32);
+    meta->setInt32(kKeyHeight, temp32);
+    rtspMetadata->GetSampleRate(&temp32);
+    meta->setInt32(kKeySampleRate, temp32);
+    rtspMetadata->GetChannelCount(&temp32);
+    meta->setInt32(kKeyChannelCount, temp32);
+    uint64_t temp64;
+    rtspMetadata->GetDuration(&temp64);
+    meta->setInt64(kKeyDuration, temp64);
+
+    nsCString tempCString;
+    rtspMetadata->GetEsdsData(tempCString);
+    if (tempCString.Length()) {
+      meta->setData(kKeyESDS, 0, tempCString.get(), tempCString.Length());
+    }
+    rtspMetadata->GetAvccData(tempCString);
+    if (tempCString.Length()) {
+      meta->setData(kKeyAVCC, 0, tempCString.get(), tempCString.Length());
+    }
+  }
+  return meta;
+}
+
+nsresult RtspOmxReader::InitOmxDecoder()
+{
+  if (!mOmxDecoder.get()) {
+    NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null.");
+    NS_ASSERTION(mDecoder->GetResource(),
+                 "RtspOmxReader mDecoder->GetResource() is null.");
+    sp<MediaExtractor> extractor = new RtspExtractor(mRtspResource);
+    mOmxDecoder = new OmxDecoder(mDecoder->GetResource(), mDecoder);
+    if (!mOmxDecoder->Init(extractor)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  return NS_OK;
+}
+
+nsresult RtspOmxReader::Seek(int64_t aTime, int64_t aStartTime,
+                             int64_t aEndTime, int64_t aCurrentTime)
+{
+  // The seek function of Rtsp is time-based, we call the SeekTime function in
+  // RtspMediaResource. The SeekTime function finally send a seek command to
+  // Rtsp stream server through network and also clear the buffer data in
+  // RtspMediaResource.
+  if (mRtspResource) {
+    mRtspResource->SeekTime(aTime);
+  }
+
+  // Call |MediaOmxReader::Seek| to notify the OMX decoder we are performing a
+  // seek operation. The function will clear the |mVideoQueue| and |mAudioQueue|
+  // that store the decoded data and also call the |DecodeToTarget| to pass
+  // the seek time to OMX a/v decoders.
+  return MediaOmxReader::Seek(aTime, aStartTime, aEndTime, aCurrentTime);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/omx/RtspOmxReader.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 !defined(RtspOmxReader_h_)
+#define RtspOmxReader_h_
+
+#include "MediaResource.h"
+#include "MediaDecoderReader.h"
+#include "MediaOmxReader.h"
+
+namespace mozilla {
+
+namespace dom {
+  class TimeRanges;
+}
+
+class AbstractMediaDecoder;
+class RtspMediaResource;
+
+/* RtspOmxReader is a subclass of MediaOmxReader.
+ * The major reason that RtspOmxReader inherit from MediaOmxReader is the
+ * same video/audio decoding logic we can reuse.
+ */
+class RtspOmxReader : public MediaOmxReader
+{
+protected:
+  // Provide a Rtsp extractor.
+  nsresult InitOmxDecoder() MOZ_FINAL MOZ_OVERRIDE;
+
+public:
+  RtspOmxReader(AbstractMediaDecoder* aDecoder)
+    : MediaOmxReader(aDecoder) {
+    MOZ_COUNT_CTOR(RtspOmxReader);
+    NS_ASSERTION(mDecoder, "RtspOmxReader mDecoder is null.");
+    NS_ASSERTION(mDecoder->GetResource(),
+                 "RtspOmxReader mDecoder->GetResource() is null.");
+    mRtspResource = mDecoder->GetResource()->GetRtspPointer();
+    MOZ_ASSERT(mRtspResource);
+  }
+
+  virtual ~RtspOmxReader() MOZ_OVERRIDE {
+    MOZ_COUNT_DTOR(RtspOmxReader);
+  }
+
+  // Implement a time-based seek instead of byte-based..
+  virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
+                        int64_t aCurrentTime) MOZ_FINAL MOZ_OVERRIDE;
+
+  // Override GetBuffered() to do nothing for below reasons:
+  // 1. Because the Rtsp stream is a/v separated. The buffered data in a/v
+  // tracks are not consistent with time stamp.
+  // For example: audio buffer: 1~2s, video buffer: 1.5~2.5s
+  // 2. Since the Rtsp is a realtime streaming, the buffer we made for
+  // RtspMediaResource is quite small. The small buffer implies the time ranges
+  // we returned are not useful for the MediaDecodeStateMachine. Unlike the
+  // ChannelMediaResource, it has a "cache" that can store the whole streaming
+  // data so the |GetBuffered| function can retrieve useful time ranges.
+  virtual nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered,
+                               int64_t aStartTime) MOZ_FINAL MOZ_OVERRIDE {
+    return NS_OK;
+  }
+
+private:
+  // A pointer to RtspMediaResource for calling the Rtsp specific function.
+  // The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder
+  // holds the MediaDecoderStateMachine and RtspMediaResource.
+  // And MediaDecoderStateMachine holds this RtspOmxReader.
+  RtspMediaResource* mRtspResource;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/media/omx/moz.build
+++ b/content/media/omx/moz.build
@@ -13,12 +13,22 @@ EXPORTS += [
 
 CPP_SOURCES += [
     'MediaOmxDecoder.cpp',
     'MediaOmxReader.cpp',
     'OmxDecoder.cpp',
     'OMXCodecProxy.cpp',
 ]
 
+if CONFIG['MOZ_RTSP']:
+    EXPORTS += [
+        'RtspOmxDecoder.h',
+        'RtspOmxReader.h',
+    ]
+    CPP_SOURCES += [
+        'RtspOmxDecoder.cpp',
+        'RtspOmxReader.cpp',
+    ]
+
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'gkconomx_s'
 
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -366,17 +366,17 @@ nsXBLProtoImplField::InstallAccessors(JS
   }
   js::SetFunctionNativeReserved(set, XBLPROTO_SLOT, wrappedClassObj);
   js::SetFunctionNativeReserved(set, FIELD_SLOT,
                                 JS::StringValue(JSID_TO_STRING(id)));
 
   // Now, re-enter the class object's scope, wrap the getters/setters, and define
   // them there.
   JSAutoCompartment ac2(aCx, aTargetClassObject);
-  if (!JS_WrapObject(aCx, get.address()) || !JS_WrapObject(aCx, set.address()) ||
+  if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set) ||
       !JS_WrapId(aCx, id.address()))
   {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, JS::UndefinedValue(),
                                JS_DATA_TO_FUNC_PTR(JSPropertyOp, get.get()),
                                JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, set.get()),
@@ -419,17 +419,17 @@ nsXBLProtoImplField::InstallField(nsIScr
 
   // First, enter the xbl scope, wrap the node, and use that as the scope for
   // the evaluation.
   JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScope(cx, aBoundNode));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
   JSAutoCompartment ac(cx, scopeObject);
 
   JS::Rooted<JSObject*> wrappedNode(cx, aBoundNode);
-  if (!JS_WrapObject(cx, wrappedNode.address()))
+  if (!JS_WrapObject(cx, &wrappedNode))
       return NS_ERROR_OUT_OF_MEMORY;
 
   JS::Rooted<JS::Value> result(cx);
   JS::CompileOptions options(cx);
   options.setFileAndLine(uriSpec.get(), mLineNumber)
          .setVersion(JSVERSION_LATEST);
   rv = aContext->EvaluateString(nsDependentString(mFieldText,
                                                   mFieldTextLength),
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -116,17 +116,17 @@ nsXBLProtoImplMethod::InstallMember(JSCo
     JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, jsMethodObject, scopeObject));
     if (!method) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Then, enter the content compartment, wrap the method pointer, and define
     // the wrapped version on the class object.
     JSAutoCompartment ac2(aCx, aTargetClassObject);
-    if (!JS_WrapObject(aCx, method.address()))
+    if (!JS_WrapObject(aCx, &method))
       return NS_ERROR_OUT_OF_MEMORY;
 
     JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*method));
     if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
                                static_cast<const jschar*>(mName),
                                name.Length(), value,
                                nullptr, nullptr, JSPROP_ENUMERATE)) {
       return NS_ERROR_OUT_OF_MEMORY;
@@ -322,17 +322,17 @@ nsXBLProtoImplAnonymousMethod::Execute(n
     return NS_ERROR_UNEXPECTED;
   MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
 
   JS::Rooted<JSObject*> thisObject(cx, &v.toObject());
   JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScope(cx, globalObject));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   JSAutoCompartment ac(cx, scopeObject);
-  if (!JS_WrapObject(cx, thisObject.address()))
+  if (!JS_WrapObject(cx, &thisObject))
       return NS_ERROR_OUT_OF_MEMORY;
 
   // Clone the function object, using thisObject as the parent so "this" is in
   // the scope chain of the resulting function (for backwards compat to the
   // days when this was an event handler).
   JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, GetCompiledMethod(), thisObject));
   if (!method)
     return NS_ERROR_OUT_OF_MEMORY;
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -147,18 +147,18 @@ nsXBLProtoImplProperty::InstallMember(JS
       if (!(setter = ::JS_CloneFunctionObject(aCx, mSetter.GetJSFunction(), scopeObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Now, enter the content compartment, wrap the getter/setter, and define
     // them on the class object.
     JSAutoCompartment ac2(aCx, aTargetClassObject);
     nsDependentString name(mName);
-    if (!JS_WrapObject(aCx, getter.address()) ||
-        !JS_WrapObject(aCx, setter.address()) ||
+    if (!JS_WrapObject(aCx, &getter) ||
+        !JS_WrapObject(aCx, &setter) ||
         !::JS_DefineUCProperty(aCx, aTargetClassObject,
                                static_cast<const jschar*>(mName),
                                name.Length(), JSVAL_VOID,
                                JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()),
                                JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get()),
                                mJSAttributes))
       return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -294,17 +294,17 @@ nsXBLPrototypeHandler::ExecuteHandler(Ev
   // Bind it to the bound element. Note that if we're using a separate XBL scope,
   // we'll actually be binding the event handler to a cross-compartment wrapper
   // to the bound element's reflector.
 
   // First, enter our XBL scope. This is where the generic handler should have
   // been compiled, above.
   JSAutoCompartment ac(cx, scopeObject);
   JS::Rooted<JSObject*> genericHandler(cx, handler.get());
-  bool ok = JS_WrapObject(cx, genericHandler.address());
+  bool ok = JS_WrapObject(cx, &genericHandler);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   MOZ_ASSERT(!js::IsCrossCompartmentWrapper(genericHandler));
 
   // Wrap the native into the XBL scope. This creates a reflector in the document
   // scope if one doesn't already exist, and potentially wraps it cross-
   // compartment into our scope (via aAllowWrapping=true).
   JS::Rooted<JS::Value> targetV(cx, JS::UndefinedValue());
   rv = nsContentUtils::WrapNative(cx, scopeObject, scriptTarget, targetV.address(), nullptr,
@@ -312,17 +312,17 @@ nsXBLPrototypeHandler::ExecuteHandler(Ev
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Next, clone the generic handler to be parented to the target.
   JS::Rooted<JSObject*> bound(cx, JS_CloneFunctionObject(cx, genericHandler, &targetV.toObject()));
   NS_ENSURE_TRUE(bound, NS_ERROR_FAILURE);
 
   // Now, wrap the bound handler into the content compartment and use it.
   JSAutoCompartment ac2(cx, globalObject);
-  if (!JS_WrapObject(cx, bound.address())) {
+  if (!JS_WrapObject(cx, &bound)) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<EventHandlerNonNull> handlerCallback =
     new EventHandlerNonNull(bound);
 
   nsEventHandler eventHandler(handlerCallback);
 
@@ -388,17 +388,17 @@ nsXBLPrototypeHandler::EnsureEventHandle
                                            nsAtomCString(aName), argCount,
                                            argNames, handlerText, handlerFun.address());
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(handlerFun, NS_ERROR_FAILURE);
 
   // Wrap the handler into the content scope, since we're about to stash it
   // on the DOM window and such.
   JSAutoCompartment ac2(cx, globalObject);
-  bool ok = JS_WrapObject(cx, handlerFun.address());
+  bool ok = JS_WrapObject(cx, &handlerFun);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   aHandler.set(handlerFun);
   NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE);
 
   if (pWindow) {
     pWindow->CacheXBLPrototypeHandler(this, aHandler);
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3185,72 +3185,21 @@ nsDocShell::FindItemWithName(const PRUni
 
         if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
             foundItem = nullptr;
         }
 
         // DoFindItemWithName only returns active items and we don't check if
         // the item is active for the special cases.
         if (foundItem) {
-
-            // If our document is sandboxed, we need to do some extra checks.
-            uint32_t sandboxFlags = 0;
-
-            nsCOMPtr<nsIDocument> doc = do_GetInterface(aOriginalRequestor);
-
-            if (doc) {
-                sandboxFlags = doc->GetSandboxFlags();
+            if (IsSandboxedFrom(foundItem, aOriginalRequestor)) {
+                return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+            } else {
+                foundItem.swap(*_retval);
             }
-
-            if (sandboxFlags) {
-                nsCOMPtr<nsIDocShellTreeItem> root;
-                GetSameTypeRootTreeItem(getter_AddRefs(root));
-
-                // Is the found item not a top level browsing context and not ourself ?
-                nsCOMPtr<nsIDocShellTreeItem> selfAsItem = static_cast<nsIDocShellTreeItem *>(this);
-                if (foundItem != root && foundItem != selfAsItem) {
-                    // Are we an ancestor of the foundItem ?
-                    bool isAncestor = false;
-
-                    nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
-                    foundItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
-                    while (parentAsItem) {
-                        if (parentAsItem == selfAsItem) {
-                            isAncestor = true;
-                            break;
-                        }
-                        nsCOMPtr<nsIDocShellTreeItem> tmp = parentAsItem;
-                        tmp->GetSameTypeParent(getter_AddRefs(parentAsItem));
-                    }
-
-                    if (!isAncestor) {
-                        // No, we are not an ancestor and our document is
-                        // sandboxed, we can't allow this.
-                        foundItem = nullptr;
-                    }
-                } else {
-                    // Top level browsing context - is it an ancestor of ours ?
-                    nsCOMPtr<nsIDocShellTreeItem> tmp;
-                    GetSameTypeParent(getter_AddRefs(tmp));
-
-                    while (tmp) {
-                        if (tmp && tmp == foundItem) {
-                            // This is an ancestor, and we are sandboxed.
-                            // Unless allow-top-navigation is set, we can't allow this.
-                            if (sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) {
-                                foundItem = nullptr;
-                            }
-                            break;
-                        }
-                        tmp->GetParent(getter_AddRefs(tmp));
-                    }
-                }
-            }
-
-            foundItem.swap(*_retval);
         }
         return NS_OK;
     }
 }
 
 nsresult
 nsDocShell::DoFindItemWithName(const PRUnichar* aName,
                                nsISupports* aRequestor,
@@ -3313,16 +3262,80 @@ nsDocShell::DoFindItemWithName(const PRU
     if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
         return mTreeOwner->
             FindItemWithName(aName, this, aOriginalRequestor, _retval);
     }
 
     return NS_OK;
 }
 
+/* static */
+bool
+nsDocShell::IsSandboxedFrom(nsIDocShellTreeItem* aTargetItem,
+                            nsIDocShellTreeItem* aAccessingItem)
+{
+    // aAccessingItem cannot be sandboxed from itself.
+    if (SameCOMIdentity(aTargetItem, aAccessingItem)) {
+        return false;
+    }
+
+    uint32_t sandboxFlags = 0;
+
+    nsCOMPtr<nsIDocument> doc = do_GetInterface(aAccessingItem);
+    if (doc) {
+        sandboxFlags = doc->GetSandboxFlags();
+    }
+
+    // If no flags, aAccessingItem is not sandboxed at all.
+    if (!sandboxFlags) {
+        return false;
+    }
+
+    // If aTargetItem has an ancestor, it is not top level.
+    nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
+    aTargetItem->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
+    if (ancestorOfTarget) {
+        do {
+            // aAccessingItem is not sandboxed if it is an ancestor of target.
+            if (SameCOMIdentity(aAccessingItem, ancestorOfTarget)) {
+                return false;
+            }
+            nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
+            ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
+            tempTreeItem.swap(ancestorOfTarget);
+        } while (ancestorOfTarget);
+
+        // Otherwise, aAccessingItem is sandboxed from aTargetItem.
+        return true;
+    }
+
+    // aTargetItem is top level, is aAccessingItem the "one permitted sandboxed
+    // navigator", i.e. did aAccessingItem open aTargetItem?
+    nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(aTargetItem);
+    nsCOMPtr<nsIDocShell> permittedNavigator;
+    targetDocShell->
+        GetOnePermittedSandboxedNavigator(getter_AddRefs(permittedNavigator));
+    if (SameCOMIdentity(aAccessingItem, permittedNavigator)) {
+        return false;
+    }
+
+    // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, aAccessingItem is
+    // not sandboxed from its top.
+    if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
+        nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
+        aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
+        if (SameCOMIdentity(aTargetItem, rootTreeItem)) {
+            return false;
+        }
+    }
+
+    // Otherwise, aAccessingItem is sandboxed from aTargetItem.
+    return true;
+}
+
 NS_IMETHODIMP
 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
 {
     NS_ENSURE_ARG_POINTER(aTreeOwner);
 
     *aTreeOwner = mTreeOwner;
     NS_IF_ADDREF(*aTreeOwner);
     return NS_OK;
@@ -5050,16 +5063,18 @@ nsDocShell::Destroy()
         if (shPrivate) {
             shPrivate->EvictAllContentViewers();
         }
         mSessionHistory = nullptr;
     }
 
     SetTreeOwner(nullptr);
 
+    mOnePermittedSandboxedNavigator = nullptr;
+
     // required to break ref cycle
     mSecurityUI = nullptr;
 
     // Cancel any timers that were set for this docshell; this is needed
     // to break the cycle between us and the timers.
     CancelRefreshURITimers();
 
     if (mInPrivateBrowsing) {
@@ -5399,16 +5414,41 @@ nsDocShell::SetSandboxFlags(uint32_t aSa
 NS_IMETHODIMP
 nsDocShell::GetSandboxFlags(uint32_t  *aSandboxFlags)
 {
     *aSandboxFlags = mSandboxFlags;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::SetOnePermittedSandboxedNavigator(nsIDocShell* aSandboxedNavigator)
+{
+    if (mOnePermittedSandboxedNavigator) {
+        NS_ERROR("One Permitted Sandboxed Navigator should only be set once.");
+        return NS_OK;
+    }
+
+    mOnePermittedSandboxedNavigator = do_GetWeakReference(aSandboxedNavigator);
+    NS_ASSERTION(mOnePermittedSandboxedNavigator,
+             "One Permitted Sandboxed Navigator must support weak references.");
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOnePermittedSandboxedNavigator(nsIDocShell** aSandboxedNavigator)
+{
+    NS_ENSURE_ARG_POINTER(aSandboxedNavigator);
+    nsCOMPtr<nsIDocShell> permittedNavigator =
+        do_QueryReferent(mOnePermittedSandboxedNavigator);
+    NS_IF_ADDREF(*aSandboxedNavigator = permittedNavigator);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags)
 {
     mDefaultLoadFlags = aDefaultLoadFlags;
 
     // Tell the load group to set these flags all requests in the group
     if (mLoadGroup) {
         mLoadGroup->SetDefaultLoadFlags(aDefaultLoadFlags);
     } else {
@@ -8714,18 +8754,20 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
     uint32_t contentType;
     bool isNewDocShell = false;
     bool isTargetTopLevelDocShell = false;
     nsCOMPtr<nsIDocShell> targetDocShell;
     if (aWindowTarget && *aWindowTarget) {
         // Locate the target DocShell.
         nsCOMPtr<nsIDocShellTreeItem> targetItem;
-        FindItemWithName(aWindowTarget, nullptr, this,
-                         getter_AddRefs(targetItem));
+        if (FindItemWithName(aWindowTarget, nullptr, this,
+               getter_AddRefs(targetItem)) == NS_ERROR_DOM_INVALID_ACCESS_ERR) {
+            return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+        }
 
         targetDocShell = do_QueryInterface(targetItem);
         // If the targetDocShell doesn't exist, then this is a new docShell
         // and we should consider this a TYPE_DOCUMENT load
         isNewDocShell = !targetDocShell;
 
         // If the targetDocShell and the rootDocShell are the same, then the
         // targetDocShell is the top level document and hence we should
@@ -8842,29 +8884,27 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     if (aWindowTarget && *aWindowTarget) {
         // We've already done our owner-inheriting.  Mask out that bit, so we
         // don't try inheriting an owner from the target window if we came up
         // with a null owner above.
         aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_INHERIT_OWNER;
         
         bool isNewWindow = false;
         if (!targetDocShell) {
-            // If the docshell's document is sandboxed and was trying to
-            // navigate/load a frame it wasn't allowed to access, the
-            // FindItemWithName above will have returned null for the target
-            // item - we don't want to actually open a new window in this case
-            // though. Check if we are sandboxed and bail out here if so.
+            // If the docshell's document is sandboxed, only open a new window
+            // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
+            // (i.e. if allow-popups is specified)
             NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
             nsIDocument* doc = mContentViewer->GetDocument();
             uint32_t sandboxFlags = 0;
 
             if (doc) {
                 sandboxFlags = doc->GetSandboxFlags();
-                if (sandboxFlags & SANDBOXED_NAVIGATION) {
-                    return NS_ERROR_FAILURE;
+                if (sandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
+                    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
                 }
             }
 
             nsCOMPtr<nsPIDOMWindow> win =
                 do_GetInterface(GetAsSupports(this));
             NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
 
             nsDependentString name(aWindowTarget);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -766,16 +766,17 @@ protected:
     int32_t                    mItemType;
 
     // Index into the SHTransaction list, indicating the previous and current
     // transaction at the time that this DocShell begins to load
     int32_t                    mPreviousTransIndex;
     int32_t                    mLoadedTransIndex;
 
     uint32_t                   mSandboxFlags;
+    nsWeakPtr                  mOnePermittedSandboxedNavigator;
 
     // mFullscreenAllowed stores how we determine whether fullscreen is allowed
     // when GetFullscreenAllowed() is called. Fullscreen is allowed in a
     // docshell when all containing iframes have the allowfullscreen
     // attribute set to true. When mFullscreenAllowed is CHECK_ATTRIBUTES
     // we check this docshell's containing frame for the allowfullscreen
     // attribute, and recurse onto the parent docshell to ensure all containing
     // frames also have the allowfullscreen attribute. If we find an ancestor
@@ -875,16 +876,20 @@ private:
 
     // Separate function to do the actual name (i.e. not _top, _self etc.)
     // searching for FindItemWithName.
     nsresult DoFindItemWithName(const PRUnichar* aName,
                                 nsISupports* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
                                 nsIDocShellTreeItem** _retval);
 
+    // Check whether accessing item is sandboxed from the target item.
+    static bool IsSandboxedFrom(nsIDocShellTreeItem* aTargetItem,
+                                nsIDocShellTreeItem* aAccessingItem);
+
 #ifdef DEBUG
     // We're counting the number of |nsDocShells| to help find leaks
     static unsigned long gNumberOfDocShells;
 #endif /* DEBUG */
 
 public:
     class InterfaceRequestorProxy : public nsIInterfaceRequestor {
     public:
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -38,17 +38,17 @@ interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(5f4d82fc-3220-4f7e-9b00-626f1033318a)]
+[scriptable, builtinclass, uuid(4ca172c3-67bf-4e6d-89a3-cbfb929c370d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -794,16 +794,23 @@ interface nsIDocShell : nsIDocShellTreeI
    * loaded.
    * The sandbox flags of a document depend on the sandbox flags on its
    * docshell and of its parent document, if any.
    * See nsSandboxFlags.h for the possible flags.
    */
   attribute unsigned long sandboxFlags;
 
   /**
+   * When a new browsing context is opened by a sandboxed document, it needs to
+   * keep track of the browsing context that opened it, so that it can be
+   * navigated by it.  This is the "one permitted sandboxed navigator".
+   */
+  attribute nsIDocShell onePermittedSandboxedNavigator;
+
+  /**
    * This member variable determines whether a document has Mixed Active Content that
    * was initially blocked from loading, but the user has choosen to override the
    * block and allow the content to load. mMixedContentChannel is set to the document's
    * channel when the user allows mixed content. The nsMixedContentBlocker content policy
    * checks if the document's root channel matches the mMixedContentChannel.  If it matches,
    * then Mixed Content is loaded.  If it does match, mixed content is blocked.
    *
    * A match implies that there is definitely mixed active content on a page that was
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -4,64 +4,67 @@
 #ifdef XP_WIN
 #include <windows.h>
 #else
 #include <unistd.h>
 #endif
 
 #include "TestHarness.h"
 
-// Work around the fact that the nsWeakPtr, used by AudioChannelService.h, is
-// not exposed to consumers outside the internal API.
-#include "nsIWeakReference.h"
-typedef nsCOMPtr<nsIWeakReference> nsWeakPtr;
-
+#include "nsWeakReference.h"
 #include "AudioChannelService.h"
 #include "AudioChannelAgent.h"
 
 #define TEST_ENSURE_BASE(_test, _msg)       \
   PR_BEGIN_MACRO                            \
-    if (!(_test)) {                           \
+    if (!(_test)) {                         \
       fail(_msg);                           \
       return NS_ERROR_FAILURE;              \
     } else {                                \
       passed(_msg);                         \
     }                                       \
   PR_END_MACRO
 
 using namespace mozilla::dom;
 
-class Agent : public nsIAudioChannelAgentCallback
+class Agent : public nsIAudioChannelAgentCallback,
+              public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
 
   Agent(AudioChannelType aType)
   : mType(aType)
   , mWaitCallback(false)
   , mRegistered(false)
   , mCanPlay(AUDIO_CHANNEL_STATE_MUTED)
   {
     mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
   }
 
-  virtual ~Agent() {}
+  virtual ~Agent()
+  {
+    if (mRegistered) {
+      StopPlaying();
+    }
+  }
 
   nsresult Init()
   {
-    nsresult rv = mAgent->Init(mType, this);
+    nsresult rv = mAgent->InitWithWeakCallback(mType, this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return mAgent->SetVisibilityState(false);
   }
 
   nsresult StartPlaying(AudioChannelState *_ret)
   {
-    if (mRegistered)
+    if (mRegistered) {
       StopPlaying();
+    }
 
     nsresult rv = mAgent->StartPlaying((int32_t *)_ret);
     mRegistered = true;
     return rv;
   }
 
   nsresult StopPlaying()
   {
@@ -107,49 +110,49 @@ public:
       if (loop++ == 5) {
         TEST_ENSURE_BASE(false, "GetCanPlay timeout");
       }
     }
     *_ret = mCanPlay;
     return NS_OK;
   }
 
-  nsCOMPtr<AudioChannelAgent> mAgent;
+  nsRefPtr<AudioChannelAgent> mAgent;
   AudioChannelType mType;
   bool mWaitCallback;
   bool mRegistered;
   AudioChannelState mCanPlay;
 };
 
-NS_IMPL_ISUPPORTS1(Agent, nsIAudioChannelAgentCallback)
+NS_IMPL_ISUPPORTS2(Agent, nsIAudioChannelAgentCallback,
+                   nsISupportsWeakReference)
 
 nsresult
 TestDoubleStartPlaying()
 {
-  nsCOMPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
 
   nsresult rv = agent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent->mAgent->StartPlaying((int32_t *)&playable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent->mAgent->StartPlaying((int32_t *)&playable);
   TEST_ENSURE_BASE(NS_FAILED(rv),
     "Test0: StartPlaying calling twice must return error");
 
-  agent->mAgent->StopPlaying();
   return NS_OK;
 }
 
 nsresult
 TestOneNormalChannel()
 {
-  nsCOMPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
   nsresult rv = agent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
     "Test1: A normal channel unvisible agent must be muted");
@@ -157,28 +160,27 @@ TestOneNormalChannel()
   rv = agent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent->GetCanPlay(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test1: A normal channel visible agent must be playable");
 
-  agent->StopPlaying();
   return rv;
 }
 
 nsresult
 TestTwoNormalChannels()
 {
-  nsCOMPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_NORMAL);
   nsresult rv = agent1->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_NORMAL);
   rv = agent2->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent1->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
     "Test2: A normal channel unvisible agent1 must be muted");
@@ -199,29 +201,27 @@ TestTwoNormalChannels()
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test2: A normal channel visible agent1 must be playable");
 
   rv = agent2->GetCanPlay(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test2: A normal channel visible agent2 must be playable");
 
-  agent1->StopPlaying();
-  agent2->StopPlaying();
   return rv;
 }
 
 nsresult
 TestContentChannels()
 {
-  nsCOMPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_CONTENT);
   nsresult rv = agent1->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_CONTENT);
   rv = agent2->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // All content channels in the foreground can be allowed to play
   rv = agent1->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent2->SetVisibilityState(true);
@@ -286,33 +286,31 @@ TestContentChannels()
     "from background state");
 
   rv = agent2->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test3: A content channel unvisible agent2 must be playable "
     "from background state");
 
-  agent1->StopPlaying();
-  agent2->StopPlaying();
   return rv;
 }
 
 nsresult
 TestFadedState()
 {
-  nsCOMPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
   nsresult rv = normalAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
   rv = contentAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
+  nsRefPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
   rv = notificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = normalAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = contentAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -367,49 +365,47 @@ TestFadedState()
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test4: A content channel unvisible agent must be playable "
     "because of notification channel is stopped");
 
   rv = contentAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  normalAgent->StopPlaying();
-  contentAgent->StopPlaying();
   return rv;
 }
 
 nsresult
 TestPriorities()
 {
-  nsCOMPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
   nsresult rv = normalAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
   rv = contentAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
+  nsRefPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
   rv = notificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> alarmAgent = new Agent(AUDIO_CHANNEL_ALARM);
+  nsRefPtr<Agent> alarmAgent = new Agent(AUDIO_CHANNEL_ALARM);
   rv = alarmAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> telephonyAgent = new Agent(AUDIO_CHANNEL_TELEPHONY);
+  nsRefPtr<Agent> telephonyAgent = new Agent(AUDIO_CHANNEL_TELEPHONY);
   rv = telephonyAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> ringerAgent = new Agent(AUDIO_CHANNEL_RINGER);
+  nsRefPtr<Agent> ringerAgent = new Agent(AUDIO_CHANNEL_RINGER);
   rv = ringerAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<Agent> pNotificationAgent =
+  nsRefPtr<Agent> pNotificationAgent =
     new Agent(AUDIO_CHANNEL_PUBLICNOTIFICATION);
   rv = pNotificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
 
   rv = normalAgent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -526,44 +522,45 @@ TestPriorities()
   rv = pNotificationAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = pNotificationAgent->GetCanPlay(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL,
     "Test5: A pNotification channel visible agent must be playable");
 
-  normalAgent->StopPlaying();
-  contentAgent->StopPlaying();
-  alarmAgent->StopPlaying();
-  telephonyAgent->StopPlaying();
-  ringerAgent->StopPlaying();
-  pNotificationAgent->StopPlaying();
   return rv;
 }
 
 int main(int argc, char** argv)
 {
   ScopedXPCOM xpcom("AudioChannelService");
-  if (xpcom.failed())
+  if (xpcom.failed()) {
     return 1;
+  }
 
-  if (NS_FAILED(TestDoubleStartPlaying()))
+  if (NS_FAILED(TestDoubleStartPlaying())) {
     return 1;
+  }
 
-  if (NS_FAILED(TestOneNormalChannel()))
+  if (NS_FAILED(TestOneNormalChannel())) {
     return 1;
+  }
 
-  if (NS_FAILED(TestTwoNormalChannels()))
+  if (NS_FAILED(TestTwoNormalChannels())) {
     return 1;
+  }
 
-  if (NS_FAILED(TestContentChannels()))
+  if (NS_FAILED(TestContentChannels())) {
     return 1;
+  }
 
-  if (NS_FAILED(TestFadedState()))
+  if (NS_FAILED(TestFadedState())) {
     return 1;
+  }
 
-  if (NS_FAILED(TestPriorities()))
+  if (NS_FAILED(TestPriorities())) {
     return 1;
+  }
 
   return 0;
 }
 
--- a/dom/base/MessagePort.cpp
+++ b/dom/base/MessagePort.cpp
@@ -134,17 +134,17 @@ PostMessageReadStructuredClone(JSContext
   if (tag == SCTAG_DOM_MESSAGEPORT) {
     NS_ASSERTION(!data, "Data should be empty");
 
     MessagePort* port;
     if (JS_ReadBytes(reader, &port, sizeof(port))) {
       JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
       if (global) {
         JS::Rooted<JSObject*> obj(cx, port->WrapObject(cx, global));
-        if (JS_WrapObject(cx, obj.address())) {
+        if (JS_WrapObject(cx, &obj)) {
           port->BindToOwner(scInfo->mPort->GetOwner());
           return obj;
         }
       }
     }
   }
 
   const JSStructuredCloneCallbacks* runtimeCallbacks =
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1549,17 +1549,17 @@ Navigator::DoNewResolve(JSContext* aCx, 
       }
 
       domObject = construct(aCx, naviObj);
       if (!domObject) {
         return Throw(aCx, NS_ERROR_FAILURE);
       }
     }
 
-    if (!JS_WrapObject(aCx, domObject.address())) {
+    if (!JS_WrapObject(aCx, &domObject)) {
       return false;
     }
 
     aValue.setObject(*domObject);
     return true;
   }
 
   NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeNavigatorProperty,
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -2780,17 +2780,17 @@ GetXPCProto(nsIXPConnect *aXPConnect, JS
   NS_ENSURE_TRUE(ci, NS_ERROR_UNEXPECTED);
 
   nsresult rv =
     aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci,
                                           aProto);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JS::Rooted<JSObject*> proto_obj(cx, (*aProto)->GetJSObject());
-  if (!JS_WrapObject(cx, proto_obj.address())) {
+  if (!JS_WrapObject(cx, &proto_obj)) {
     return NS_ERROR_FAILURE;
   }
 
   NS_IF_RELEASE(*aProto);
   return aXPConnect->HoldObject(cx, proto_obj, aProto);
 }
 
 // Either ci_data must be non-null or name_struct must be non-null and of type
@@ -2917,17 +2917,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec
       JS::Rooted<JSObject*> xpc_proto_proto(cx);
       if (!::JS_GetPrototype(cx, dot_prototype, &xpc_proto_proto)) {
         return NS_ERROR_UNEXPECTED;
       }
 
       if (proto &&
           (!xpc_proto_proto ||
            JS_GetClass(xpc_proto_proto) == sObjectClass)) {
-        if (!JS_WrapObject(cx, proto.address()) ||
+        if (!JS_WrapObject(cx, &proto) ||
             !JS_SetPrototype(cx, dot_prototype, proto)) {
           return NS_ERROR_UNEXPECTED;
         }
       }
     } else {
       JSAutoCompartment ac(cx, winobj);
       if (!proto) {
         proto = JS_GetObjectPrototype(cx, winobj);
@@ -3066,17 +3066,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
                                                        defineOnGlobal));
       if (!interfaceObject) {
         return NS_ERROR_FAILURE;
       }
 
       if (defineOnXray) {
         // This really should be handled by the Xray for the window.
         ac.destroy();
-        if (!JS_WrapObject(cx, interfaceObject.address()) ||
+        if (!JS_WrapObject(cx, &interfaceObject) ||
             !JS_DefinePropertyById(cx, obj, id,
                                    JS::ObjectValue(*interfaceObject), JS_PropertyStub,
                                    JS_StrictPropertyStub, 0)) {
           return NS_ERROR_FAILURE;
         }
 
       }
 
@@ -3639,17 +3639,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     JS::Rooted<JSObject*> funObj(cx);
     JSFunction *fun = ::JS_NewFunction(cx, ContentWindowGetter, 0, 0,
                                        obj, "_content");
     if (!fun) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     funObj = ::JS_GetFunctionObject(fun);
 
-    if (!JS_WrapObject(cx, funObj.address()) ||
+    if (!JS_WrapObject(cx, &funObj) ||
         !JS_DefinePropertyById(cx, obj, id, JSVAL_VOID,
                                JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
                                JS_StrictPropertyStub,
                                JSPROP_ENUMERATE | JSPROP_GETTER |
                                JSPROP_SHARED)) {
       return NS_ERROR_FAILURE;
     }
 
@@ -3740,17 +3740,17 @@ nsWindowSH::OuterObject(nsIXPConnectWrap
 
   JS::Rooted<JSObject*> winObj(cx, win->FastGetGlobalJSObject());
   MOZ_ASSERT(winObj);
 
   // Note that while |wrapper| is same-compartment with cx, the outer window
   // might not be. If we're running script in an inactive scope and evalute
   // |this|, the outer window is actually a cross-compartment wrapper. So we
   // need to wrap here.
-  if (!JS_WrapObject(cx, winObj.address())) {
+  if (!JS_WrapObject(cx, &winObj)) {
     *_retval = nullptr;
     return NS_ERROR_UNEXPECTED;
   }
 
   *_retval = winObj;
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4000,18 +4000,18 @@ nsGlobalWindow::SetOpener(nsIDOMWindow* 
     // expandos on Xrays as needed.
 
     JS::Rooted<JSObject*> otherObj(cx, glob->GetGlobalJSObject());
     NS_ENSURE_STATE(otherObj);
 
     JS::Rooted<JSObject*> thisObj(cx, mJSObject);
     NS_ENSURE_STATE(mJSObject);
 
-    if (!JS_WrapObject(cx, otherObj.address()) ||
-        !JS_WrapObject(cx, thisObj.address()) ||
+    if (!JS_WrapObject(cx, &otherObj) ||
+        !JS_WrapObject(cx, &thisObj) ||
         !JS_DefineProperty(cx, thisObj, "opener", JS::ObjectValue(*otherObj),
                            JS_PropertyStub, JS_StrictPropertyStub,
                            JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
@@ -6817,17 +6817,17 @@ PostMessageReadStructuredClone(JSContext
   if (MessageChannel::PrefEnabled() && tag == SCTAG_DOM_MESSAGEPORT) {
     NS_ASSERTION(!data, "Data should be empty");
 
     MessagePort* port;
     if (JS_ReadBytes(reader, &port, sizeof(port))) {
       JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
       if (global) {
         JS::Rooted<JSObject*> obj(cx, port->WrapObject(cx, global));
-        if (JS_WrapObject(cx, obj.address())) {
+        if (JS_WrapObject(cx, &obj)) {
           port->BindToOwner(scInfo->window);
           return obj;
         }
       }
     }
   }
 
   const JSStructuredCloneCallbacks* runtimeCallbacks =
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -684,17 +684,17 @@ NativeInterface2JSObjectAndThrowIfFailed
   nsWrapperCache *cache = aHelper.GetWrapperCache();
 
   if (cache && cache->IsDOMBinding()) {
       JS::RootedObject obj(aCx, cache->GetWrapper());
       if (!obj) {
           obj = cache->WrapObject(aCx, aScope);
       }
 
-      if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, obj.address())) {
+      if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
         return false;
       }
 
       if (obj) {
         *aRetval = JS::ObjectValue(*obj);
         return true;
       }
   }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1263,17 +1263,17 @@ WrapCallThisObject(JSContext* cx, JS::Ha
     // wrap anything for us.
     obj = WrapNativeParent(cx, scope, p);
     if (!obj) {
       return nullptr;
     }
   }
 
   // But all that won't necessarily put things in the compartment of cx.
-  if (!JS_WrapObject(cx, obj.address())) {
+  if (!JS_WrapObject(cx, &obj)) {
     return nullptr;
   }
 
   return obj;
 }
 
 // Helper for calling WrapNewBindingObject with smart pointers
 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4606,19 +4606,19 @@ def wrapTypeIntoCurrentCompartment(type,
         else:
             value = value + ".address()"
         return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
                          "  return false;\n"
                          "}" % value)
 
     if type.isObject():
         if isMember:
-            value = "&%s" % value
-        else:
-            value = value + ".address()"
+            value = "JS::MutableHandleObject::fromMarkedLocation(&%s)" % value
+        else:
+            value = "&" + value
         return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n"
                          "  return false;\n"
                          "}" % value)
 
     if type.isSpiderMonkeyInterface():
         origValue = value
         if type.nullable():
             value = "%s.Value()" % value
@@ -9900,17 +9900,17 @@ class CGJSImplClass(CGBindingImplClass):
     def getWrapObjectBody(self):
         return ("JS::Rooted<JSObject*> obj(aCx, %sBinding::Wrap(aCx, aScope, this));\n"
                 "if (!obj) {\n"
                 "  return nullptr;\n"
                 "}\n"
                 "\n"
                 "// Now define it on our chrome object\n"
                 "JSAutoCompartment ac(aCx, mImpl->Callback());\n"
-                "if (!JS_WrapObject(aCx, obj.address())) {\n"
+                "if (!JS_WrapObject(aCx, &obj)) {\n"
                 "  return nullptr;\n"
                 "}\n"
                 'if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", JS::ObjectValue(*obj), nullptr, nullptr, 0)) {\n'
                 "  return nullptr;\n"
                 "}\n"
                 "return obj;" % self.descriptor.name)
 
     def getGetParentObjectReturnType(self):
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -87,17 +87,17 @@ public:
 
   inline JSObject *Obj() const {
     MOZ_ASSERT(inited());
     return mObj;
   }
 
   inline bool WrapIntoNewCompartment(JSContext* cx)
   {
-    return JS_WrapObject(cx, &mObj);
+    return JS_WrapObject(cx, JS::MutableHandleObject::fromMarkedLocation(&mObj));
   }
 
 protected:
   inline void DoInit(JSObject* obj)
   {
     mObj = UnboxArray(obj, &mLength, &mData);
   }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1497,16 +1497,27 @@ ContentParent::ContentParent(mozIApplica
         nsCOMArray<nsIStyleSheet>& authorSheets = *sheetService->AuthorStyleSheets();
         for (uint32_t i = 0; i < authorSheets.Length(); i++) {
             URIParams uri;
             SerializeURI(authorSheets[i]->GetSheetURI(), uri);
             unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
         }
     }
 
+#ifdef MOZ_CONTENT_SANDBOX
+    // Bug 921817.  We enable the sandbox in RecvSetProcessPrivileges,
+    // which is where a preallocated process drops unnecessary privileges,
+    // but a non-preallocated process will already have changed its
+    // uid/gid/etc immediately after forking.  Thus, we send this message,
+    // which is otherwise a no-op, to sandbox it at an appropriate point
+    // during startup.
+    if (aOSPrivileges != base::PRIVILEGES_INHERIT) {
+        SendSetProcessPrivileges(base::PRIVILEGES_INHERIT);
+    }
+#endif
 }
 
 #ifdef MOZ_NUWA_PROCESS
 static const FileDescriptor*
 FindFdProtocolFdMapping(const nsTArray<ProtocolFdMapping>& aFds,
                         ProtocolId aProtoId)
 {
     for (unsigned int i = 0; i < aFds.Length(); i++) {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -289,33 +289,38 @@ TabParent::ActorDestroy(ActorDestroyReas
   if (sEventCapturer == this) {
     sEventCapturer = nullptr;
   }
   if (mIMETabParent == this) {
     mIMETabParent = nullptr;
   }
   nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  nsRefPtr<nsFrameMessageManager> fmm;
   if (frameLoader) {
+    fmm = frameLoader->GetFrameMessageManager();
     nsCOMPtr<Element> frameElement(mFrameElement);
     ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr);
     frameLoader->DestroyChild();
 
     if (why == AbnormalShutdown && os) {
       os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
                           "oop-frameloader-crashed", nullptr);
       nsContentUtils::DispatchTrustedEvent(frameElement->OwnerDoc(), frameElement,
                                            NS_LITERAL_STRING("oop-browser-crashed"),
                                            true, true);
     }
   }
 
   if (os) {
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr);
   }
+  if (fmm) {
+    fmm->Disconnect();
+  }
 }
 
 bool
 TabParent::RecvMoveFocus(const bool& aForward)
 {
   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   if (fm) {
     nsCOMPtr<nsIDOMElement> dummy;
--- a/dom/network/interfaces/nsIDOMNetworkStats.idl
+++ b/dom/network/interfaces/nsIDOMNetworkStats.idl
@@ -1,39 +1,40 @@
 /* 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 nsIDOMMozNetworkStatsInterface;
+
 [scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)]
 interface nsIDOMMozNetworkStatsData : nsISupports
 {
   readonly attribute unsigned long   rxBytes;   // Received bytes.
   readonly attribute unsigned long   txBytes;   // Sent bytes.
   readonly attribute jsval           date;      // Date.
 };
 
-[scriptable, builtinclass, uuid(6613ea55-b99c-44f9-91bf-d07da10b9b74)]
+[scriptable, builtinclass, uuid(b6fc4b14-628d-4c99-bf4e-e4ed56916cbe)]
 interface nsIDOMMozNetworkStats : nsISupports
 {
   /**
    * Manifest URL of an application for specifying the per-app
    * stats of the specified app. If null, system stats are returned.
    */
   readonly attribute DOMString    manifestURL;
 
   /**
-   * Can be 'mobile', 'wifi' or null.
-   * If null, stats for both mobile and wifi are returned.
+   * Network the returned data belongs to.
    */
-  readonly attribute DOMString    connectionType;
+  readonly attribute nsIDOMMozNetworkStatsInterface network;
 
   /**
-   * Stats for connectionType
+   * Stats for a network.
    */
   readonly attribute jsval        data;      // array of NetworkStatsData.
                                              // one element per day.
 
   /**
    * Dates
    */
   readonly attribute jsval        start; // Date.
--- a/dom/network/interfaces/nsIDOMNetworkStatsManager.idl
+++ b/dom/network/interfaces/nsIDOMNetworkStatsManager.idl
@@ -1,62 +1,70 @@
 /* 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 nsIDOMDOMRequest;
 
-dictionary NetworkStatsOptions
+/**
+ * Represents a data interface for which the manager is recording statistics.
+ */
+[scriptable, uuid(f540615b-d803-43ff-8200-2a9d145a5645)]
+interface nsIDOMMozNetworkStatsInterface : nsISupports
 {
+  readonly attribute long type;
+
   /**
-   * Connection type used to filter which network stats will be returned:
-   * 'mobile', 'wifi' or null.
-   * If null, stats for both mobile and wifi are returned.
-   *
-   * Manifest URL used to retrieve network stats per app.
-   * If null, system stats (regardless of the app) are returned.
+   * Id value is '0' for wifi or the iccid for mobile (SIM).
    */
-  DOMString connectionType;
-  DOMString manifestURL;
-  jsval start;              // date
-  jsval end;                // date
+  readonly attribute DOMString id;
 };
 
-[scriptable,  uuid(87529a6c-aef6-11e1-a595-4f034275cfa6)]
+[scriptable,  uuid(5fbdcae6-a2cd-47b3-929f-83ac75bd4881)]
 interface nsIDOMMozNetworkStatsManager : nsISupports
 {
   /**
-   * Query network statistics.
-   *
-   * If options.connectionType is not provided, return statistics for all known
-   * network interfaces.
-   *
-   * If options.manifestURL is not provided, return statistics regardless of the app.
+   * Constants for known interface types.
+   */
+  const long WIFI = 0;
+  const long MOBILE = 1;
+
+  /**
+   * Find samples between two dates start and end, both included.
    *
-   * If successful, the request result will be an nsIDOMMozNetworkStats object.
+   * If manifestURL is provided, per-app usage is retrieved,
+   * otherwise the target will be system usage.
    *
-   * If network stats are not available for some dates, then rxBytes &
-   * txBytes are undefined for those dates.
+   * If success, the request result will be an nsIDOMMozNetworkStats object.
    */
-  nsIDOMDOMRequest               getNetworkStats(in jsval options);
+  nsIDOMDOMRequest getSamples(in nsIDOMMozNetworkStatsInterface network,
+                              in jsval start,
+                              in jsval end,
+                              [optional] in DOMString manifestURL);
 
   /**
-   * Return available connection types.
+   * Remove all stats related with the provided network from DB.
    */
-  readonly attribute jsval       connectionTypes; // array of DOMStrings.
+  nsIDOMDOMRequest clearStats(in nsIDOMMozNetworkStatsInterface network);
 
   /**
-   * Clear all stats from DB.
+   * Remove all stats in the database.
    */
-  nsIDOMDOMRequest               clearAllData();
+  nsIDOMDOMRequest clearAllStats();
 
   /**
-   * Time in seconds between samples stored in database.
+   * Return currently available networks.
    */
-  readonly attribute long        sampleRate;
+  readonly attribute jsval availableNetworks; // array of nsIDOMMozNetworkStatsInterface.
 
   /**
-   * Maximum number of samples stored in the database per connection type.
+   * Minimum time in milliseconds between samples stored in the database.
    */
-  readonly attribute long        maxStorageSamples;
+  readonly attribute long sampleRate;
+
+  /**
+   * Time in milliseconds recorded by the API until present time. All samples
+   * older than maxStorageAge from now are deleted.
+   */
+  readonly attribute long long maxStorageAge;
 };
--- a/dom/network/interfaces/nsINetworkStatsServiceProxy.idl
+++ b/dom/network/interfaces/nsINetworkStatsServiceProxy.idl
@@ -1,35 +1,37 @@
 /* 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 nsINetworkInterface;
+
 [scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
 interface nsINetworkStatsServiceProxyCallback : nsISupports
 {
   /*
    * @param aResult callback result with boolean value
    * @param aMessage message
    */
   void notify(in boolean aResult, in jsval aMessage);
 };
 
-[scriptable, uuid(8fbd115d-f590-474c-96dc-e2b6803ca975)]
+[scriptable, uuid(facef032-3fd9-4509-a396-83d94c1a11ae)]
 interface nsINetworkStatsServiceProxy : nsISupports
 {
   /*
    * An interface used to record per-app traffic data.
    * @param aAppId app id
-   * @param aConnectionType network connection type (0 for wifi, 1 for mobile)
+   * @param aNetworkInterface network
    * @param aTimeStamp time stamp
    * @param aRxBytes received data amount
    * @param aTxBytes transmitted data amount
    * @param aCallback an optional callback
    */
   void saveAppStats(in unsigned long aAppId,
-                    in long aConnectionType,
+                    in nsINetworkInterface aNetwork,
                     in unsigned long long aTimeStamp,
                     in unsigned long long aRxBytes,
                     in unsigned long long aTxBytes,
          [optional] in nsINetworkStatsServiceProxyCallback aCallback);
 };
--- a/dom/network/src/NetworkStatsDB.jsm
+++ b/dom/network/src/NetworkStatsDB.jsm
@@ -11,45 +11,43 @@ function debug(s) { dump("-*- NetworkSta
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 
 const DB_NAME = "net_stats";
 const DB_VERSION = 2;
-const STORE_NAME = "net_stats"; // Deprecated. Use "net_stats_v2" instead.
-const STORE_NAME_V2 = "net_stats_v2";
+const STORE_NAME = "net_stats";
 
 // Constant defining the maximum values allowed per interface. If more, older
 // will be erased.
 const VALUES_MAX_LENGTH = 6 * 30;
 
 // Constant defining the rate of the samples. Daily.
 const SAMPLE_RATE = 1000 * 60 * 60 * 24;
 
-this.NetworkStatsDB = function NetworkStatsDB(aConnectionTypes) {
+this.NetworkStatsDB = function NetworkStatsDB() {
   if (DEBUG) {
     debug("Constructor");
   }
-  this._connectionTypes = aConnectionTypes;
-  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2]);
+  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]);
 }
 
 NetworkStatsDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
   dbNewTxn: function dbNewTxn(txn_type, callback, txnCb) {
     function successCb(result) {
       txnCb(null, result);
     }
     function errorCb(error) {
       txnCb(error, null);
     }
-    return this.newTxn(txn_type, STORE_NAME_V2, callback, successCb, errorCb);
+    return this.newTxn(txn_type, STORE_NAME, callback, successCb, errorCb);
   },
 
   upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
     if (DEBUG) {
       debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!");
     }
     let db = aDb;
     let objectStore;
@@ -64,371 +62,392 @@ NetworkStatsDB.prototype = {
         objectStore.createIndex("timestamp", "timestamp", { unique: false });
         objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
         objectStore.createIndex("txBytes", "txBytes", { unique: false });
         objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
         objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
         if (DEBUG) {
           debug("Created object stores and indexes");
         }
-
-        // There could be a time delay between the point when the network
-        // interface comes up and the point when the database is initialized.
-        // In this short interval some traffic data are generated but are not
-        // registered by the first sample. The initialization of the database
-        // should make up the missing sample.
-        let stats = [];
-        for (let connection in this._connectionTypes) {
-          let connectionType = this._connectionTypes[connection].name;
-          let timestamp = this.normalizeDate(new Date());
-          stats.push({ connectionType: connectionType,
-                       timestamp:      timestamp,
-                       rxBytes:        0,
-                       txBytes:        0,
-                       rxTotalBytes:   0,
-                       txTotalBytes:   0 });
-        }
-        this._saveStats(aTransaction, objectStore, stats);
-        if (DEBUG) {
-          debug("Database initialized");
-        }
       } else if (currVersion == 1) {
         // In order to support per-app traffic data storage, the original
         // objectStore needs to be replaced by a new objectStore with new
         // key path ("appId") and new index ("appId").
-        let newObjectStore;
-        newObjectStore = db.createObjectStore(STORE_NAME_V2, { keyPath: ["appId", "connectionType", "timestamp"] });
-        newObjectStore.createIndex("appId", "appId", { unique: false });
-        newObjectStore.createIndex("connectionType", "connectionType", { unique: false });
-        newObjectStore.createIndex("timestamp", "timestamp", { unique: false });
-        newObjectStore.createIndex("rxBytes", "rxBytes", { unique: false });
-        newObjectStore.createIndex("txBytes", "txBytes", { unique: false });
-        newObjectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
-        newObjectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
-        if (DEBUG) {
-          debug("Created new object stores and indexes");
-        }
+        // Also, since now networks are identified by their
+        // [networkId, networkType] not just by their connectionType,
+        // to modify the keyPath is mandatory to delete the object store
+        // and create it again. Old data is going to be deleted because the
+        // networkId for each sample can not be set.
+        db.deleteObjectStore(STORE_NAME);
 
-        // Copy the data from the original objectStore to the new objectStore.
-        objectStore = aTransaction.objectStore(STORE_NAME);
-        objectStore.openCursor().onsuccess = function(event) {
-          let cursor = event.target.result;
-          if (!cursor) {
-            // Delete the original object store.
-            db.deleteObjectStore(STORE_NAME);
-            return;
-          }
+        objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
+        objectStore.createIndex("appId", "appId", { unique: false });
+        objectStore.createIndex("network", "network", { unique: false });
+        objectStore.createIndex("networkType", "networkType", { unique: false });
+        objectStore.createIndex("timestamp", "timestamp", { unique: false });
+        objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
+        objectStore.createIndex("txBytes", "txBytes", { unique: false });
+        objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
+        objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
 
-          let oldStats = cursor.value;
-          let newStats = { appId:          0,
-                           connectionType: oldStats.connectionType,
-                           timestamp:      oldStats.timestamp,
-                           rxBytes:        oldStats.rxBytes,
-                           txBytes:        oldStats.txBytes,
-                           rxTotalBytes:   oldStats.rxTotalBytes,
-                           txTotalBytes:   oldStats.txTotalBytes };
-          this._saveStats(aTransaction, newObjectStore, newStats);
-          cursor.continue();
-        }.bind(this);
+        debug("Created object stores and indexes for version 2");
       }
     }
   },
 
+  importData: function importData(aStats) {
+    let stats = { appId:        aStats.appId,
+                  network:      [aStats.networkId, aStats.networkType],
+                  timestamp:    aStats.timestamp,
+                  rxBytes:      aStats.rxBytes,
+                  txBytes:      aStats.txBytes,
+                  rxTotalBytes: aStats.rxTotalBytes,
+                  txTotalBytes: aStats.txTotalBytes };
+
+    return stats;
+  },
+
+  exportData: function exportData(aStats) {
+    let stats = { appId:        aStats.appId,
+                  networkId:    aStats.network[0],
+                  networkType:  aStats.network[1],
+                  timestamp:    aStats.timestamp,
+                  rxBytes:      aStats.rxBytes,
+                  txBytes:      aStats.txBytes,
+                  rxTotalBytes: aStats.rxTotalBytes,
+                  txTotalBytes: aStats.txTotalBytes };
+
+    return stats;
+  },
+
   normalizeDate: function normalizeDate(aDate) {
     // Convert to UTC according to timezone and
     // filter timestamp to get SAMPLE_RATE precission
     let timestamp = aDate.getTime() - aDate.getTimezoneOffset() * 60 * 1000;
     timestamp = Math.floor(timestamp / SAMPLE_RATE) * SAMPLE_RATE;
     return timestamp;
   },
 
-  saveStats: function saveStats(stats, aResultCb) {
-    let timestamp = this.normalizeDate(stats.date);
+  saveStats: function saveStats(aStats, aResultCb) {
+    let timestamp = this.normalizeDate(aStats.date);
 
-    stats = { appId:          stats.appId,
-              connectionType: stats.connectionType,
-              timestamp:      timestamp,
-              rxBytes:        (stats.appId == 0) ? 0 : stats.rxBytes,
-              txBytes:        (stats.appId == 0) ? 0 : stats.txBytes,
-              rxTotalBytes:   (stats.appId == 0) ? stats.rxBytes : 0,
-              txTotalBytes:   (stats.appId == 0) ? stats.txBytes : 0 };
+    let stats = { appId:        aStats.appId,
+                  networkId:    aStats.networkId,
+                  networkType:  aStats.networkType,
+                  timestamp:    timestamp,
+                  rxBytes:      (aStats.appId == 0) ? 0 : aStats.rxBytes,
+                  txBytes:      (aStats.appId == 0) ? 0 : aStats.txBytes,
+                  rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
+                  txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 };
 
-    this.dbNewTxn("readwrite", function(txn, store) {
+    stats = this.importData(stats);
+
+    this.dbNewTxn("readwrite", function(aTxn, aStore) {
       if (DEBUG) {
         debug("Filtered time: " + new Date(timestamp));
         debug("New stats: " + JSON.stringify(stats));
       }
 
-      let request = store.index("connectionType").openCursor(stats.connectionType, "prev");
+    let request = aStore.index("network").openCursor(stats.network, "prev");
       request.onsuccess = function onsuccess(event) {
         let cursor = event.target.result;
         if (!cursor) {
           // Empty, so save first element.
-          this._saveStats(txn, store, stats);
+
+          // There could be a time delay between the point when the network
+          // interface comes up and the point when the database is initialized.
+          // In this short interval some traffic data are generated but are not
+          // registered by the first sample.
+          if (stats.appId == 0) {
+            stats.rxBytes = stats.rxTotalBytes;
+            stats.txBytes = stats.txTotalBytes;
+          }
+
+          this._saveStats(aTxn, aStore, stats);
           return;
         }
 
         if (stats.appId != cursor.value.appId) {
           cursor.continue();
           return;
         }
 
         // There are old samples
         if (DEBUG) {
           debug("Last value " + JSON.stringify(cursor.value));
         }
 
         // Remove stats previous to now - VALUE_MAX_LENGTH
-        this._removeOldStats(txn, store, stats.appId, stats.connectionType, stats.timestamp);
+        this._removeOldStats(aTxn, aStore, stats.appId, stats.network, stats.timestamp);
 
         // Process stats before save
-        this._processSamplesDiff(txn, store, cursor, stats);
+        this._processSamplesDiff(aTxn, aStore, cursor, stats);
       }.bind(this);
     }.bind(this), aResultCb);
   },
 
   /*
    * This function check that stats are saved in the database following the sample rate.
    * In this way is easier to find elements when stats are requested.
    */
-  _processSamplesDiff: function _processSamplesDiff(txn, store, lastSampleCursor, newSample) {
-    let lastSample = lastSampleCursor.value;
+  _processSamplesDiff: function _processSamplesDiff(aTxn, aStore, aLastSampleCursor, aNewSample) {
+    let lastSample = aLastSampleCursor.value;
 
     // Get difference between last and new sample.
-    let diff = (newSample.timestamp - lastSample.timestamp) / SAMPLE_RATE;
+    let diff = (aNewSample.timestamp - lastSample.timestamp) / SAMPLE_RATE;
     if (diff % 1) {
       // diff is decimal, so some error happened because samples are stored as a multiple
       // of SAMPLE_RATE
-      txn.abort();
+      aTxn.abort();
       throw new Error("Error processing samples");
     }
 
     if (DEBUG) {
-      debug("New: " + newSample.timestamp + " - Last: " + lastSample.timestamp + " - diff: " + diff);
+      debug("New: " + aNewSample.timestamp + " - Last: " +
+            lastSample.timestamp + " - diff: " + diff);
     }
 
     // If the incoming data is obtained from netd (|newSample.appId| is 0),
     // the new |txBytes|/|rxBytes| is assigend by the differnce between the new
     // |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
     // Else, the incoming data is per-app data (|newSample.appId| is not 0),
     // the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
-    if (newSample.appId == 0) {
-      let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes;
-      let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes;
+    if (aNewSample.appId == 0) {
+      let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes;
+      let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes;
       if (rxDiff < 0 || txDiff < 0) {
-        rxDiff = newSample.rxTotalBytes;
-        txDiff = newSample.txTotalBytes;
+        rxDiff = aNewSample.rxTotalBytes;
+        txDiff = aNewSample.txTotalBytes;
       }
-      newSample.rxBytes = rxDiff;
-      newSample.txBytes = txDiff;
+      aNewSample.rxBytes = rxDiff;
+      aNewSample.txBytes = txDiff;
     }
 
     if (diff == 1) {
       // New element.
 
       // If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
       // needs to be obtained by adding new |rxBytes|/|txBytes| to last
       // |rxTotalBytes|/|txTotalBytes|.
-      if (newSample.appId != 0) {
-        newSample.rxTotalBytes = newSample.rxBytes + lastSample.rxTotalBytes;
-        newSample.txTotalBytes = newSample.txBytes + lastSample.txTotalBytes;
+      if (aNewSample.appId != 0) {
+        aNewSample.rxTotalBytes = aNewSample.rxBytes + lastSample.rxTotalBytes;
+        aNewSample.txTotalBytes = aNewSample.txBytes + lastSample.txTotalBytes;
       }
-      this._saveStats(txn, store, newSample);
+
+      this._saveStats(aTxn, aStore, aNewSample);
       return;
     }
     if (diff > 1) {
       // Some samples lost. Device off during one or more samplerate periods.
       // Time or timezone changed
       // Add lost samples with 0 bytes and the actual one.
       if (diff > VALUES_MAX_LENGTH) {
         diff = VALUES_MAX_LENGTH;
       }
 
       let data = [];
       for (let i = diff - 2; i >= 0; i--) {
-        let time = newSample.timestamp - SAMPLE_RATE * (i + 1);
-        let sample = {appId:          newSample.appId,
-                      connectionType: newSample.connectionType,
-                      timestamp:      time,
-                      rxBytes:        0,
-                      txBytes:        0,
-                      rxTotalBytes:   lastSample.rxTotalBytes,
-                      txTotalBytes:   lastSample.txTotalBytes};
+        let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1);
+        let sample = { appId:        aNewSample.appId,
+                       network:      aNewSample.network,
+                       timestamp:    time,
+                       rxBytes:      0,
+                       txBytes:      0,
+                       rxTotalBytes: lastSample.rxTotalBytes,
+                       txTotalBytes: lastSample.txTotalBytes };
+
         data.push(sample);
       }
 
-      data.push(newSample);
-      this._saveStats(txn, store, data);
+      data.push(aNewSample);
+      this._saveStats(aTxn, aStore, data);
       return;
     }
     if (diff == 0 || diff < 0) {
       // New element received before samplerate period.
       // It means that device has been restarted (or clock / timezone change).
       // Update element.
 
       // If diff < 0, clock or timezone changed back. Place data in the last sample.
 
-      lastSample.rxBytes += newSample.rxBytes;
-      lastSample.txBytes += newSample.txBytes;
+      lastSample.rxBytes += aNewSample.rxBytes;
+      lastSample.txBytes += aNewSample.txBytes;
 
       // If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
       // needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
-      if (newSample.appId == 0) {
-        lastSample.rxTotalBytes = newSample.rxTotalBytes;
-        lastSample.txTotalBytes = newSample.txTotalBytes;
+      if (aNewSample.appId == 0) {
+        lastSample.rxTotalBytes = aNewSample.rxTotalBytes;
+        lastSample.txTotalBytes = aNewSample.txTotalBytes;
       } else {
         // Else, the incoming data is per-app data, old |rxTotalBytes|/
         // |txTotalBytes| needs to get updated by adding the new
         // |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
-        lastSample.rxTotalBytes += newSample.rxBytes;
-        lastSample.txTotalBytes += newSample.txBytes;
+        lastSample.rxTotalBytes += aNewSample.rxBytes;
+        lastSample.txTotalBytes += aNewSample.txBytes;
       }
       if (DEBUG) {
         debug("Update: " + JSON.stringify(lastSample));
       }
-      let req = lastSampleCursor.update(lastSample);
+      let req = aLastSampleCursor.update(lastSample);
     }
   },
 
-  _saveStats: function _saveStats(txn, store, networkStats) {
+  _saveStats: function _saveStats(aTxn, aStore, aNetworkStats) {
     if (DEBUG) {
-      debug("_saveStats: " + JSON.stringify(networkStats));
+      debug("_saveStats: " + JSON.stringify(aNetworkStats));
     }
 
-    if (Array.isArray(networkStats)) {
-      let len = networkStats.length - 1;
+    if (Array.isArray(aNetworkStats)) {
+      let len = aNetworkStats.length - 1;
       for (let i = 0; i <= len; i++) {
-        store.put(networkStats[i]);
+        aStore.put(aNetworkStats[i]);
       }
     } else {
-      store.put(networkStats);
+      aStore.put(aNetworkStats);
     }
   },
 
-  _removeOldStats: function _removeOldStats(txn, store, appId, connType, date) {
+  _removeOldStats: function _removeOldStats(aTxn, aStore, aAppId, aNetwork, aDate) {
     // Callback function to remove old items when new ones are added.
-    let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
-    let lowerFilter = [appId, connType, 0];
-    let upperFilter = [appId, connType, filterDate];
+    let filterDate = aDate - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
+    let lowerFilter = [aAppId, aNetwork, 0];
+    let upperFilter = [aAppId, aNetwork, filterDate];
     let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
-    store.openCursor(range).onsuccess = function(event) {
+    let lastSample = null;
+    let self = this;
+
+    aStore.openCursor(range).onsuccess = function(event) {
       var cursor = event.target.result;
       if (cursor) {
+        lastSample = cursor.value;
         cursor.delete();
         cursor.continue();
+        return;
       }
-    }.bind(this);
+
+      // If all samples for a network are removed, an empty sample
+      // has to be saved to keep the totalBytes in order to compute
+      // future samples because system counters are not set to 0.
+      // Thus, if there are no samples left, the last sample removed
+      // will be saved again after setting its bytes to 0.
+      let request = aStore.index("network").openCursor(aNetwork);
+      request.onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        if (!cursor && lastSample != null) {
+          let timestamp = new Date();
+          timestamp = self.normalizeDate(timestamp);
+          lastSample.timestamp = timestamp;
+          lastSample.rxBytes = 0;
+          lastSample.txBytes = 0;
+          self._saveStats(aTxn, aStore, lastSample);
+        }
+      };
+    };
   },
 
-  clear: function clear(aResultCb) {
-    this.dbNewTxn("readwrite", function(txn, store) {
-      if (DEBUG) {
-        debug("Going to clear all!");
-      }
-      store.clear();
+  clearInterfaceStats: function clearInterfaceStats(aNetwork, aResultCb) {
+    let network = [aNetwork.id, aNetwork.type];
+    let self = this;
+
+    // Clear and save an empty sample to keep sync with system counters
+    this.dbNewTxn("readwrite", function(aTxn, aStore) {
+      let sample = null;
+      let request = aStore.index("network").openCursor(network, "prev");
+      request.onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        if (cursor) {
+          if (!sample) {
+            sample = cursor.value;
+          }
+
+          cursor.delete();
+          cursor.continue();
+          return;
+        }
+
+        if (sample) {
+          let timestamp = new Date();
+          timestamp = self.normalizeDate(timestamp);
+          sample.timestamp = timestamp;
+          sample.appId = 0;
+          sample.rxBytes = 0;
+          sample.txBytes = 0;
+
+          self._saveStats(aTxn, aStore, sample);
+        }
+      };
     }, aResultCb);
   },
 
-  find: function find(aResultCb, aOptions) {
+  clearStats: function clearStats(aNetworks, aResultCb) {
+    let index = 0;
+    let stats = [];
+    let self = this;
+
+    let callback = function(aError, aResult) {
+      index++;
+
+      if (!aError && index < aNetworks.length) {
+        self.clearInterfaceStats(aNetworks[index], callback);
+        return;
+      }
+
+      aResultCb(aError, aResult);
+    };
+
+    if (!aNetworks[index]) {
+      aResultCb(null, true);
+      return;
+    }
+    this.clearInterfaceStats(aNetworks[index], callback);
+  },
+
+  find: function find(aResultCb, aNetwork, aStart, aEnd, aAppId, aManifestURL) {
     let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
-    let start = this.normalizeDate(aOptions.start);
-    let end = this.normalizeDate(aOptions.end);
+    let start = this.normalizeDate(aStart);
+    let end = this.normalizeDate(aEnd);
 
     if (DEBUG) {
-      debug("Find: appId: " + aOptions.appId + " connectionType:" +
-            aOptions.connectionType + " start: " + start + " end: " + end);
+      debug("Find samples for appId: " + aAppId + " network " +
+            JSON.stringify(aNetwork) + " from " + start + " until " + end);
       debug("Start time: " + new Date(start));
       debug("End time: " + new Date(end));
     }
 
-    this.dbNewTxn("readonly", function(txn, store) {
-      let lowerFilter = [aOptions.appId, aOptions.connectionType, start];
-      let upperFilter = [aOptions.appId, aOptions.connectionType, end];
+    this.dbNewTxn("readonly", function(aTxn, aStore) {
+      let network = [aNetwork.id, aNetwork.type];
+      let lowerFilter = [aAppId, network, start];
+      let upperFilter = [aAppId, network, end];
       let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
 
       let data = [];
 
-      if (!txn.result) {
-        txn.result = {};
+      if (!aTxn.result) {
+        aTxn.result = {};
       }
 
-      let request = store.openCursor(range).onsuccess = function(event) {
+      let request = aStore.openCursor(range).onsuccess = function(event) {
         var cursor = event.target.result;
         if (cursor){
           data.push({ rxBytes: cursor.value.rxBytes,
                       txBytes: cursor.value.txBytes,
                       date: new Date(cursor.value.timestamp + offset) });
           cursor.continue();
           return;
         }
 
         // When requested samples (start / end) are not in the range of now and
         // now - VALUES_MAX_LENGTH, fill with empty samples.
         this.fillResultSamples(start + offset, end + offset, data);
 
-        txn.result.manifestURL = aOptions.manifestURL;
-        txn.result.connectionType = aOptions.connectionType;
-        txn.result.start = aOptions.start;
-        txn.result.end = aOptions.end;
-        txn.result.data = data;
-      }.bind(this);
-    }.bind(this), aResultCb);
-  },
-
-  findAll: function findAll(aResultCb, aOptions) {
-    let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
-    let start = this.normalizeDate(aOptions.start);
-    let end = this.normalizeDate(aOptions.end);
-
-    if (DEBUG) {
-      debug("FindAll: appId: " + aOptions.appId +
-            " start: " + start + " end: " + end + "\n");
-    }
-
-    let self = this;
-    this.dbNewTxn("readonly", function(txn, store) {
-      let lowerFilter = start;
-      let upperFilter = end;
-      let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
-
-      let data = [];
-
-      if (!txn.result) {
-        txn.result = {};
-      }
-
-      let request = store.index("timestamp").openCursor(range).onsuccess = function(event) {
-        var cursor = event.target.result;
-        if (cursor) {
-          if (cursor.value.appId != aOptions.appId) {
-            cursor.continue();
-            return;
-          }
-
-          if (data.length > 0 &&
-              data[data.length - 1].date.getTime() == cursor.value.timestamp + offset) {
-            // Time is the same, so add values.
-            data[data.length - 1].rxBytes += cursor.value.rxBytes;
-            data[data.length - 1].txBytes += cursor.value.txBytes;
-          } else {
-            data.push({ rxBytes: cursor.value.rxBytes,
-                        txBytes: cursor.value.txBytes,
-                        date: new Date(cursor.value.timestamp + offset) });
-          }
-          cursor.continue();
-          return;
-        }
-
-        this.fillResultSamples(start + offset, end + offset, data);
-
-        txn.result.manifestURL = aOptions.manifestURL;
-        txn.result.connectionType = aOptions.connectionType;
-        txn.result.start = aOptions.start;
-        txn.result.end = aOptions.end;
-        txn.result.data = data;
+        aTxn.result.manifestURL = aManifestURL;
+        aTxn.result.network = aNetwork;
+        aTxn.result.start = aStart;
+        aTxn.result.end = aEnd;
+        aTxn.result.data = data;
       }.bind(this);
     }.bind(this), aResultCb);
   },
 
   /*
    * Fill data array (samples from database) with empty samples to match
    * requested start / end dates.
    */
@@ -456,15 +475,15 @@ NetworkStatsDB.prototype = {
     return SAMPLE_RATE;
   },
 
   get maxStorageSamples () {
     return VALUES_MAX_LENGTH;
   },
 
   logAllRecords: function logAllRecords(aResultCb) {
-    this.dbNewTxn("readonly", function(txn, store) {
-      store.mozGetAll().onsuccess = function onsuccess(event) {
-        txn.result = event.target.result;
+    this.dbNewTxn("readonly", function(aTxn, aStore) {
+      aStore.mozGetAll().onsuccess = function onsuccess(event) {
+        aTxn.result = event.target.result;
       };
     }, aResultCb);
   },
 };
--- a/dom/network/src/NetworkStatsManager.js
+++ b/dom/network/src/NetworkStatsManager.js
@@ -50,60 +50,90 @@ NetworkStatsData.prototype = {
                                      contractID:"@mozilla.org/networkstatsdata;1",
                                      classDescription: "NetworkStatsData",
                                      interfaces: [nsIDOMMozNetworkStatsData],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData])
 };
 
+// NetworkStatsInterface
+const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
+const NETWORKSTATSINTERFACE_CID        = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
+const nsIDOMMozNetworkStatsInterface   = Components.interfaces.nsIDOMMozNetworkStatsInterface;
+
+function NetworkStatsInterface(aNetwork) {
+  if (DEBUG) {
+    debug("NetworkStatsInterface Constructor");
+  }
+  this.type = aNetwork.type;
+  this.id = aNetwork.id;
+}
+
+NetworkStatsInterface.prototype = {
+  __exposedProps__: {
+                      id: 'r',
+                      type: 'r',
+                    },
+
+  classID : NETWORKSTATSINTERFACE_CID,
+  classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
+                                     contractID: NETWORKSTATSINTERFACE_CONTRACTID,
+                                     classDescription: "NetworkStatsInterface",
+                                     interfaces: [nsIDOMMozNetworkStatsInterface],
+                                     flags: nsIClassInfo.DOM_OBJECT}),
+
+  QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface])
+}
+
 // NetworkStats
 const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
-const NETWORKSTATS_CID        = Components.ID("{6613ea55-b99c-44f9-91bf-d07da10b9b74}");
+const NETWORKSTATS_CID        = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}");
 const nsIDOMMozNetworkStats   = Components.interfaces.nsIDOMMozNetworkStats;
 
 function NetworkStats(aWindow, aStats) {
   if (DEBUG) {
     debug("NetworkStats Constructor");
   }
   this.manifestURL = aStats.manifestURL || null;
-  this.connectionType = aStats.connectionType || null;
+  this.network = new NetworkStatsInterface(aStats.network);
   this.start = aStats.start || null;
   this.end = aStats.end || null;
 
   let samples = this.data = Cu.createArrayIn(aWindow);
   for (let i = 0; i < aStats.data.length; i++) {
     samples.push(new NetworkStatsData(aStats.data[i]));
   }
 }
 
 NetworkStats.prototype = {
   __exposedProps__: {
                       manifestURL: 'r',
-                      connectionType: 'r',
+                      network: 'r',
                       start: 'r',
                       end:  'r',
                       data:  'r',
                     },
 
   classID : NETWORKSTATS_CID,
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID,
                                      contractID: NETWORKSTATS_CONTRACTID,
                                      classDescription: "NetworkStats",
                                      interfaces: [nsIDOMMozNetworkStats],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
-                                          nsIDOMMozNetworkStatsData])
+                                          nsIDOMMozNetworkStatsData,
+                                          nsIDOMMozNetworkStatsInterface])
 }
 
 // NetworkStatsManager
 
 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
-const NETWORKSTATSMANAGER_CID        = Components.ID("{87529a6c-aef6-11e1-a595-4f034275cfa6}");
+const NETWORKSTATSMANAGER_CID        = Components.ID("{5fbdcae6-a2cd-47b3-929f-83ac75bd4881}");
 const nsIDOMMozNetworkStatsManager   = Components.interfaces.nsIDOMMozNetworkStatsManager;
 
 function NetworkStatsManager() {
   if (DEBUG) {
     debug("Constructor");
   }
 }
 
@@ -111,52 +141,74 @@ NetworkStatsManager.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   checkPrivileges: function checkPrivileges() {
     if (!this.hasPrivileges) {
       throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE);
     }
   },
 
-  getNetworkStats: function getNetworkStats(aOptions) {
+  getSamples: function getSamples(aNetwork, aStart, aEnd, aManifestURL) {
     this.checkPrivileges();
 
-    if (!aOptions.start || !aOptions.end ||
-      aOptions.start > aOptions.end) {
+    if (aStart.constructor.name !== "Date" ||
+        aEnd.constructor.name !== "Date" ||
+        aStart > aEnd) {
       throw Components.results.NS_ERROR_INVALID_ARG;
     }
 
     let request = this.createRequest();
     cpmm.sendAsyncMessage("NetworkStats:Get",
-                          {data: aOptions, id: this.getRequestId(request)});
+                          { network: aNetwork,
+                            start: aStart,
+                            end: aEnd,
+                            manifestURL: aManifestURL,
+                            id: this.getRequestId(request) });
     return request;
   },
 
-  clearAllData: function clearAllData() {
+  clearStats: function clearStats(aNetwork) {
     this.checkPrivileges();
 
     let request = this.createRequest();
     cpmm.sendAsyncMessage("NetworkStats:Clear",
+                          { network: aNetwork,
+                            id: this.getRequestId(request) });
+    return request;
+  },
+
+  clearAllStats: function clearAllStats() {
+    this.checkPrivileges();
+
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("NetworkStats:ClearAll",
                           {id: this.getRequestId(request)});
     return request;
   },
 
-  get connectionTypes() {
+  get availableNetworks() {
     this.checkPrivileges();
-    return ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Types")[0], this._window);
+
+    let result = ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Networks")[0], this._window);
+    let networks = this.data = Cu.createArrayIn(this._window);
+    for (let i = 0; i < result.length; i++) {
+      networks.push(new NetworkStatsInterface(result[i]));
+    }
+
+    return networks;
   },
 
   get sampleRate() {
     this.checkPrivileges();
-    return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0] / 1000;
+    return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
   },
 
-  get maxStorageSamples() {
+  get maxStorageAge() {
     this.checkPrivileges();
-    return cpmm.sendSyncMessage("NetworkStats:MaxStorageSamples")[0];
+    return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
   },
 
   receiveMessage: function(aMessage) {
     if (DEBUG) {
       debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
     }
     let msg = aMessage.json;
 
@@ -178,16 +230,17 @@ NetworkStatsManager.prototype = {
         let result = new NetworkStats(this._window, msg.result);
         if (DEBUG) {
           debug("result: " + JSON.stringify(result));
         }
         Services.DOMRequest.fireSuccess(req, result);
         break;
 
       case "NetworkStats:Clear:Return":
+      case "NetworkStats:ClearAll:Return":
         if (msg.error) {
           Services.DOMRequest.fireError(req, msg.error);
           return;
         }
 
         Services.DOMRequest.fireSuccess(req, true);
         break;
 
@@ -217,17 +270,18 @@ NetworkStatsManager.prototype = {
       debug("has privileges: " + this.hasPrivileges);
     }
 
     if (!this.hasPrivileges) {
       return null;
     }
 
     this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
-                              "NetworkStats:Clear:Return"]);
+                                        "NetworkStats:Clear:Return",
+                                        "NetworkStats:ClearAll:Return"]);
   },
 
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     if (DEBUG) {
       debug("uninit call");
     }
   },
@@ -240,10 +294,11 @@ NetworkStatsManager.prototype = {
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
                                      contractID: NETWORKSTATSMANAGER_CONTRACTID,
                                      classDescription: "NetworkStatsManager",
                                      interfaces: [nsIDOMMozNetworkStatsManager],
                                      flags: nsIClassInfo.DOM_OBJECT})
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
+                                                     NetworkStatsInterface,
                                                      NetworkStats,
                                                      NetworkStatsManager]);
--- a/dom/network/src/NetworkStatsManager.manifest
+++ b/dom/network/src/NetworkStatsManager.manifest
@@ -1,9 +1,12 @@
 component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
 contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
 
-component {6613ea55-b99c-44f9-91bf-d07da10b9b74} NetworkStatsManager.js
-contract @mozilla.org/networkStats;1 {6613ea55-b99c-44f9-91bf-d07da10b9b74}
+component {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} NetworkStatsManager.js
+contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}
 
-component {87529a6c-aef6-11e1-a595-4f034275cfa6} NetworkStatsManager.js
-contract @mozilla.org/networkStatsManager;1 {87529a6c-aef6-11e1-a595-4f034275cfa6}
+component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
+contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
+
+component {5fbdcae6-a2cd-47b3-929f-83ac75bd4881} NetworkStatsManager.js
+contract @mozilla.org/networkStatsManager;1 {5fbdcae6-a2cd-47b3-929f-83ac75bd4881}
 category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1
--- a/dom/network/src/NetworkStatsService.jsm
+++ b/dom/network/src/NetworkStatsService.jsm
@@ -1,80 +1,108 @@
 /* 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) { dump("-*- NetworkStatsService: " + s + "\n"); }
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- NetworkStatsService: " + s + "\n");
+  }
+}
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = ["NetworkStatsService"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
 
 const NET_NETWORKSTATSSERVICE_CONTRACTID = "@mozilla.org/network/netstatsservice;1";
 const NET_NETWORKSTATSSERVICE_CID = Components.ID("{18725604-e9ac-488a-8aa0-2471e7f6c0a4}");
 
 const TOPIC_INTERFACE_REGISTERED   = "network-interface-registered";
 const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
 const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
-const NET_TYPE_UNKNOWN = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN;
 
 // The maximum traffic amount can be saved in the |cachedAppStats|.
 const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "networkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
+                                   "@mozilla.org/settingsService;1",
+                                   "nsISettingsService");
+
+XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () {
+  let ril = Cc["@mozilla.org/ril;1"].getService(Ci["nsIRadioInterfaceLayer"]);
+  // TODO: Bug 923382 - B2G Multi-SIM: support multiple SIM cards for network metering.
+  return ril.getRadioInterface(0);
+});
+
 this.NetworkStatsService = {
   init: function() {
-    if (DEBUG) {
-      debug("Service started");
-    }
+    debug("Service started");
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, false);
     Services.obs.addObserver(this, "profile-after-change", false);
 
     this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
-    this._connectionTypes = Object.create(null);
-    this._connectionTypes[NET_TYPE_WIFI] = { name: "wifi",
-                                             network: Object.create(null) };
-    this._connectionTypes[NET_TYPE_MOBILE] = { name: "mobile",
-                                               network: Object.create(null) };
+    // Object to store network interfaces, each network interface is composed
+    // by a network object (network type and network Id) and a interfaceName
+    // that contains the name of the physical interface (wlan0, rmnet0, etc.).
+    // The network type can be 0 for wifi or 1 for mobile. On the other hand,
+    // the network id is '0' for wifi or the iccid for mobile (SIM).
+    // Each networkInterface is placed in the _networks object by the index of
+    // 'networkId + networkType'.
+    //
+    // _networks object allows to map available network interfaces at low level
+    // (wlan0, rmnet0, etc.) to a network. It's not mandatory to have a
+    // networkInterface per network but can't exist a networkInterface not
+    // being mapped to a network.
 
+    this._networks = Object.create(null);
+
+    // There is no way to know a priori if wifi connection is available,
+    // just when the wifi driver is loaded, but it is unloaded when
+    // wifi is switched off. So wifi connection is hardcoded
+    let netId = this.getNetworkId('0', NET_TYPE_WIFI);
+    this._networks[netId] = { network:       { id: '0',
+                                               type: NET_TYPE_WIFI },
+                              interfaceName: null };
 
     this.messages = ["NetworkStats:Get",
                      "NetworkStats:Clear",
-                     "NetworkStats:Types",
+                     "NetworkStats:ClearAll",
+                     "NetworkStats:Networks",
                      "NetworkStats:SampleRate",
-                     "NetworkStats:MaxStorageSamples"];
+                     "NetworkStats:MaxStorageAge"];
 
-    this.messages.forEach(function(msgName) {
-      ppmm.addMessageListener(msgName, this);
+    this.messages.forEach(function(aMsgName) {
+      ppmm.addMessageListener(aMsgName, this);
     }, this);
 
-    this._db = new NetworkStatsDB(this._connectionTypes);
+    this._db = new NetworkStatsDB();
 
     // Stats for all interfaces are updated periodically
     this.timer.initWithCallback(this, this._db.sampleRate,
                                 Ci.nsITimer.TYPE_REPEATING_PRECISE);
 
     // App stats are firstly stored in the cached.
     this.cachedAppStats = Object.create(null);
     this.cachedAppStatsDate = new Date();
@@ -83,68 +111,66 @@ this.NetworkStatsService = {
     this.isQueueRunning = false;
   },
 
   receiveMessage: function(aMessage) {
     if (!aMessage.target.assertPermission("networkstats-manage")) {
       return;
     }
 
-    if (DEBUG) {
-      debug("receiveMessage " + aMessage.name);
-    }
+    debug("receiveMessage " + aMessage.name);
+
     let mm = aMessage.target;
     let msg = aMessage.json;
 
     switch (aMessage.name) {
       case "NetworkStats:Get":
-        this.getStats(mm, msg);
+        this.getSamples(mm, msg);
         break;
       case "NetworkStats:Clear":
+        this.clearInterfaceStats(mm, msg);
+        break;
+      case "NetworkStats:ClearAll":
         this.clearDB(mm, msg);
         break;
-      case "NetworkStats:Types":
-        // This message is sync.
-        let types = [];
-        for (let i in this._connectionTypes) {
-          types.push(this._connectionTypes[i].name);
-        }
-        return types;
+      case "NetworkStats:Networks":
+        return this.availableNetworks();
       case "NetworkStats:SampleRate":
         // This message is sync.
         return this._db.sampleRate;
-      case "NetworkStats:MaxStorageSamples":
+      case "NetworkStats:MaxStorageAge":
         // This message is sync.
-        return this._db.maxStorageSamples;
+        return this._db.maxStorageSamples * this._db.sampleRate;
     }
   },
 
-  observe: function observe(subject, topic, data) {
-    switch (topic) {
+  observe: function observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
       case TOPIC_INTERFACE_REGISTERED:
       case TOPIC_INTERFACE_UNREGISTERED:
+
         // If new interface is registered (notified from NetworkManager),
         // the stats are updated for the new interface without waiting to
-        // complete the updating period
-        let network = subject.QueryInterface(Ci.nsINetworkInterface);
-        if (DEBUG) {
-          debug("Network " + network.name + " of type " + network.type + " status change");
+        // complete the updating period.
+
+        let network = aSubject.QueryInterface(Ci.nsINetworkInterface);
+        debug("Network " + network.name + " of type " + network.type + " status change");
+
+        let netId = this.convertNetworkInterface(network);
+        if (!netId) {
+          break;
         }
-        if (this._connectionTypes[network.type]) {
-          this._connectionTypes[network.type].network = network;
-          this.updateStats(network.type);
-        }
+
+        this.updateStats(netId);
         break;
       case "xpcom-shutdown":
-        if (DEBUG) {
-          debug("Service shutdown");
-        }
+        debug("Service shutdown");
 
-        this.messages.forEach(function(msgName) {
-          ppmm.removeMessageListener(msgName, this);
+        this.messages.forEach(function(aMsgName) {
+          ppmm.removeMessageListener(aMsgName, this);
         }, this);
 
         Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.obs.removeObserver(this, "profile-after-change");
         Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
         Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
 
         this.timer.cancel();
@@ -155,167 +181,206 @@ this.NetworkStatsService = {
         break;
     }
   },
 
   /*
    * nsITimerCallback
    * Timer triggers the update of all stats
    */
-  notify: function(timer) {
+  notify: function(aTimer) {
     this.updateAllStats();
   },
 
   /*
+   * nsINetworkStatsService
+   */
+
+  convertNetworkInterface: function(aNetwork) {
+    if (aNetwork.type != NET_TYPE_MOBILE &&
+        aNetwork.type != NET_TYPE_WIFI) {
+      return null;
+    }
+
+    let id = '0';
+    if (aNetwork.type == NET_TYPE_MOBILE) {
+      // Bug 904542 will provide the serviceId to map the iccId with the
+      // nsINetworkInterface of the NetworkManager. Now, lets assume that
+      // network is mapped with the current iccId of the single SIM.
+      id = gRadioInterface.rilContext.iccInfo.iccid;
+    }
+
+    let netId = this.getNetworkId(id, aNetwork.type);
+
+    if (!this._networks[netId]) {
+      this._networks[netId] = Object.create(null);
+      this._networks[netId].network = { id: id,
+                                        type: aNetwork.type };
+    }
+
+    this._networks[netId].interfaceName = aNetwork.name;
+    return netId;
+  },
+
+  getNetworkId: function getNetworkId(aIccId, aNetworkType) {
+    return aIccId + '' + aNetworkType;
+  },
+
+  availableNetworks: function availableNetworks() {
+    let result = [];
+    for (let netId in this._networks) {
+      result.push(this._networks[netId].network);
+    }
+
+    return result;
+  },
+
+  /*
    * Function called from manager to get stats from database.
    * In order to return updated stats, first is performed a call to
    * updateAllStats function, which will get last stats from netd
    * and update the database.
    * Then, depending on the request (stats per appId or total stats)
    * it retrieve them from database and return to the manager.
    */
-  getStats: function getStats(mm, msg) {
-    this.updateAllStats(function onStatsUpdated(aResult, aMessage) {
-
-      let data = msg.data;
-
-      let options = { appId:          0,
-                      connectionType: data.connectionType,
-                      start:          data.start,
-                      end:            data.end };
-
-      let manifestURL = data.manifestURL;
-      if (manifestURL) {
-        let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
-        if (DEBUG) {
-          debug("get appId: " + appId + " from manifestURL: " + manifestURL);
-        }
+  getSamples: function getSamples(mm, msg) {
+    let self = this;
+    let network = msg.network;
+    let netId = this.getNetworkId(network.id, network.type);
 
-        if (!appId) {
-          mm.sendAsyncMessage("NetworkStats:Get:Return",
-                              { id: msg.id, error: "Invalid manifestURL", result: null });
-          return;
-        }
-
-        options.appId = appId;
-        options.manifestURL = manifestURL;
-      }
+    if (!this._networks[netId]) {
+      mm.sendAsyncMessage("NetworkStats:Get:Return",
+                          { id: msg.id, error: "Invalid connectionType", result: null });
+      return;
+    }
 
-      if (DEBUG) {
-        debug("getStats for options: " + JSON.stringify(options));
-      }
+    let appId = 0;
+    let manifestURL = msg.manifestURL;
+    if (manifestURL) {
+      appId = appsService.getAppLocalIdByManifestURL(manifestURL);
 
-      if (!options.connectionType || options.connectionType.length == 0) {
-        this._db.findAll(function onStatsFound(error, result) {
-          mm.sendAsyncMessage("NetworkStats:Get:Return",
-                              { id: msg.id, error: error, result: result });
-        }, options);
+      if (!appId) {
+        mm.sendAsyncMessage("NetworkStats:Get:Return",
+                            { id: msg.id, error: "Invalid manifestURL", result: null });
         return;
       }
+    }
 
-      for (let i in this._connectionTypes) {
-        if (this._connectionTypes[i].name == options.connectionType) {
-          this._db.find(function onStatsFound(error, result) {
-            mm.sendAsyncMessage("NetworkStats:Get:Return",
-                                { id: msg.id, error: error, result: result });
-          }, options);
-          return;
-        }
-      }
+    let start = new Date(msg.start);
+    let end = new Date(msg.end);
+
+    this.updateStats(netId, function onStatsUpdated(aResult, aMessage) {
+      debug("getstats for network " + network.id + " of type " + network.type);
+      debug("appId: " + appId + " from manifestURL: " + manifestURL);
+
+      self._db.find(function onStatsFound(aError, aResult) {
+        mm.sendAsyncMessage("NetworkStats:Get:Return",
+                            { id: msg.id, error: aError, result: aResult });
+      }, network, start, end, appId, manifestURL);
+
+    });
+  },
 
-      mm.sendAsyncMessage("NetworkStats:Get:Return",
-                          { id: msg.id, error: "Invalid connectionType", result: null });
+  clearInterfaceStats: function clearInterfaceStats(mm, msg) {
+    let network = msg.network;
+    let netId = this.getNetworkId(network.id, network.type);
+
+    debug("clear stats for network " + network.id + " of type " + network.type);
 
-    }.bind(this));
+    if (!this._networks[netId]) {
+      mm.sendAsyncMessage("NetworkStats:Clear:Return",
+                          { id: msg.id, error: "Invalid networkType", result: null });
+      return;
+    }
+
+    this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
+        mm.sendAsyncMessage("NetworkStats:Clear:Return",
+                            { id: msg.id, error: aError, result: aResult });
+    });
   },
 
   clearDB: function clearDB(mm, msg) {
-    this._db.clear(function onDBCleared(error, result) {
-      mm.sendAsyncMessage("NetworkStats:Clear:Return",
-                          { id: msg.id, error: error, result: result });
+    let networks = this.availableNetworks();
+    this._db.clearStats(networks, function onDBCleared(aError, aResult) {
+      mm.sendAsyncMessage("NetworkStats:ClearAll:Return",
+                          { id: msg.id, error: aError, result: aResult });
     });
   },
 
-  updateAllStats: function updateAllStats(callback) {
+  updateAllStats: function updateAllStats(aCallback) {
     // Update |cachedAppStats|.
     this.updateCachedAppStats();
 
     let elements = [];
     let lastElement;
 
     // For each connectionType create an object containning the type
     // and the 'queueIndex', the 'queueIndex' is an integer representing
     // the index of a connection type in the global queue array. So, if
     // the connection type is already in the queue it is not appended again,
     // else it is pushed in 'elements' array, which later will be pushed to
     // the queue array.
-    for (let i in this._connectionTypes) {
-      lastElement = { type: i,
-                      queueIndex: this.updateQueueIndex(i)};
+    for (let netId in this._networks) {
+      lastElement = { netId: netId,
+                      queueIndex: this.updateQueueIndex(netId)};
+
       if (lastElement.queueIndex == -1) {
-        elements.push({type: lastElement.type, callbacks: []});
+        elements.push({netId: lastElement.netId, callbacks: []});
       }
     }
 
     if (elements.length > 0) {
       // If length of elements is greater than 0, callback is set to
       // the last element.
-      elements[elements.length - 1].callbacks.push(callback);
+      elements[elements.length - 1].callbacks.push(aCallback);
       this.updateQueue = this.updateQueue.concat(elements);
     } else {
       // Else, it means that all connection types are already in the queue to
       // be updated, so callback for this request is added to
       // the element in the main queue with the index of the last 'lastElement'.
       // But before is checked that element is still in the queue because it can
       // be processed while generating 'elements' array.
-
-      if (!this.updateQueue[lastElement.queueIndex] ||
-          this.updateQueue[lastElement.queueIndex].type != lastElement.queueIndex) {
-        if (callback) {
-          callback();
-        }
+      let element = this.updateQueue[lastElement.queueIndex];
+      if (aCallback &&
+         (!element || element.netId != lastElement.netId)) {
+        aCallback();
         return;
       }
 
-      this.updateQueue[lastElement.queueIndex].callbacks.push(callback);
+      this.updateQueue[lastElement.queueIndex].callbacks.push(aCallback);
     }
 
     // Call the function that process the elements of the queue.
     this.processQueue();
 
     if (DEBUG) {
       this.logAllRecords();
     }
   },
 
-  updateStats: function updateStats(connectionType, callback) {
-    // Check if the connection type is in the main queue, push a new element
+  updateStats: function updateStats(aNetId, aCallback) {
+    // Check if the connection is in the main queue, push a new element
     // if it is not being processed or add a callback if it is.
-    let index = this.updateQueueIndex(connectionType);
+    let index = this.updateQueueIndex(aNetId);
     if (index == -1) {
-      this.updateQueue.push({type: connectionType, callbacks: [callback]});
+      this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
     } else {
-      this.updateQueue[index].callbacks.push(callback);
+      this.updateQueue[index].callbacks.push(aCallback);
     }
 
     // Call the function that process the elements of the queue.
     this.processQueue();
   },
 
   /*
-   * Find if a connection type is in the main queue array and return its
+   * Find if a connection is in the main queue array and return its
    * index, if it is not in the array return -1.
    */
-  updateQueueIndex: function updateQueueIndex(type) {
-    for (let i in this.updateQueue) {
-      if (this.updateQueue[i].type == type) {
-        return i;
-      }
-    }
-    return -1;
+  updateQueueIndex: function updateQueueIndex(aNetId) {
+    return this.updateQueue.map(function(e) { return e.netId; }).indexOf(aNetId);
   },
 
   /*
    * Function responsible of process all requests in the queue.
    */
   processQueue: function processQueue(aResult, aMessage) {
     // If aResult is not undefined, the caller of the function is the result
     // of processing an element, so remove that element and call the callbacks
@@ -342,101 +407,109 @@ this.NetworkStatsService = {
 
     // Check length to determine if queue is empty and stop processing.
     if (this.updateQueue.length < 1) {
       this.isQueueRunning = false;
       return;
     }
 
     // Call the update function for the next element.
-    this.update(this.updateQueue[0].type, this.processQueue.bind(this));
+    this.update(this.updateQueue[0].netId, this.processQueue.bind(this));
   },
 
-  update: function update(connectionType, callback) {
+  update: function update(aNetId, aCallback) {
     // Check if connection type is valid.
-    if (!this._connectionTypes[connectionType]) {
-      if (callback) {
-        callback(false, "Invalid network type " + connectionType);
+    if (!this._networks[aNetId]) {
+      if (aCallback) {
+        aCallback(false, "Invalid network " + aNetId);
       }
       return;
     }
 
-    if (DEBUG) {
-      debug("Update stats for " + this._connectionTypes[connectionType].name);
-    }
+    let interfaceName = this._networks[aNetId].interfaceName;
+    debug("Update stats for " + interfaceName);
 
     // Request stats to NetworkManager, which will get stats from netd, passing
     // 'networkStatsAvailable' as a callback.
-    let networkName = this._connectionTypes[connectionType].network.name;
-    if (networkName) {
-      networkManager.getNetworkInterfaceStats(networkName,
-                this.networkStatsAvailable.bind(this, callback, connectionType));
+    if (interfaceName) {
+      networkManager.getNetworkInterfaceStats(interfaceName,
+                this.networkStatsAvailable.bind(this, aCallback, aNetId));
       return;
     }
-    if (callback) {
-      callback(true, "ok");
+
+    if (aCallback) {
+      aCallback(true, "ok");
     }
   },
 
   /*
    * Callback of request stats. Store stats in database.
    */
-  networkStatsAvailable: function networkStatsAvailable(callback, connType, result, rxBytes, txBytes, date) {
-    if (!result) {
-      if (callback) {
-        callback(false, "Netd IPC error");
+  networkStatsAvailable: function networkStatsAvailable(aCallback, aNetId,
+                                                        aResult, aRxBytes,
+                                                        aTxBytes, aDate) {
+    if (!aResult) {
+      if (aCallback) {
+        aCallback(false, "Netd IPC error");
       }
       return;
     }
 
-    let stats = { appId:          0,
-                  connectionType: this._connectionTypes[connType].name,
-                  date:           date,
-                  rxBytes:        rxBytes,
-                  txBytes:        txBytes };
+    let stats = { appId:       0,
+                  networkId:   this._networks[aNetId].network.id,
+                  networkType: this._networks[aNetId].network.type,
+                  date:        aDate,
+                  rxBytes:     aTxBytes,
+                  txBytes:     aRxBytes };
 
-    if (DEBUG) {
-      debug("Update stats for " + stats.connectionType + ": rx=" + stats.rxBytes +
-            " tx=" + stats.txBytes + " timestamp=" + stats.date);
-    }
-    this._db.saveStats(stats, function onSavedStats(error, result) {
-      if (callback) {
-        if (error) {
-          callback(false, error);
+    debug("Update stats for: " + JSON.stringify(stats));
+
+    this._db.saveStats(stats, function onSavedStats(aError, aResult) {
+      if (aCallback) {
+        if (aError) {
+          aCallback(false, aError);
           return;
         }
 
-        callback(true, "OK");
+        aCallback(true, "OK");
       }
     });
   },
 
   /*
    * Function responsible for receiving per-app stats.
    */
-  saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
-    if (DEBUG) {
-      debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
-            aTimeStamp + " " + aRxBytes + " " + aTxBytes);
+  saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
+    let netId = this.convertNetworkInterface(aNetwork);
+    if (!netId) {
+      if (aCallback) {
+        aCallback.notify(false, "Invalid network type");
+      }
+      return;
     }
 
+    debug("saveAppStats: " + aAppId + " " + netId + " " +
+          aTimeStamp + " " + aRxBytes + " " + aTxBytes);
+
     // Check if |aAppId| and |aConnectionType| are valid.
-    if (!aAppId || aConnectionType == NET_TYPE_UNKNOWN) {
+    if (!aAppId || !this._networks[netId]) {
+      debug("Invalid appId or network interface");
       return;
     }
 
     let stats = { appId: aAppId,
-                  connectionType: this._connectionTypes[aConnectionType].name,
+                  networkId: this._networks[netId].network.id,
+                  networkType: this._networks[netId].network.type,
                   date: new Date(aTimeStamp),
                   rxBytes: aRxBytes,
                   txBytes: aTxBytes };
 
     // Generate an unique key from |appId| and |connectionType|,
     // which is used to retrieve data in |cachedAppStats|.
-    let key = stats.appId + stats.connectionType;
+    let key = stats.appId + "" + netId;
 
     // |cachedAppStats| only keeps the data with the same date.
     // If the incoming date is different from |cachedAppStatsDate|,
     // both |cachedAppStats| and |cachedAppStatsDate| will get updated.
     let diff = (this._db.normalizeDate(stats.date) -
                 this._db.normalizeDate(this.cachedAppStatsDate)) /
                this._db.sampleRate;
     if (diff != 0) {
@@ -473,29 +546,25 @@ this.NetworkStatsService = {
 
     // If new rxBytes or txBytes exceeds MAX_CACHED_TRAFFIC
     // the corresponding row will be saved to indexedDB.
     // Then, the row will be removed from the cached.
     if (appStats.rxBytes > MAX_CACHED_TRAFFIC ||
         appStats.txBytes > MAX_CACHED_TRAFFIC) {
       this._db.saveStats(appStats,
         function (error, result) {
-          if (DEBUG) {
-            debug("Application stats inserted in indexedDB");
-          }
+          debug("Application stats inserted in indexedDB");
         }
       );
       delete this.cachedAppStats[key];
     }
   },
 
-  updateCachedAppStats: function updateCachedAppStats(callback) {
-    if (DEBUG) {
-      debug("updateCachedAppStats: " + this.cachedAppStatsDate);
-    }
+  updateCachedAppStats: function updateCachedAppStats(aCallback) {
+    debug("updateCachedAppStats: " + this.cachedAppStatsDate);
 
     let stats = Object.keys(this.cachedAppStats);
     if (stats.length == 0) {
       // |cachedAppStats| is empty, no need to update.
       return;
     }
 
     let index = 0;
@@ -504,47 +573,47 @@ this.NetworkStatsService = {
         if (DEBUG) {
           debug("Application stats inserted in indexedDB");
         }
 
         // Clean up the |cachedAppStats| after updating.
         if (index == stats.length - 1) {
           this.cachedAppStats = Object.create(null);
 
-          if (!callback) {
+          if (!aCallback) {
             return;
           }
 
           if (error) {
-            callback(false, error);
+            aCallback(false, error);
             return;
           }
 
-          callback(true, "ok");
+          aCallback(true, "ok");
           return;
         }
 
         // Update is not finished, keep updating.
         index += 1;
         this._db.saveStats(this.cachedAppStats[stats[index]],
                            onSavedStats.bind(this, error, result));
       }.bind(this));
   },
 
   get maxCachedTraffic () {
     return MAX_CACHED_TRAFFIC;
   },
 
   logAllRecords: function logAllRecords() {
-    this._db.logAllRecords(function onResult(error, result) {
-      if (error) {
-        debug("Error: " + error);
+    this._db.logAllRecords(function onResult(aError, aResult) {
+      if (aError) {
+        debug("Error: " + aError);
         return;
       }
 
       debug("===== LOG =====");
-      debug("There are " + result.length + " items");
-      debug(JSON.stringify(result));
+      debug("There are " + aResult.length + " items");
+      debug(JSON.stringify(aResult));
     });
-  }
+  },
 };
 
 NetworkStatsService.init();
--- a/dom/network/src/NetworkStatsServiceProxy.js
+++ b/dom/network/src/NetworkStatsServiceProxy.js
@@ -24,24 +24,24 @@ function NetworkStatsServiceProxy() {
   }
 }
 
 NetworkStatsServiceProxy.prototype = {
   /*
    * Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
    * to pass the per-app stats to NetworkStatsService.
    */
-  saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp,
+  saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp,
                                       aRxBytes, aTxBytes, aCallback) {
     if (DEBUG) {
-      debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
-            aTimeStamp + " " + aRxBytes + " " + aTxBytes);
+      debug("saveAppStats: " + aAppId + " connectionType " + aNetwork.type +
+            " " + aTimeStamp + " " + aRxBytes + " " + aTxBytes);
     }
 
-    NetworkStatsService.saveAppStats(aAppId, aConnectionType, aTimeStamp,
-                                     aRxBytes, aTxBytes, aCallback);
+    NetworkStatsService.saveAppStats(aAppId, aNetwork, aTimeStamp,
+                                      aRxBytes, aTxBytes, aCallback);
   },
 
   classID : NETWORKSTATSSERVICEPROXY_CID,
   QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]),
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsServiceProxy]);
--- a/dom/network/src/TCPSocket.js
+++ b/dom/network/src/TCPSocket.js
@@ -162,17 +162,17 @@ TCPSocket.prototype = {
   _waitingForStartTLS: false,
   _pendingDataAfterStartTLS: [],
 
 #ifdef MOZ_WIDGET_GONK
   // Network statistics (Gonk-specific feature)
   _txBytes: 0,
   _rxBytes: 0,
   _appId: Ci.nsIScriptSecurityManager.NO_APP_ID,
-  _connectionType: Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN,
+  _activeNetwork: null,
 #endif
 
   // Public accessors.
   get readyState() {
     return this._readyState;
   },
   get binaryType() {
     return this._binaryType;
@@ -342,17 +342,17 @@ TCPSocket.prototype = {
     }
 
     let nssProxy = Cc["@mozilla.org/networkstatsServiceProxy;1"]
                      .getService(Ci.nsINetworkStatsServiceProxy);
     if (!nssProxy) {
       LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available.");
       return;
     }
-    nssProxy.saveAppStats(this._appId, this._connectionType, Date.now(),
+    nssProxy.saveAppStats(this._appId, this._activeNetwork, Date.now(),
                           this._rxBytes, this._txBytes);
 
     // Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
     this._txBytes = this._rxBytes = 0;
   },
   // End of helper method for network statistics.
 #endif
 
@@ -525,22 +525,22 @@ TCPSocket.prototype = {
       return that;
     }
 
     let transport = that._transport = this._createTransport(host, port, that._ssl);
     transport.setEventSink(that, Services.tm.currentThread);
     that._initStream(that._binaryType);
 
 #ifdef MOZ_WIDGET_GONK
-    // Set _connectionType, which is only required for network statistics.
+    // Set _activeNetwork, which is only required for network statistics.
     // Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
     // Gonk-specific.
     let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
-    if (networkManager && networkManager.active) {
-      that._connectionType = networkManager.active.type;
+    if (networkManager) {
+      that._activeNetwork = networkManager.active;
     }
 #endif
 
     return that;
   },
 
   upgradeToSecure: function ts_upgradeToSecure() {
     if (this._readyState !== kOPEN) {
--- a/dom/network/tests/marionette/test_mobile_mmi.js
+++ b/dom/network/tests/marionette/test_mobile_mmi.js
@@ -71,18 +71,37 @@ tasks.push(function testGettingIMEI() {
     ok(event.target.result instanceof Object, "result instanceof Object");
     is(event.target.result.statusMessage, "000000000000000", "Emulator IMEI");
     is(event.target.result.serviceCode, "scImei", "Service code IMEI");
     is(event.target.result.additionalInformation, undefined,
        "No additional information");
     tasks.next();
   }
   request.onerror = function onerror() {
-    ok(false, "request success");
+    ok(false, "request should not error");
     tasks.abort();
   };
 });
 
+tasks.push(function testInvalidMMICode(){
+  log("Test invalid MMI code ...");
+
+  let request = mobileConnection.sendMMI("InvalidMMICode");
+  ok(request instanceof DOMRequest,
+     "request is instanceof " + request.constructor);
+
+  request.onsuccess = function onsuccess(event) {
+    ok(false, "request should not success");
+    tasks.abort();
+  };
+
+  request.onerror = function onerror() {
+    ok(true, "request error");
+    is(request.error.name, "emMmiError", "MMI error name");
+    tasks.next();
+  };
+});
+
 // WARNING: All tasks should be pushed before this!!!
 tasks.push(function cleanUp() {
   SpecialPowers.removePermission("mobileconnection", document);
   finish();
 });
--- a/dom/network/tests/test_networkstats_basics.html
+++ b/dom/network/tests/test_networkstats_basics.html
@@ -7,55 +7,44 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-// Test for NetworkStats
-function checkInterface(aInterface) {
-  ok(!(aInterface in window), aInterface + " should be prefixed");
-  ok(("Moz" + aInterface) in window, aInterface + " should be prefixed");
-}
-
 function test() {
-  // Test interfaces
-  checkInterface("NetworkStatsManager");
-  checkInterface("NetworkStats");
-  checkInterface("NetworkStatsData");
-
   ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist");
   ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object");
 
   netStats = navigator.mozNetworkStats;
 
   // Test IDL attributes
-  ok('connectionTypes' in netStats,
-   "connectionTypes should be a NetworkStats attribute");
-  ok(Array.isArray(netStats.connectionTypes) && netStats.connectionTypes.length > 0,
-   "connectionTypes is an array not empty.");
+  ok('availableNetworks' in netStats,
+   "availableNetworks should be a NetworkStats attribute");
+  ok(Array.isArray(netStats.availableNetworks) && netStats.availableNetworks.length > 0,
+   "availableNetworks is an array not empty.");
 
   ok('sampleRate' in netStats,
    "sampleRate should be a NetworkStats attribute");
   ok(netStats.sampleRate > 0,
    "sampleRate is greater than 0.");
 
-  ok('maxStorageSamples' in netStats,
-   "maxStorageSamples should be a NetworkStats attribute");
-  ok(netStats.maxStorageSamples > 0,
-   "maxStorageSamples is greater than 0.");
+  ok('maxStorageAge' in netStats,
+   "maxStorageAge should be a NetworkStats attribute");
+  ok(netStats.maxStorageAge > 0,
+   "maxStorageAge is greater than 0.");
 
   // Test IDL methods
   next();
   return;
 }
 
-function checkDataDates(data, start, end, sampleRate){
+function checkDataDates(data, start, end, sampleRate) {
   var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
   start = Math.floor((start.getTime() - offset) / sampleRate) * sampleRate + offset;
   end = Math.floor((end.getTime() - offset) / sampleRate) * sampleRate + offset;
 
   var counter = 0;
   var date = start;
   var success = true;
 
@@ -66,292 +55,195 @@ function checkDataDates(data, start, end
     }
     date += sampleRate;
     counter++;
   } while (date <= end);
 
   ok(success, "data result has correct dates");
 }
 
+function compareNetworks(networkA, networkB) {
+  return (networkA.id == networkB.id &&
+          networkA.type == networkB.type);
+}
+
 var req;
 var index = -1;
 var netStats = null;
 
 var steps = [
   function () {
-    // Test clearAlldata
-    req = netStats.clearAllData();
+    // Test clearAllStats
+    req = netStats.clearAllStats();
     req.onsuccess = function () {
-      ok(true, "clearAllData deleted the database");
+      ok(true, "clearAllStats deleted the database");
       next();
     };
     req.onerror = function () {
-      ok(false, "clearAllData deleted the database");
+      ok(false, "clearAllStats deleted the database");
     }
   },
   function () {
-    // Check if getNetworkStats launch exception when start is greather than end
+    // Check if getSamples launch exception when start is greather than end
 
     // Prepare get params
-    var type = netStats.connectionTypes[0];
+    var network = netStats.availableNetworks[0];
     // Get dates
     var endDate = new Date();
     var startDate = new Date(endDate.getTime() + 1000);
 
     try {
-      netStats.getNetworkStats({start: startDate, end: endDate});
+      netStats.getSamples(network, startDate, endDate);
     } catch(ex) {
-      ok(true, "getNetworkStats launch exception when start is greater than end");
+      ok(true, "getSamples launch exception when start is greater than end");
       next();
       return;
     }
 
-    ok(false, "getNetworkStats launch exceptionwhen start is greater than end");
+    ok(false, "getSamples launch exception when start is greater than end");
     next();
     return;
   },
   function () {
-    // Test if call getNetworkStats with undefined start param launch an exception
+    // Test if call getSamples with network of type different than
+    // nsIDOMMozNetworkStatsInterface launch an exception
 
     // Prepare get params
-    var type = netStats.connectionTypes[0];
-    setTimeout(function() {
-      try {
-        netStats.getNetworkStats({end: new Date()});
-      } catch(ex) {
-        ok(true, "getNetworkStats launch exception when start param does not exist");
-        next();
-        return;
-      }
+    var network = "wifi";
+    var endDate = new Date();
+    var startDate = new Date(endDate.getTime() - 1000);
 
-      ok(false, "getNetworkStats launch exception when start param does not exist");
-    }, 1000);
-  },
-  function () {
-    // Test if call getNetworkStats with undefined end param launch an exception
+    try {
+      netStats.getSamples(network, new Date(), new Date());
+    } catch(ex) {
+      ok(true, "getSamples launch exception if network is not " +
+               "a nsIDOMMozNetworkStatsInterface");
+      next();
+      return;
+    }
 
-    // Prepare get params
-    var type = netStats.connectionTypes[0];
-    setTimeout(function() {
-      try {
-        netStats.getNetworkStats({start: new Date()});
-      } catch(ex) {
-        ok(true, "getNetworkStats launch exception when end param does not exist");
-        next();
-        return;
-      }
-
-      ok(false, "getNetworkStats launch exception when end param does not exist");
-    }, 1000);
+    ok(false, "getSamples launch exception if network is not " +
+              "a nsIDOMMozNetworkStatsInterface");
   },
   function () {
-    ok(true, "Get system stats for a connectionType and dates adapted to samplerate");
+    // Test if call getSamples with start parameter type different than Date launch an exception
+
     // Prepare get params
-    var type = netStats.connectionTypes[0];
+    var network = netStats.availableNetworks[0];
+    var endDate = new Date();
+    var startDate = new Date(endDate.getTime() - 1000);
+    startDate = startDate.toString();
+
+    try {
+      netStats.getSamples(network, startDate, endDate);
+    } catch(ex) {
+      ok(true, "getSamples launch exception when start param is not a Date");
+      next();
+      return;
+    }
+
+    ok(false, "getSamples launch exception when start param is not a Date");
+  },
+  function () {
+    // Test if call getSamples with end parameter type different than Date launch an exception
+
+    // Prepare get params
+    var network = netStats.availableNetworks[0];
+    var endDate = new Date();
+    var startDate = new Date(endDate.getTime() - 1000);
+    endDate = startDate.toString();
+
+    try {
+      netStats.getSamples(network, startDate, endDate);
+    } catch(ex) {
+      ok(true, "getSamples launch exception when end param is not a Date");
+      next();
+      return;
+    }
+
+    ok(false, "getSamples launch exception when end param is not a Date");
+  },
+  function () {
+    ok(true, "Get stats for a network and dates adapted to samplerate");
+    // Prepare get params
+    var network = netStats.availableNetworks[0];
     var diff = 2;
     // Get samplerate in millis
-    var sampleRate = netStats.sampleRate * 1000;
-    // Get date with samplerate's precision
-    var offset = new Date().getTimezoneOffset() * 60 * 1000;
-    var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
-                           * sampleRate + offset);
-    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
-    // Calculate the number of samples that should be returned based on the
-    // the samplerate and including final and initial samples.
-    var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
-
-    // Launch request
-    req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
-    req.onsuccess = function () {
-      ok(true, "Get system stats request ok");
-      ok(req.result.manifestURL == null, "manifestURL should be null");
-      ok(req.result.connectionType == type, "connectionTypes should be equals");
-      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
-      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
-      var data = req.result.data;
-      ok(Array.isArray(data) && data.length == samples,
-         "data is an array of length " + samples);
-      checkDataDates(data, startDate, endDate, sampleRate);
-      next();
-    };
-    req.onerror = function () {
-      ok(false, "Get system stats for a connectionType failure!");
-    }
-  },
-  function () {
-    ok(true, "Get system stats for all connectionTypes and dates adapted to samplerate");
-    // Prepare get params
-    var diff = 2;
-    // Get samplerate in millis
-    var sampleRate = netStats.sampleRate * 1000;
+    var sampleRate = netStats.sampleRate;
     // Get date with samplerate's precision
     var offset = new Date().getTimezoneOffset() * 60 * 1000;
     var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
                            * sampleRate + offset);
     var startDate = new Date(endDate.getTime() - (sampleRate * diff));
     // Calculate the number of samples that should be returned based on the
     // the samplerate and including final and initial samples.
     var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
 
     // Launch request
-    req = netStats.getNetworkStats({start: startDate, end: endDate});
+    req = netStats.getSamples(network, startDate, endDate);
     req.onsuccess = function () {
-      ok(true, "Get stats request ok");
+      ok(true, "Get system stats request ok");
       ok(req.result.manifestURL == null, "manifestURL should be null");
-      ok(req.result.connectionType == null, "connectionTypes should be null");
-      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
-      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
-      var data = req.result.data;
-      ok(Array.isArray(data) && data.length == samples,
-         "data is an array of length " + samples);
-      checkDataDates(data, startDate, endDate, sampleRate);
-      next();
-    };
-    req.onerror = function () {
-      ok(false, "Get system stats for all connectionTypes failure!");
-    }
-  },
-  function () {
-    ok(true, "Get app stats for a connectionType and dates adapted to samplerate");
-    // Prepare get params
-    var url = 'app://browser.gaiamobile.org/manifest.webapp';
-    var type = netStats.connectionTypes[0];
-    var diff = 2;
-    // Get samplerate in millis
-    var sampleRate = netStats.sampleRate * 1000;
-    // Get date with samplerate's precision
-    var offset = new Date().getTimezoneOffset() * 60 * 1000;
-    var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
-                           * sampleRate + offset);
-    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
-    // Calculate the number of samples that should be returned based on the
-    // the samplerate and including final and initial samples.
-    var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
-
-    // Launch request
-    req = netStats.getNetworkStats({start:          startDate,
-                                    end:            endDate,
-                                    connectionType: type,
-                                    manifestURL:    url});
-    req.onsuccess = function () {
-      ok(true, "Get app stats request ok");
-      ok(req.result.manifestURL == url, "manifestURL should be equals");
-      ok(req.result.connectionType == type, "connectionTypes should be equals");
+      ok(compareNetworks(req.result.network, network), "networks should be equals");
       ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
       ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
       var data = req.result.data;
       ok(Array.isArray(data) && data.length == samples,
          "data is an array of length " + samples);
       checkDataDates(data, startDate, endDate, sampleRate);
       next();
     };
     req.onerror = function () {
-      ok(false, "Get app stats for a connectionType failure!");
+      ok(false, "Get stats failure!");
     }
   },
   function () {
-    ok(true, "Get app stats for all connectionTypes and dates adapted to samplerate");
+    ok(true, "Get system stats for a network and dates not adapted to samplerate");
     // Prepare get params
-    var url = 'app://browser.gaiamobile.org/manifest.webapp';
+    var network = netStats.availableNetworks[0];
     var diff = 2;
     // Get samplerate in millis
-    var sampleRate = netStats.sampleRate * 1000;
-    // Get date with samplerate's precision
-    var offset = new Date().getTimezoneOffset() * 60 * 1000;
-    var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
-                           * sampleRate + offset);
+    var sampleRate = netStats.sampleRate;
+    var endDate = new Date();
     var startDate = new Date(endDate.getTime() - (sampleRate * diff));
     // Calculate the number of samples that should be returned based on the
-    // the samplerate and including final and initial samples.
-    var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
+    // the samplerate, including final and initial samples and taking into
+    // account that these will be filtered according to precision.
+    var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate -
+                   Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
 
     // Launch request
-    req = netStats.getNetworkStats({start:       startDate,
-                                    end:         endDate,
-                                    manifestURL: url});
+    req = netStats.getSamples(network, startDate, endDate);
     req.onsuccess = function () {
-      ok(true, "Get app stats request ok");
-      ok(req.result.manifestURL == url, "manifestURL should be equals");
-      ok(req.result.connectionType == null, "connectionTypes should be null");
+      ok(true, "Get stats request ok");
+      ok(req.result.manifestURL == null, "manifestURL should be null");
+      ok(compareNetworks(req.result.network, network), "networks should be equals");
       ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
       ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
       var data = req.result.data;
       ok(Array.isArray(data) && data.length == samples,
          "data is an array of length " + samples);
       checkDataDates(data, startDate, endDate, sampleRate);
       next();
     };
     req.onerror = function () {
-      ok(false, "Get app stats for all connectionTypes failure!");
+      ok(false, "Get stats failure!");
     }
   },
   function () {
-    ok(true, "Get system stats for a connectionType and dates not adapted to samplerate");
-    // Prepare get params
-    var type = netStats.connectionTypes[0];
-    var diff = 2;
-    // Get samplerate in millis
-    var sampleRate = netStats.sampleRate * 1000;
-    var endDate = new Date();
-    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
-    // Calculate the number of samples that should be returned based on the
-    // the samplerate, including final and initial samples and taking into
-    // account that these will be filtered according to precision.
-    var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate -
-                   Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
+    // Test clearStats
+    var network = netStats.availableNetworks[0];
 
-    // Launch request
-    req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
+    req = netStats.clearStats(network);
     req.onsuccess = function () {
-      ok(true, "Get system stats request ok");
-      ok(req.result.manifestURL == null, "manifestURL should be null");
-      ok(req.result.connectionType == type, "connectionTypes should be equals");
-      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
-      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
-      var data = req.result.data;
-      ok(Array.isArray(data) && data.length == samples,
-         "data is an array of length " + samples);
-      checkDataDates(data, startDate, endDate, sampleRate);
+      ok(true, "clearStats deleted the database");
       next();
     };
     req.onerror = function () {
-      ok(false, "Get system stats for a connectionType failure!");
-    }
-  },
-  function () {
-    ok(true, "Get system stats for all connectionTypes and dates not adapted to samplerate");
-    // Prepare get params
-    var diff = 2;
-    // Get samplerate in millis
-    var sampleRate = netStats.sampleRate * 1000;
-    // Get date with samplerate's precision
-    var endDate = new Date();
-    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
-    // Calculate the number of samples that should be returned based on the
-    // the samplerate, including final and initial samples and taking into
-    // account that these will be filtered according to precision.
-    var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate -
-                   Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
-
-    // Launch request
-    req = netStats.getNetworkStats({start: startDate, end: endDate});
-    req.onsuccess = function () {
-      ok(true, "Get stats request ok");
-      ok(req.result.manifestURL == null, "manifestURL should be null");
-      ok(req.result.connectionType == null, "connectionTypes should be null");
-      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
-      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
-      var data = req.result.data;
-      ok(Array.isArray(data) && data.length == samples,
-         "data is an array of length " + samples);
-      checkDataDates(data, startDate, endDate, sampleRate);
-      next();
-    };
-    req.onerror = function () {
-      ok(false, "Get system stats for all connectionType failure!");
+      ok(false, "clearStats deleted the database");
     }
   },
   function () {
     ok(true, "all done!\n");
     SpecialPowers.removePermission("networkstats-manage", document);
     SimpleTest.finish();
     return;
   }
--- a/dom/network/tests/test_networkstats_enabled_no_perm.html
+++ b/dom/network/tests/test_networkstats_enabled_no_perm.html
@@ -7,29 +7,29 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-// Test to ensure NetworkStats is enabled but mozNetworkStats.connectionTypes
+// Test to ensure NetworkStats is enabled but mozNetworkStats.availableNetworks
 // does not work in content.
 
 SpecialPowers.setBoolPref("dom.mozNetworkStats.enabled", true);
 SpecialPowers.removePermission("networkstats-manage", document);
 
 ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should be accessible if dom.mozNetworkStats.enabled is true");
 
 var error;
 try {
-  navigator.mozNetworkStats.connectionTypes;
-  ok(false, "Accessing navigator.mozNetworkStats.connectionTypes should have thrown!");
+  navigator.mozNetworkStats.availableNetworks;
+  ok(false, "Accessing navigator.mozNetworkStats.availableNetworks should have thrown!");
 } catch (ex) {
   error = ex;
 }
-ok(error, "Got an exception accessing navigator.mozNetworkStats.connectionTypes");
+ok(error, "Got an exception accessing navigator.mozNetworkStats.availableNetworks");
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/network/tests/unit_stats/test_networkstats_db.js
+++ b/dom/network/tests/unit_stats/test_networkstats_db.js
@@ -1,23 +1,32 @@
 /* Any: copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
 
-const netStatsDb = new NetworkStatsDB(this);
+const netStatsDb = new NetworkStatsDB();
 
 function filterTimestamp(date) {
   var sampleRate = netStatsDb.sampleRate;
   var offset = date.getTimezoneOffset() * 60 * 1000;
   return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate;
 }
 
+function getNetworks() {
+  return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI },
+          { id: '1234', type: Ci.nsIDOMMozNetworkStatsManager.MOBILE }];
+}
+
+function compareNetworks(networkA, networkB) {
+  return (networkA[0] == networkB[0] && networkA[1] == networkB[1]);
+}
+
 add_test(function test_sampleRate() {
   var sampleRate = netStatsDb.sampleRate;
   do_check_true(sampleRate > 0);
   netStatsDb.sampleRate = 0;
   sampleRate = netStatsDb.sampleRate;
   do_check_true(sampleRate > 0);
 
   run_next_test();
@@ -84,80 +93,99 @@ add_test(function test_fillResultSamples
     aux += sampleRate;
   }
   do_check_true(success);
 
   run_next_test();
 });
 
 add_test(function test_clear() {
-  netStatsDb.clear(function (error, result) {
+  var networks = getNetworks();
+  netStatsDb.clearStats(networks, function (error, result) {
+    do_check_eq(error, null);
+    run_next_test();
+  });
+});
+
+add_test(function test_clear_interface() {
+  var networks = getNetworks();
+  netStatsDb.clearInterfaceStats(networks[0], function (error, result) {
     do_check_eq(error, null);
     run_next_test();
   });
 });
 
 add_test(function test_internalSaveStats_singleSample() {
-  var stats = {appId:          0,
-               connectionType: "wifi",
-               timestamp:      Date.now(),
-               rxBytes:        0,
-               txBytes:        0,
-               rxTotalBytes:   1234,
-               txTotalBytes:   1234};
+  var networks = getNetworks();
+
+  var stats = { appId:        0,
+                network:      [networks[0].id, networks[0].type],
+                timestamp:    Date.now(),
+                rxBytes:      0,
+                txBytes:      0,
+                rxTotalBytes: 1234,
+                txTotalBytes: 1234 };
 
   netStatsDb.dbNewTxn("readwrite", function(txn, store) {
     netStatsDb._saveStats(txn, store, stats);
   }, function(error, result) {
     do_check_eq(error, null);
 
     netStatsDb.logAllRecords(function(error, result) {
       do_check_eq(error, null);
       do_check_eq(result.length, 1);
       do_check_eq(result[0].appId, stats.appId);
-      do_check_eq(result[0].connectionType, stats.connectionType);
+      do_check_true(compareNetworks(result[0].network, stats.network));
       do_check_eq(result[0].timestamp, stats.timestamp);
       do_check_eq(result[0].rxBytes, stats.rxBytes);
       do_check_eq(result[0].txBytes, stats.txBytes);
       do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes);
       do_check_eq(result[0].txTotalBytes, stats.txTotalBytes);
       run_next_test();
     });
   });
 });
 
 add_test(function test_internalSaveStats_arraySamples() {
-  netStatsDb.clear(function (error, result) {
+  var networks = getNetworks();
+
+  netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
 
+    var network = [networks[0].id, networks[0].type];
+
     var samples = 2;
     var stats = [];
     for (var i = 0; i < samples; i++) {
-      stats.push({appId:          0,
-                  connectionType: "wifi",
-                  timestamp:      Date.now() + (10 * i),
-                  rxBytes:        0,
-                  txBytes:        0,
-                  rxTotalBytes:   1234,
-                  txTotalBytes:   1234});
+      stats.push({ appId:        0,
+                   network:      network,
+                   timestamp:    Date.now() + (10 * i),
+                   rxBytes:      0,
+                   txBytes:      0,
+                   rxTotalBytes: 1234,
+                   txTotalBytes: 1234 });
     }
 
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
+
+        // Result has one sample more than samples because clear inserts
+        // an empty sample to keep totalBytes synchronized with netd counters
+        result.shift();
         do_check_eq(result.length, samples);
 
         var success = true;
-        for (var i = 0; i < samples; i++) {
+        for (var i = 1; i < samples; i++) {
           if (result[i].appId != stats[i].appId ||
-              result[i].connectionType != stats[i].connectionType ||
+              !compareNetworks(result[i].network, stats[i].network) ||
               result[i].timestamp != stats[i].timestamp ||
               result[i].rxBytes != stats[i].rxBytes ||
               result[i].txBytes != stats[i].txBytes ||
               result[i].rxTotalBytes != stats[i].rxTotalBytes ||
               result[i].txTotalBytes != stats[i].txTotalBytes) {
             success = false;
             break;
           }
@@ -165,59 +193,62 @@ add_test(function test_internalSaveStats
         do_check_true(success);
         run_next_test();
       });
     });
   });
 });
 
 add_test(function test_internalRemoveOldStats() {
-  netStatsDb.clear(function (error, result) {
+  var networks = getNetworks();
+
+  netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
 
+    var network = [networks[0].id, networks[0].type];
     var samples = 10;
     var stats = [];
     for (var i = 0; i < samples - 1; i++) {
-      stats.push({appId:               0,
-                  connectionType: "wifi", timestamp:    Date.now() + (10 * i),
-                  rxBytes:             0, txBytes:      0,
-                  rxTotalBytes:     1234, txTotalBytes: 1234});
+      stats.push({ appId:              0,
+                   network:      network, timestamp:    Date.now() + (10 * i),
+                   rxBytes:            0, txBytes:      0,
+                   rxTotalBytes:    1234, txTotalBytes: 1234 });
     }
 
-    stats.push({appId:               0,
-                connectionType: "wifi", timestamp:    Date.now() + (10 * samples),
-                rxBytes:             0, txBytes:      0,
-                rxTotalBytes:     1234, txTotalBytes: 1234});
+    stats.push({ appId:              0,
+                 network:      network, timestamp:    Date.now() + (10 * samples),
+                 rxBytes:            0, txBytes:      0,
+                 rxTotalBytes:    1234, txTotalBytes: 1234 });
 
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
-      var date = stats[stats.length -1].timestamp
+      var date = stats[stats.length - 1].timestamp
                  + (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
-      netStatsDb._removeOldStats(txn, store, 0, "wifi", date);
+      netStatsDb._removeOldStats(txn, store, 0, network, date);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
         do_check_eq(result.length, 1);
 
         run_next_test();
       });
     });
   });
 });
 
-function processSamplesDiff(lastStat, newStat, callback) {
-  netStatsDb.clear(function (error, result){
+function processSamplesDiff(networks, lastStat, newStat, callback) {
+  netStatsDb.clearStats(networks, function (error, result){
     do_check_eq(error, null);
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, lastStat);
     }, function(error, result) {
       netStatsDb.dbNewTxn("readwrite", function(txn, store) {
-        let request = store.index("connectionType").openCursor(newStat.connectionType, "prev");
+        let request = store.index("network").openCursor(newStat.network, "prev");
         request.onsuccess = function onsuccess(event) {
           let cursor = event.target.result;
           do_check_neq(cursor, null);
           netStatsDb._processSamplesDiff(txn, store, cursor, newStat);
         };
       }, function(error, result) {
         do_check_eq(error, null);
         netStatsDb.logAllRecords(function(error, result) {
@@ -225,292 +256,314 @@ function processSamplesDiff(lastStat, ne
           callback(result);
         });
       });
     });
   });
 }
 
 add_test(function test_processSamplesDiffSameSample() {
+  var networks = getNetworks();
+  var network = [networks[0].id, networks[0].type];
+
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-  var lastStat = {appId:               0,
-                  connectionType: "wifi", timestamp:    date,
-                  rxBytes:             0, txBytes:      0,
-                  rxTotalBytes:     1234, txTotalBytes: 1234};
+
+  var lastStat = { appId:              0,
+                   network:      network, timestamp:    date,
+                   rxBytes:            0, txBytes:      0,
+                   rxTotalBytes:    1234, txTotalBytes: 1234 };
 
-  var newStat = {appId:                 0,
-                 connectionType:   "wifi", timestamp:    date,
-                 rxBytes:               0, txBytes:      0,
-                 rxTotalBytes:       2234, txTotalBytes: 2234};
+  var newStat = { appId:              0,
+                  network:      network, timestamp:    date,
+                  rxBytes:            0, txBytes:      0,
+                  rxTotalBytes:    2234, txTotalBytes: 2234 };
 
-  processSamplesDiff(lastStat, newStat, function(result) {
+  processSamplesDiff(networks, lastStat, newStat, function(result) {
     do_check_eq(result.length, 1);
     do_check_eq(result[0].appId, newStat.appId);
-    do_check_eq(result[0].connectionType, newStat.connectionType);
+    do_check_true(compareNetworks(result[0].network, newStat.network));
     do_check_eq(result[0].timestamp, newStat.timestamp);
     do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
     do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
     do_check_eq(result[0].rxTotalBytes, newStat.rxTotalBytes);
     do_check_eq(result[0].txTotalBytes, newStat.txTotalBytes);
     run_next_test();
   });
 });
 
 add_test(function test_processSamplesDiffNextSample() {
+  var networks = getNetworks();
+  var network = [networks[0].id, networks[0].type];
+
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-  var lastStat = {appId:               0,
-                  connectionType: "wifi", timestamp:    date,
-                  rxBytes:             0, txBytes:      0,
-                  rxTotalBytes:     1234, txTotalBytes: 1234};
+
+  var lastStat = { appId:              0,
+                   network:      network, timestamp:    date,
+                   rxBytes:            0, txBytes:      0,
+                   rxTotalBytes:    1234, txTotalBytes: 1234 };
 
-  var newStat = {appId:                 0,
-                 connectionType:   "wifi", timestamp:    date + sampleRate,
-                 rxBytes:               0, txBytes:      0,
-                 rxTotalBytes:        500, txTotalBytes: 500};
+  var newStat = { appId:              0,
+                  network:      network, timestamp:    date + sampleRate,
+                  rxBytes:            0, txBytes:      0,
+                  rxTotalBytes:     500, txTotalBytes: 500 };
 
-  processSamplesDiff(lastStat, newStat, function(result) {
+  processSamplesDiff(networks, lastStat, newStat, function(result) {
     do_check_eq(result.length, 2);
     do_check_eq(result[1].appId, newStat.appId);
-    do_check_eq(result[1].connectionType, newStat.connectionType);
+    do_check_true(compareNetworks(result[1].network, newStat.network));
     do_check_eq(result[1].timestamp, newStat.timestamp);
     do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
     do_check_eq(result[1].txBytes, newStat.txTotalBytes);
     do_check_eq(result[1].rxTotalBytes, newStat.rxTotalBytes);
     do_check_eq(result[1].txTotalBytes, newStat.txTotalBytes);
     run_next_test();
   });
 });
 
 add_test(function test_processSamplesDiffSamplesLost() {
+  var networks = getNetworks();
+  var network = [networks[0].id, networks[0].type];
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-  var lastStat = {appId:               0,
-                  connectionType: "wifi", timestamp:    date,
-                  rxBytes:             0, txBytes:      0,
-                  rxTotalBytes:     1234, txTotalBytes: 1234};
+  var lastStat = { appId:             0,
+                   network:     network, timestamp:    date,
+                   rxBytes:           0, txBytes:      0,
+                   rxTotalBytes:   1234, txTotalBytes: 1234 };
 
-  var newStat = {appId:                0,
-                 connectionType:  "wifi", timestamp:    date + (sampleRate * samples),
-                 rxBytes:              0, txBytes:      0,
-                 rxTotalBytes:      2234, txTotalBytes: 2234};
+  var newStat = { appId:              0,
+                  network:      network, timestamp:    date + (sampleRate * samples),
+                  rxBytes:            0, txBytes:      0,
+                  rxTotalBytes:    2234, txTotalBytes: 2234 };
 
-  processSamplesDiff(lastStat, newStat, function(result) {
+  processSamplesDiff(networks, lastStat, newStat, function(result) {
     do_check_eq(result.length, samples + 1);
     do_check_eq(result[0].appId, newStat.appId);
-    do_check_eq(result[samples].connectionType, newStat.connectionType);
+    do_check_true(compareNetworks(result[samples].network, newStat.network));
     do_check_eq(result[samples].timestamp, newStat.timestamp);
     do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
     do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
     do_check_eq(result[samples].rxTotalBytes, newStat.rxTotalBytes);
     do_check_eq(result[samples].txTotalBytes, newStat.txTotalBytes);
     run_next_test();
   });
 });
 
 add_test(function test_saveStats() {
-  var stats = {appId:          0,
-               connectionType: "wifi",
-               date:           new Date(),
-               rxBytes:        2234,
-               txBytes:        2234};
+  var networks = getNetworks();
+  var network = [networks[0].id, networks[0].type];
 
-  netStatsDb.clear(function (error, result) {
+  var stats = { appId:       0,
+                networkId:   networks[0].id,
+                networkType: networks[0].type,
+                date:        new Date(),
+                rxBytes:     2234,
+                txBytes:     2234};
+
+  netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
         do_check_eq(result.length, 1);
         do_check_eq(result[0].appId, stats.appId);
-        do_check_eq(result[0].connectionType, stats.connectionType);
+        do_check_true(compareNetworks(result[0].network, network));
         let timestamp = filterTimestamp(stats.date);
         do_check_eq(result[0].timestamp, timestamp);
         do_check_eq(result[0].rxBytes, 0);
         do_check_eq(result[0].txBytes, 0);
         do_check_eq(result[0].rxTotalBytes, stats.rxBytes);
         do_check_eq(result[0].txTotalBytes, stats.txBytes);
         run_next_test();
       });
     });
   });
 });
 
 add_test(function test_saveAppStats() {
-  var stats = {appId:          1,
-               connectionType: "wifi",
-               date:           new Date(),
-               rxBytes:        2234,
-               txBytes:        2234};
+  var networks = getNetworks();
+  var network = [networks[0].id, networks[0].type];
 
-  netStatsDb.clear(function (error, result) {
+  var stats = { appId:       1,
+                networkId:   networks[0].id,
+                networkType: networks[0].type,
+                date:        new Date(),
+                rxBytes:     2234,
+                txBytes:     2234};
+
+  netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
-        do_check_eq(result.length, 1);
-        do_check_eq(result[0].appId, stats.appId);
-        do_check_eq(result[0].connectionType, stats.connectionType);
+        // The clear function clears all records of the datbase but
+        // inserts a new element for each [appId, connectionId, connectionType]
+        // record to keep the track of rxTotalBytes / txTotalBytes.
+        // So at this point, we have two records, one for the appId 0 used in
+        // past tests and the new one for appId 1
+        do_check_eq(result.length, 2);
+        do_check_eq(result[1].appId, stats.appId);
+        do_check_true(compareNetworks(result[1].network, network));
         let timestamp = filterTimestamp(stats.date);
-        do_check_eq(result[0].timestamp, timestamp);
-        do_check_eq(result[0].rxBytes, stats.rxBytes);
-        do_check_eq(result[0].txBytes, stats.txBytes);
-        do_check_eq(result[0].rxTotalBytes, 0);
-        do_check_eq(result[0].txTotalBytes, 0);
+        do_check_eq(result[1].timestamp, timestamp);
+        do_check_eq(result[1].rxBytes, stats.rxBytes);
+        do_check_eq(result[1].txBytes, stats.txBytes);
+        do_check_eq(result[1].rxTotalBytes, 0);
+        do_check_eq(result[1].txTotalBytes, 0);
         run_next_test();
       });
     });
   });
 });
 
-function prepareFind(stats, callback) {
-  netStatsDb.clear(function (error, result) {
+function prepareFind(network, stats, callback) {
+  netStatsDb.clearStats(network, function (error, result) {
     do_check_eq(error, null);
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
         callback(error, result);
     });
   });
 }
 
 add_test(function test_find () {
+  var networks = getNetworks();
+  var networkWifi = [networks[0].id, networks[0].type];
+  var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
+  var appId = 0;
+
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var start = Date.now();
   var saveDate = filterTimestamp(new Date());
   var end = new Date(start + (sampleRate * (samples - 1)));
   start = new Date(start - sampleRate);
   var stats = [];
   for (var i = 0; i < samples; i++) {
-    stats.push({appId:               0,
-                connectionType: "wifi", timestamp:    saveDate + (sampleRate * i),
-                rxBytes:             0, txBytes:      10,
-                rxTotalBytes:        0, txTotalBytes: 0});
+    stats.push({ appId:              appId,
+                 network:      networkWifi, timestamp:    saveDate + (sampleRate * i),
+                 rxBytes:                0, txBytes:      10,
+                 rxTotalBytes:           0, txTotalBytes: 0 });
 
-    stats.push({appId:                 0,
-                connectionType: "mobile", timestamp:    saveDate + (sampleRate * i),
-                rxBytes:               0, txBytes:      10,
-                rxTotalBytes:          0, txTotalBytes: 0});
+    stats.push({ appId:                appId,
+                 network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
+                 rxBytes:                  0, txBytes:      10,
+                 rxTotalBytes:             0, txTotalBytes: 0 });
   }
 
-  prepareFind(stats, function(error, result) {
+  prepareFind(networks[0], stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
-      do_check_eq(result.connectionType, "wifi");
+      do_check_eq(result.network.id, networks[0].id);
+      do_check_eq(result.network.type, networks[0].type);
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
       do_check_eq(result.data.length, samples + 1);
       do_check_eq(result.data[0].rxBytes, null);
       do_check_eq(result.data[1].rxBytes, 0);
       do_check_eq(result.data[samples].rxBytes, 0);
-
-      netStatsDb.findAll(function (error, result) {
-        do_check_eq(error, null);
-        do_check_eq(result.connectionType, null);
-        do_check_eq(result.start.getTime(), start.getTime());
-        do_check_eq(result.end.getTime(), end.getTime());
-        do_check_eq(result.data.length, samples + 1);
-        do_check_eq(result.data[0].rxBytes, null);
-        do_check_eq(result.data[1].rxBytes, 0);
-        do_check_eq(result.data[1].txBytes, 20);
-        do_check_eq(result.data[samples].rxBytes, 0);
-        run_next_test();
-      }, {appId: 0, start: start, end: end});
-    }, {start: start, end: end, connectionType: "wifi", appId: 0});
+      run_next_test();
+    }, networks[0], start, end, appId);
   });
 });
 
 add_test(function test_findAppStats () {
+  var networks = getNetworks();
+  var networkWifi = [networks[0].id, networks[0].type];
+  var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
+
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var start = Date.now();
   var saveDate = filterTimestamp(new Date());
   var end = new Date(start + (sampleRate * (samples - 1)));
   start = new Date(start - sampleRate);
   var stats = [];
   for (var i = 0; i < samples; i++) {
-    stats.push({appId:               1,
-                connectionType: "wifi", timestamp:    saveDate + (sampleRate * i),
-                rxBytes:             0, txBytes:      10,
-                rxTotalBytes:        0, txTotalBytes: 0});
+    stats.push({ appId:                  1,
+                 network:      networkWifi, timestamp:    saveDate + (sampleRate * i),
+                 rxBytes:                0, txBytes:      10,
+                 rxTotalBytes:           0, txTotalBytes: 0 });
 
-    stats.push({appId:                 1,
-                connectionType: "mobile", timestamp:    saveDate + (sampleRate * i),
-                rxBytes:               0, txBytes:      10,
-                rxTotalBytes:          0, txTotalBytes: 0});
+    stats.push({ appId:                    1,
+                 network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
+                 rxBytes:                  0, txBytes:      10,
+                 rxTotalBytes:             0, txTotalBytes: 0 });
   }
 
-  prepareFind(stats, function(error, result) {
+  prepareFind(networks[0], stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
-      do_check_eq(result.connectionType, "wifi");
+      do_check_eq(result.network.id, networks[0].id);
+      do_check_eq(result.network.type, networks[0].type);
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
       do_check_eq(result.data.length, samples + 1);
       do_check_eq(result.data[0].rxBytes, null);
       do_check_eq(result.data[1].rxBytes, 0);
       do_check_eq(result.data[samples].rxBytes, 0);
-
-      netStatsDb.findAll(function (error, result) {
-        do_check_eq(error, null);
-        do_check_eq(result.connectionType, null);
-        do_check_eq(result.start.getTime(), start.getTime());
-        do_check_eq(result.end.getTime(), end.getTime());
-        do_check_eq(result.data.length, samples + 1);
-        do_check_eq(result.data[0].rxBytes, null);
-        do_check_eq(result.data[1].rxBytes, 0);
-        do_check_eq(result.data[1].txBytes, 20);
-        do_check_eq(result.data[samples].rxBytes, 0);
-        run_next_test();
-      }, {start: start, end: end, appId: 1});
-    }, {start: start, end: end, connectionType: "wifi", appId: 1});
+      run_next_test();
+    }, networks[0], start, end, 1);
   });
 });
 
 add_test(function test_saveMultipleAppStats () {
+  var networks = getNetworks();
+  var networkWifi = networks[0];
+  var networkMobile = networks[1]; // Fake mobile interface
+
   var saveDate = filterTimestamp(new Date());
   var cached = Object.create(null);
 
-  cached['1wifi'] = {appId:                 1,
-                     connectionType:   "wifi", date:    new Date(),
-                     rxBytes:               0, txBytes:      10};
+  cached['1wifi'] = { appId:                      1, date:           new Date(),
+                      networkId:     networkWifi.id, networkType: networkWifi.type,
+                      rxBytes:                    0, txBytes:      10 };
 
-  cached['1mobile'] = {appId:                 1,
-                       connectionType: "mobile", date:    new Date(),
-                       rxBytes:               0, txBytes:      10};
+  cached['1mobile'] = { appId:                    1, date:           new Date(),
+                        networkId: networkMobile.id, networkType: networkMobile.type,
+                        rxBytes:                  0, txBytes:      10 };
 
-  cached['2wifi'] = {appId:                 2,
-                     connectionType:   "wifi", date:    new Date(),
-                     rxBytes:               0, txBytes:      10};
+  cached['2wifi'] = { appId:                      2, date:           new Date(),
+                      networkId:     networkWifi.id, networkType: networkWifi.type,
+                      rxBytes:                    0, txBytes:      10 };
 
-  cached['2mobile'] = {appId:                 2,
-                       connectionType: "mobile", date:    new Date(),
-                       rxBytes:               0, txBytes:      10};
+  cached['2mobile'] = { appId:                    2, date:           new Date(),
+                        networkId: networkMobile.id, networkType: networkMobile.type,
+                        rxBytes:                  0, txBytes:      10 };
 
   let keys = Object.keys(cached);
   let index = 0;
 
-  netStatsDb.clear(function (error, result) {
+  networks.push(networkMobile);
+  netStatsDb.clearStats(networks, function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(cached[keys[index]],
       function callback(error, result) {
         do_check_eq(error, null);
 
         if (index == keys.length - 1) {
           netStatsDb.logAllRecords(function(error, result) {
+          // Again, result has two samples more than expected samples because
+          // clear inserts one empty sample for each network to keep totalBytes
+          // synchronized with netd counters. so the first two samples have to
+          // be discarted.
+          result.shift();
+          result.shift();
+
           do_check_eq(error, null);
           do_check_eq(result.length, 4);
           do_check_eq(result[0].appId, 1);
-          do_check_eq(result[0].connectionType, 'mobile');
+          do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type]));
           do_check_eq(result[0].rxBytes, 0);
           do_check_eq(result[0].txBytes, 10);
           run_next_test();
           });
         }
 
         index += 1;
         netStatsDb.saveStats(cached[keys[index]], callback);
--- a/dom/network/tests/unit_stats/test_networkstats_service.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service.js
@@ -1,56 +1,64 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 add_test(function test_clearDB() {
-  NetworkStatsService._db.clear(function onDBCleared(error, result) {
+  var networks = NetworkStatsService.availableNetworks();
+  NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) {
     do_check_eq(result, null);
     run_next_test();
   });
 });
 
+function getNetworkId() {
+  var network = (NetworkStatsService.availableNetworks())[0];
+  return NetworkStatsService.getNetworkId(network.id, network.type);
+}
 
 add_test(function test_networkStatsAvailable_ok() {
+  var netId = getNetworkId();
   NetworkStatsService.networkStatsAvailable(function (success, msg) {
     do_check_eq(success, true);
     run_next_test();
-  }, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, true, 1234, 4321, new Date());
+  }, netId, true, 1234, 4321, new Date());
 });
 
 add_test(function test_networkStatsAvailable_failure() {
+  var netId = getNetworkId();
   NetworkStatsService.networkStatsAvailable(function (success, msg) {
     do_check_eq(success, false);
     run_next_test();
-  }, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, false, 1234, 4321, new Date());
+  }, netId, false, 1234, 4321, new Date());
 });
 
-add_test(function test_update_invalidConnection() {
+add_test(function test_update_invalidNetwork() {
   NetworkStatsService.update(-1, function (success, msg) {
     do_check_eq(success, false);
-    do_check_eq(msg, "Invalid network type -1");
+    do_check_eq(msg, "Invalid network -1");
     run_next_test();
   });
 });
 
 add_test(function test_update() {
-  NetworkStatsService.update(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function (success, msg) {
+  var netId = getNetworkId();
+  NetworkStatsService.update(netId, function (success, msg) {
     do_check_eq(success, true);
     run_next_test();
   });
 });
 
 add_test(function test_updateQueueIndex() {
-  NetworkStatsService.updateQueue = [{type: 0, callbacks: null},
-                                     {type: 1, callbacks: null},
-                                     {type: 2, callbacks: null},
-                                     {type: 3, callbacks: null},
-                                     {type: 4, callbacks: null}];
+  NetworkStatsService.updateQueue = [{netId: 0, callbacks: null},
+                                     {netId: 1, callbacks: null},
+                                     {netId: 2, callbacks: null},
+                                     {netId: 3, callbacks: null},
+                                     {netId: 4, callbacks: null}];
   var index = NetworkStatsService.updateQueueIndex(3);
   do_check_eq(index, 3);
   index = NetworkStatsService.updateQueueIndex(10);
   do_check_eq(index, -1);
 
   NetworkStatsService.updateQueue = [];
   run_next_test();
 });
@@ -58,48 +66,54 @@ add_test(function test_updateQueueIndex(
 add_test(function test_updateAllStats() {
   NetworkStatsService.updateAllStats(function(success, msg) {
     do_check_eq(success, true);
     run_next_test();
   });
 });
 
 add_test(function test_updateStats_ok() {
-  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function(success, msg){
+  var netId = getNetworkId();
+  NetworkStatsService.updateStats(netId, function(success, msg){
     do_check_eq(success, true);
     run_next_test();
   });
 });
 
 add_test(function test_updateStats_failure() {
   NetworkStatsService.updateStats(-1, function(success, msg){
     do_check_eq(success, false);
     run_next_test();
   });
 });
 
 add_test(function test_queue() {
-  // Fill connections with fake network interfaces (wlan0 and rmnet0)
+  // Fill networks with fake network interfaces
   // to enable netd async requests
-  NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_WIFI]
-                     .network.name = 'wlan0';
-  NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE]
-                     .network.name = 'rmnet0';
+  var network = {id: "1234", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE};
+  var netId1 = NetworkStatsService.getNetworkId(network.id, network.type);
+  NetworkStatsService._networks[netId1] = { network: network,
+                                            interfaceName: "net1" };
 
-  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI);
-  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
+  network = {id: "5678", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE};
+  var netId2 = NetworkStatsService.getNetworkId(network.id, network.type);
+  NetworkStatsService._networks[netId2] = { network: network,
+                                            interfaceName: "net2" };
+
+  NetworkStatsService.updateStats(netId1);
+  NetworkStatsService.updateStats(netId2);
   do_check_eq(NetworkStatsService.updateQueue.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1);
 
   var callback = function(success, msg) {
     return;
   };
 
-  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, callback);
-  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, callback);
+  NetworkStatsService.updateStats(netId1, callback);
+  NetworkStatsService.updateStats(netId2, callback);
 
   do_check_eq(NetworkStatsService.updateQueue.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks[0], null);
   do_check_neq(NetworkStatsService.updateQueue[0].callbacks[1], null);
 
   run_next_test();
 });
--- a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
@@ -4,116 +4,154 @@
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
                                    "@mozilla.org/networkstatsServiceProxy;1",
                                    "nsINetworkStatsServiceProxy");
 
+function mokConvertNetworkInterface() {
+  NetworkStatsService.convertNetworkInterface = function(aNetwork) {
+    if (aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
+        aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
+      return null;
+    }
+
+    let id = '0';
+    if (aNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
+      id = '1234'
+    }
+
+    let netId = this.getNetworkId(id, aNetwork.type);
+
+    if (!this._networks[netId]) {
+      this._networks[netId] = Object.create(null);
+      this._networks[netId].network = { id: id,
+                                        type: aNetwork.type };
+    }
+
+    return netId;
+  };
+}
+
 add_test(function test_saveAppStats() {
   var cachedAppStats = NetworkStatsService.cachedAppStats;
   var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
   var samples = 5;
 
+  // Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
+  // be instantiated, these two vars will emulate it by filling the properties
+  // that will be used.
+  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
+  var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
+
+  // Insert fake mobile network interface in NetworkStatsService
+  var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+
   do_check_eq(Object.keys(cachedAppStats).length, 0);
 
   for (var i = 0; i < samples; i++) {
-    nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
-                          timestamp, 10, 20);
+    nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
 
-    nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
-                          timestamp, 10, 20);
+    nssProxy.saveAppStats(1, mobile, timestamp, 10, 20);
   }
 
-  var key1 = 1 + 'wifi';
-  var key2 = 1 + 'mobile';
+  var key1 = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
+  var key2 = 1 + mobileNetId;
 
   do_check_eq(Object.keys(cachedAppStats).length, 2);
   do_check_eq(cachedAppStats[key1].appId, 1);
-  do_check_eq(cachedAppStats[key1].connectionType, 'wifi');
+  do_check_eq(cachedAppStats[key1].networkId, wifi.id);
+  do_check_eq(cachedAppStats[key1].networkType, wifi.type);
   do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000,
               Math.floor(timestamp / 1000));
   do_check_eq(cachedAppStats[key1].rxBytes, 50);
   do_check_eq(cachedAppStats[key1].txBytes, 100);
   do_check_eq(cachedAppStats[key2].appId, 1);
-  do_check_eq(cachedAppStats[key2].connectionType, 'mobile');
+  do_check_eq(cachedAppStats[key2].networkId, mobile.id);
+  do_check_eq(cachedAppStats[key2].networkType, mobile.type);
   do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000,
               Math.floor(timestamp / 1000));
   do_check_eq(cachedAppStats[key2].rxBytes, 50);
   do_check_eq(cachedAppStats[key2].txBytes, 100);
 
   run_next_test();
 });
 
 add_test(function test_saveAppStatsWithDifferentDates() {
   var today = NetworkStatsService.cachedAppStatsDate;
   var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
-  var key = 1 + 'wifi';
+
+  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
+  var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
+
+  var key = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
 
   NetworkStatsService.updateCachedAppStats(
     function (success, msg) {
       do_check_eq(success, true);
 
       do_check_eq(Object.keys(NetworkStatsService.cachedAppStats).length, 0);
 
-      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
-                            today.getTime(), 10, 20);
+      nssProxy.saveAppStats(1, wifi, today.getTime(), 10, 20);
 
-      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
-                            today.getTime(), 10, 20);
+      nssProxy.saveAppStats(1, mobile, today.getTime(), 10, 20);
 
       var saveAppStatsCb = {
         notify: function notify(success, message) {
           do_check_eq(success, true);
 
           var cachedAppStats = NetworkStatsService.cachedAppStats;
-          var key = 2 + 'mobile';
+          var key = 2 + NetworkStatsService.getNetworkId(mobile.id, mobile.type);
           do_check_eq(Object.keys(cachedAppStats).length, 1);
           do_check_eq(cachedAppStats[key].appId, 2);
-          do_check_eq(cachedAppStats[key].connectionType, 'mobile');
+          do_check_eq(cachedAppStats[key].networkId, mobile.id);
+          do_check_eq(cachedAppStats[key].networkType, mobile.type);
           do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000,
                       Math.floor(tomorrow.getTime() / 1000));
           do_check_eq(cachedAppStats[key].rxBytes, 30);
           do_check_eq(cachedAppStats[key].txBytes, 40);
 
           run_next_test();
         }
       };
 
-      nssProxy.saveAppStats(2, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
-                            tomorrow.getTime(), 30, 40, saveAppStatsCb);
+      nssProxy.saveAppStats(2, mobile, tomorrow.getTime(), 30, 40, saveAppStatsCb);
     }
   );
 });
 
 add_test(function test_saveAppStatsWithMaxCachedTraffic() {
   var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
   var maxtraffic = NetworkStatsService.maxCachedTraffic;
+  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
 
   NetworkStatsService.updateCachedAppStats(
     function (success, msg) {
       do_check_eq(success, true);
 
       var cachedAppStats = NetworkStatsService.cachedAppStats;
       do_check_eq(Object.keys(cachedAppStats).length, 0);
 
-      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
-                            timestamp, 10, 20);
+      nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
 
       do_check_eq(Object.keys(cachedAppStats).length, 1);
 
-      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
-                            timestamp, maxtraffic, 20);
+      nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20);
 
       do_check_eq(Object.keys(cachedAppStats).length, 0);
 
       run_next_test();
   });
 });
 
 function run_test() {
   do_get_profile();
 
   Cu.import("resource://gre/modules/NetworkStatsService.jsm");
 
+  // Function convertNetworkInterface of NetworkStatsService causes errors when dealing
+  // with RIL to get the iccid, so overwrite it.
+  mokConvertNetworkInterface();
+
   run_next_test();
 }
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1070,17 +1070,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
 // compartment for callers that plan to hold onto the result or do anything
 // substantial with it.
 static JSObject *
 GetNPObjectWrapper(JSContext *cx, JSObject *aObj, bool wrapResult = true)
 {
   JS::Rooted<JSObject*> obj(cx, aObj);
   while (obj && (obj = js::CheckedUnwrap(obj))) {
     if (JS_GetClass(obj) == &sNPObjectJSWrapperClass) {
-      if (wrapResult && !JS_WrapObject(cx, obj.address())) {
+      if (wrapResult && !JS_WrapObject(cx, &obj)) {
         return NULL;
       }
       return obj;
     }
     if (!::JS_GetPrototype(cx, obj, &obj)) {
       return NULL;
     }
   }
@@ -1725,17 +1725,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
 
     return nullptr;
   }
 
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     // npobj is one of our own, return its existing JSObject.
 
     JS::Rooted<JSObject*> obj(cx, ((nsJSObjWrapper *)npobj)->mJSObj);
-    if (!JS_WrapObject(cx, obj.address())) {
+    if (!JS_WrapObject(cx, &obj)) {
       return NULL;
     }
     return obj;
   }
 
   if (!npp) {
     NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
 
@@ -1762,17 +1762,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
 
     return nullptr;
   }
 
   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
     // Found a live NPObject wrapper. It may not be in the same compartment
     // as cx, so we need to wrap it before returning it.
     JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
-    if (!JS_WrapObject(cx, obj.address())) {
+    if (!JS_WrapObject(cx, &obj)) {
       return NULL;
     }
     return obj;
   }
 
   entry->mNPObj = npobj;
   entry->mNpp = npp;
 
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -220,33 +220,16 @@ public:
   }
 
 private:
   nsCOMPtr<nsIDOMGeoPosition> mPosition;
   nsRefPtr<nsGeolocationRequest> mRequest;
   nsRefPtr<Geolocation> mLocator;
 };
 
-class RequestRestartTimerEvent : public nsRunnable
-{
-public:
-  RequestRestartTimerEvent(nsGeolocationRequest* aRequest)
-    : mRequest(aRequest)
-  {
-  }
-
-  NS_IMETHOD Run() {
-    mRequest->SetTimeoutTimer();
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<nsGeolocationRequest> mRequest;
-};
-
 ////////////////////////////////////////////////////
 // PositionError
 ////////////////////////////////////////////////////
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
@@ -631,24 +614,22 @@ NS_INTERFACE_MAP_BEGIN(nsGeolocationServ
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsGeolocationService)
 NS_IMPL_RELEASE(nsGeolocationService)
 
 
 static bool sGeoEnabled = true;
 static bool sGeoInitPending = true;
-static bool sGeoIgnoreLocationFilter = false;
 static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
 
 nsresult nsGeolocationService::Init()
 {
   Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
   Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
-  Preferences::AddBoolVarCache(&sGeoIgnoreLocationFilter, "geo.ignore.location_filter", sGeoIgnoreLocationFilter);
 
   if (!sGeoEnabled) {
     return NS_ERROR_FAILURE;
   }
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     sGeoInitPending = false;
     return NS_OK;
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -847,27 +847,27 @@ RILContentHelper.prototype = {
         lockType: lockType,
         requestId: requestId
       }
     });
     return request;
   },
 
   sendMMI: function sendMMI(window, mmi) {
-    // We need to save the global window to get the proper MMIError
-    // constructor once we get the reply from the parent process.
-    this._window = window;
-
     debug("Sending MMI " + mmi);
     if (!window) {
       throw Components.Exception("Can't get window object",
                                  Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
     let requestId = this.getRequestId(request);
+    // We need to save the global window to get the proper MMIError
+    // constructor once we get the reply from the parent process.
+    this._windowsMap[requestId] = window;
+
     cpmm.sendAsyncMessage("RIL:SendMMI", {
       clientId: 0,
       data: {
         mmi: mmi,
         requestId: requestId
       }
     });
     return request;
@@ -1829,16 +1829,18 @@ RILContentHelper.prototype = {
       return;
     }
     Services.DOMRequest.fireSuccess(request, null);
   },
 
   handleSendCancelMMI: function handleSendCancelMMI(message) {
     debug("handleSendCancelMMI " + JSON.stringify(message));
     let request = this.takeRequest(message.requestId);
+    let requestWindow = this._windowsMap[message.requestId];
+    delete this._windowsMap[message.requestId];
     if (!request) {
       return;
     }
 
     let success = message.success;
 
     // We expect to have an IMEI at this point if the request was supposed
     // to query for the IMEI, so getting a successful reply from the RIL
@@ -1863,20 +1865,20 @@ RILContentHelper.prototype = {
       additionalInformation: message.additionalInformation
     };
 
     if (success) {
       result.statusMessage = message.statusMessage;
       let mmiResult = new DOMMMIResult(result);
       Services.DOMRequest.fireSuccess(request, mmiResult);
     } else {
-      let mmiError = new this._window.DOMMMIError(result.serviceCode,
-                                                  message.errorMsg,
-                                                  null,
-                                                  result.additionalInformation);
+      let mmiError = new requestWindow.DOMMMIError(result.serviceCode,
+                                                   message.errorMsg,
+                                                   null,
+                                                   result.additionalInformation);
       Services.DOMRequest.fireDetailedError(request, mmiError);
     }
   },
 
   _deliverEvent: function _deliverEvent(listenerType, name, args) {
     let thisListeners = this[listenerType];
     if (!thisListeners) {
       return;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -237,20 +237,16 @@ WEBIDL_FILES = [
     'OfflineAudioCompletionEvent.webidl',
     'OfflineAudioContext.webidl',
     'OfflineResourceList.webidl',
     'OscillatorNode.webidl',
     'PaintRequest.webidl',
     'PaintRequestList.webidl',
     'PannerNode.webidl',
     'ParentNode.webidl',
-    'PeerConnectionImpl.webidl',
-    'PeerConnectionImplEnums.webidl',
-    'PeerConnectionObserver.webidl',
-    'PeerConnectionObserverEnums.webidl',
     'Performance.webidl',
     'PerformanceNavigation.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
     'PhoneNumberService.webidl',
     'Plugin.webidl',
     'PluginArray.webidl',
     'Position.webidl',
@@ -439,16 +435,20 @@ if CONFIG['MOZ_WEBGL']:
         'WebGL2RenderingContext.webidl',
         'WebGLRenderingContext.webidl',
     ]
 
 if CONFIG['MOZ_WEBRTC']:
     WEBIDL_FILES += [
         'DataChannel.webidl',
         'MediaStreamList.webidl',
+        'PeerConnectionImpl.webidl',
+        'PeerConnectionImplEnums.webidl',
+        'PeerConnectionObserver.webidl',
+        'PeerConnectionObserverEnums.webidl',
     ]
 
 if CONFIG['MOZ_WEBSPEECH']:
     WEBIDL_FILES += [
         'SpeechGrammar.webidl',
         'SpeechGrammarList.webidl',
         'SpeechRecognition.webidl',
         'SpeechRecognitionAlternative.webidl',
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -470,17 +470,20 @@ nsWindowWatcher::OpenWindowInternal(nsID
   if (aFeatures) {
     features.Assign(aFeatures);
     featuresSpecified = true;
     features.StripWhitespace();
   }
 
   // try to find an extant window with the given name
   nsCOMPtr<nsIDOMWindow> foundWindow;
-  SafeGetWindowByName(name, aParent, getter_AddRefs(foundWindow));
+  if (SafeGetWindowByName(name, aParent, getter_AddRefs(foundWindow)) ==
+      NS_ERROR_DOM_INVALID_ACCESS_ERR) {
+    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+  }
   GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
 
   // no extant window? make a new one.
 
   // If no parent, consider it chrome.
   bool hasChromeParent = true;
   if (aParent) {
     // Check if the parent document has chrome privileges.
@@ -538,31 +541,36 @@ nsWindowWatcher::OpenWindowInternal(nsID
     // the initial about:blank document to be system, so that the subsequent XUL
     // load can reuse the inner window and avoid blowing away expandos. As such,
     // we decide whether to load with the principal of the caller or of the parent
     // based on whether the docshell type is chrome or content.
 
     callerContextGuard.Push(cx);
   }
 
+  uint32_t activeDocsSandboxFlags = 0;
   if (!newDocShellItem) {
     // We're going to either open up a new window ourselves or ask a
     // nsIWindowProvider for one.  In either case, we'll want to set the right
     // name on it.
     windowNeedsName = true;
 
-    // If the parent trying to open a new window is sandboxed,
-    // this is not allowed and we fail here.
+    // If the parent trying to open a new window is sandboxed
+    // without 'allow-popups', this is not allowed and we fail here.
     if (aParent) {
       nsCOMPtr<nsIDOMDocument> domdoc;
       aParent->GetDocument(getter_AddRefs(domdoc));
       nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
 
-      if (doc && (doc->GetSandboxFlags() & SANDBOXED_NAVIGATION)) {
-        return NS_ERROR_FAILURE;
+      if (doc) {
+        // Save sandbox flags for copying to new browsing context (docShell).
+        activeDocsSandboxFlags = doc->GetSandboxFlags();
+        if (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
+          return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+        }
       }
     }
 
     // Now check whether it's ok to ask a window provider for a window.  Don't
     // do it if we're opening a dialog or if our parent is a chrome window or
     // if we're opening something that has modal, dialog, or chrome flags set.
     nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
     if (!aDialog && !chromeWin &&
@@ -704,16 +712,26 @@ nsWindowWatcher::OpenWindowInternal(nsID
   }
 
   // better have a window to use by this point
   if (!newDocShellItem)
     return rv;
 
   nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
   NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
+
+  // Set up sandboxing attributes if the window is new.
+  // The flags can only be non-zero for new windows.
+  if (activeDocsSandboxFlags != 0) {
+    newDocShell->SetSandboxFlags(activeDocsSandboxFlags);
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aParent);
+    if (window) {
+      newDocShell->SetOnePermittedSandboxedNavigator(window->GetDocShell());
+    }
+  }
   
<