Merge m-i to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 05 Oct 2013 17:39:34 -0700
changeset 149996 f191c70fcbfba32d19f34bb377b2f6bd1e31222c
parent 149974 87fd173f56f185c01a652d984ada2accc296f8a4 (current diff)
parent 149995 8434bf06134c7284d28c365be764a13292d1be49 (diff)
child 149999 977391f7a45d7252038a41a0468720401cc3d585
child 150003 22a5355a3460db1e85482735cce5f24126a026cd
child 150009 641378038788b77a1d1915381975bc14513bdf2c
child 155821 a657ab82443bb4d3571cfc8f8ac92d7a0219577c
push id25412
push userphilringnalda@gmail.com
push dateSun, 06 Oct 2013 00:39:45 +0000
treeherderautoland@f191c70fcbfb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
f191c70fcbfb / 27.0a1 / 20131006030201 / files
nightly linux64
f191c70fcbfb / 27.0a1 / 20131006030201 / files
nightly mac
f191c70fcbfb / 27.0a1 / 20131006030201 / files
nightly win32
f191c70fcbfb / 27.0a1 / 20131006030201 / files
nightly win64
f191c70fcbfb / 27.0a1 / 20131006030201 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c
--- a/content/events/test/test_all_synthetic_events.html
+++ b/content/events/test/test_all_synthetic_events.html
@@ -379,16 +379,20 @@ const kEventConstructors = {
                                                          var e = document.createEvent("touchevent");
                                                          e.initTouchEvent(aName, aProps.bubbles, aProps.cancelable,
                                                                           aProps.view, aProps.detail,
                                                                           aProps.ctrlKey, aProps.altKey, aProps.shiftKey, aProps.metaKey,
                                                                           aProps.touches, aProps.targetTouches, aProps.changedTouches);
                                                          return e;
                                                        },
                                              },
+  TrackEvent:                                { create: function (aName, aProps) {
+                                                         return new TrackEvent(aName, aProps);
+                                                       },
+                                             },
   TransitionEvent:                           { create: function (aName, aProps) {
                                                          return new TransitionEvent(aName, aProps);
                                                        },
                                              },
   UIEvent:                                   { create: function (aName, aProps) {
                                                          return new UIEvent(aName, aProps);
                                                        },
                                              },
--- a/content/html/content/reftests/468263-2-alternate-ref.html
+++ b/content/html/content/reftests/468263-2-alternate-ref.html
@@ -1,6 +1,7 @@
 <!DOCTYPE HTML>
 <html>
 <body>
+    <img id="image1" src="">
     <input id="image3" type="image">
 </body>
 </html>
--- a/content/media/TextTrackList.cpp
+++ b/content/media/TextTrackList.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/TextTrackList.h"
 #include "mozilla/dom/TextTrackListBinding.h"
+#include "mozilla/dom/TrackEvent.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(TextTrackList,
                                      nsDOMEventTargetHelper,
                                      mGlobal,
                                      mTextTracks)
@@ -49,18 +51,20 @@ TextTrackList::IndexedGetter(uint32_t aI
 already_AddRefed<TextTrack>
 TextTrackList::AddTextTrack(HTMLMediaElement* aMediaElement,
                             TextTrackKind aKind,
                             const nsAString& aLabel,
                             const nsAString& aLanguage)
 {
   nsRefPtr<TextTrack> track = new TextTrack(mGlobal, aMediaElement, aKind,
                                             aLabel, aLanguage);
-  mTextTracks.AppendElement(track);
-  // TODO: dispatch addtrack event
+  if (mTextTracks.AppendElement(track)) {
+    CreateAndDispatchTrackEventRunner(track, NS_LITERAL_STRING("addtrack"));
+  }
+
   return track.forget();
 }
 
 TextTrack*
 TextTrackList::GetTrackById(const nsAString& aId)
 {
   nsAutoString id;
   for (uint32_t i = 0; i < Length(); i++) {
@@ -68,23 +72,65 @@ TextTrackList::GetTrackById(const nsAStr
     if (aId.Equals(id)) {
       return mTextTracks[i];
     }
   }
   return nullptr;
 }
 
 void
-TextTrackList::RemoveTextTrack(const TextTrack& aTrack)
+TextTrackList::RemoveTextTrack(TextTrack& aTrack)
 {
-  mTextTracks.RemoveElement(&aTrack);
+  if (mTextTracks.RemoveElement(&aTrack)) {
+    CreateAndDispatchTrackEventRunner(&aTrack, NS_LITERAL_STRING("removetrack"));
+  }
 }
 
 void
 TextTrackList::DidSeek()
 {
   for (uint32_t i = 0; i < mTextTracks.Length(); i++) {
     mTextTracks[i]->SetDirty();
   }
 }
 
+class TrackEventRunner MOZ_FINAL: public nsRunnable
+{
+public:
+  TrackEventRunner(TextTrackList* aList, TrackEvent* aEvent)
+    : mList(aList)
+    , mEvent(aEvent)
+  {}
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    return mList->DispatchTrackEvent(mEvent);
+  }
+
+private:
+  nsRefPtr<TextTrackList> mList;
+  nsRefPtr<TrackEvent> mEvent;
+};
+
+nsresult
+TextTrackList::DispatchTrackEvent(TrackEvent* aEvent)
+{
+  return DispatchTrustedEvent(aEvent);
+}
+
+void
+TextTrackList::CreateAndDispatchTrackEventRunner(TextTrack* aTrack,
+                                                 const nsAString& aEventName)
+{
+  TrackEventInitInitializer eventInit;
+  eventInit.mBubbles = false;
+  eventInit.mCancelable = false;
+  eventInit.mTrack = aTrack;
+  nsRefPtr<TrackEvent> trackEvent =
+    TrackEvent::Constructor(this, aEventName, eventInit);
+
+  // Dispatch the TrackEvent asynchronously.
+  nsCOMPtr<nsIRunnable> event = new TrackEventRunner(this, trackEvent);
+  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/media/TextTrackList.h
+++ b/content/media/TextTrackList.h
@@ -9,16 +9,19 @@
 
 #include "mozilla/dom/TextTrack.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDOMEventTargetHelper.h"
 
 namespace mozilla {
 namespace dom {
 
+class TrackEvent;
+class TrackEventRunner;
+
 class TextTrackList MOZ_FINAL : public nsDOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextTrackList, nsDOMEventTargetHelper)
 
   TextTrackList(nsISupports* aGlobal);
 
@@ -45,23 +48,28 @@ public:
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage);
   TextTrack* GetTrackById(const nsAString& aId);
 
   void AddTextTrack(TextTrack* aTextTrack) {
     mTextTracks.AppendElement(aTextTrack);
   }
 
-  void RemoveTextTrack(const TextTrack& aTrack);
+  void RemoveTextTrack(TextTrack& aTrack);
   void DidSeek();
 
+  nsresult DispatchTrackEvent(TrackEvent* aEvent);
+
   IMPL_EVENT_HANDLER(addtrack)
   IMPL_EVENT_HANDLER(removetrack)
 
 private:
   nsCOMPtr<nsISupports> mGlobal;
   nsTArray< nsRefPtr<TextTrack> > mTextTracks;
+
+  void CreateAndDispatchTrackEventRunner(TextTrack* aTrack,
+                                         const nsAString& aEventName);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TextTrackList_h
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -140,16 +140,17 @@ MOCHITEST_FILES = \
 		test_streams_element_capture.html \
 		test_streams_element_capture_reset.html \
 		test_streams_element_capture_createObjectURL.html \
 		test_streams_gc.html \
 		test_streams_tracks.html \
 		$(filter disabled-for-intermittent-failures--bug-608634, test_error_in_video_document.html) \
 		test_texttrack.html \
 		test_texttrackcue.html \
+		test_trackevent.html \
 		test_texttrackregion.html \
 		test_timeupdate_small_files.html \
 		test_unseekable.html \
 		test_VideoPlaybackQuality.html \
 		test_VideoPlaybackQuality_disabled.html \
 		test_webvtt_disabled.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_trackevent.html
@@ -0,0 +1,62 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 893309 - Implement TrackEvent</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
+  function() {
+    var video = document.createElement("video");
+    isnot(video.textTracks, undefined, "HTMLMediaElement::TextTrack() property should be available.")
+    ok(typeof video.addTextTrack == "function", "HTMLMediaElement::AddTextTrack() function should be available.")
+
+    var trackList = video.textTracks;
+    is(trackList.length, 0, "Length should be 0.");
+
+    var evtTextTrack, numOfCalls = 0;
+    trackList.onaddtrack = function(event) {
+      ok(event instanceof TrackEvent, "Fired event from onaddtrack should be a TrackEvent");
+      is(event.type, "addtrack", "Event type should be addtrack");
+      ok(event.isTrusted, "Event should be trusted!");
+      ok(!event.bubbles, "Event shouldn't bubble!");
+      ok(!event.cancelable, "Event shouldn't be cancelable!");
+      
+      evtTextTrack = event.track;
+      ok(textTrack[numOfCalls] === evtTextTrack, "Text tracks should be the same");
+      is(evtTextTrack.label, label[numOfCalls], "Label should be set to "+ label[numOfCalls]);
+      is(evtTextTrack.language, language[numOfCalls], "Language should be " + language[numOfCalls]);
+      is(evtTextTrack.kind, kind[numOfCalls], "Kind should be " + kind[numOfCalls]);
+
+      if (++numOfCalls == 3) {
+        SimpleTest.finish();
+      }
+    };
+
+    var label = ["Oasis", "Coldplay", "t.A.T.u"];
+    language = ["en-CA", "en-GB", "ru" ];
+    kind = ["subtitles", "captions", "chapters"];
+
+    var textTrack = new Array(3);
+    for (var i = 0; i < 3; ++i) {
+      textTrack[i] = video.addTextTrack(kind[i], label[i], language[i]);
+      is(trackList.length, i + 1, "Length should be " + (i+1));
+    }
+
+    //TODO: Tests for removetrack event to be added along with bug 882677
+  }
+);
+</script>
+</pre>
+</body>
+</html>
+
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -118,22 +118,24 @@ nsDSURIContentListener::DoContent(const 
 
         mDocShell->SetLoadType(aIsContentPreferred ? LOAD_LINK : LOAD_NORMAL);
     }
 
     rv = mDocShell->CreateContentViewer(aContentType, request, aContentHandler);
 
     if (rv == NS_ERROR_REMOTE_XUL) {
       request->Cancel(rv);
+      *aAbortProcess = true;
       return NS_OK;
     }
 
-    if (NS_FAILED(rv)) {
-       // it's okay if we don't know how to handle the content   
-        return NS_OK;
+    if (NS_FAILED(rv)) { 
+      // we don't know how to handle the content
+      *aContentHandler = nullptr;
+      return rv;
     }
 
     if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
         nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(static_cast<nsIDocShell*>(mDocShell));
         NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
         domWindow->Focus();
     }
 
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -39,35 +39,40 @@ WebappsRegistry.prototype = {
     if (msg.oid != this._id)
       return
     let req = this.getRequest(msg.requestID);
     if (!req)
       return;
     let app = msg.app;
     switch (aMessage.name) {
       case "Webapps:Install:Return:OK":
+        this.removeMessageListeners("Webapps:Install:Return:KO");
         Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
         cpmm.sendAsyncMessage("Webapps:Install:Return:Ack",
                               { manifestURL : app.manifestURL });
         break;
       case "Webapps:Install:Return:KO":
+        this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireError(req, msg.error || "DENIED");
         break;
       case "Webapps:GetSelf:Return:OK":
+        this.removeMessageListeners(aMessage.name);
         if (msg.apps.length) {
           app = msg.apps[0];
           Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
         } else {
           Services.DOMRequest.fireSuccess(req, null);
         }
         break;
       case "Webapps:CheckInstalled:Return:OK":
+        this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireSuccess(req, msg.app);
         break;
       case "Webapps:GetInstalled:Return:OK":
+        this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window));
         break;
     }
     this.removeRequest(msg.requestID);
   },
 
   _getOrigin: function(aURL) {
     let uri = Services.io.newURI(aURL, null, null);
@@ -150,46 +155,51 @@ WebappsRegistry.prototype = {
   // mozIDOMApplicationRegistry implementation
 
   install: function(aURL, aParams) {
     let uri = this._validateURL(aURL);
 
     let request = this.createRequest();
 
     if (this._ensureForeground(request)) {
+      this.addMessageListeners("Webapps:Install:Return:KO");
       cpmm.sendAsyncMessage("Webapps:Install",
                             this._prepareInstall(uri, request, aParams, false));
     }
 
     return request;
   },
 
   getSelf: function() {
     let request = this.createRequest();
+    this.addMessageListeners("Webapps:GetSelf:Return:OK");
     cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href),
                                                appId: this._window.document.nodePrincipal.appId,
                                                oid: this._id,
                                                requestID: this.getRequestId(request) });
     return request;
   },
 
   checkInstalled: function(aManifestURL) {
     let manifestURL = Services.io.newURI(aManifestURL, null, this._window.document.baseURIObject);
     this._window.document.nodePrincipal.checkMayLoad(manifestURL, true, false);
 
     let request = this.createRequest();
+
+    this.addMessageListeners("Webapps:CheckInstalled:Return:OK");
     cpmm.sendAsyncMessage("Webapps:CheckInstalled", { origin: this._getOrigin(this._window.location.href),
                                                       manifestURL: manifestURL.spec,
                                                       oid: this._id,
                                                       requestID: this.getRequestId(request) });
     return request;
   },
 
   getInstalled: function() {
     let request = this.createRequest();
+    this.addMessageListeners("Webapps:GetInstalled:Return:OK");
     cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href),
                                                     oid: this._id,
                                                     requestID: this.getRequestId(request) });
     return request;
   },
 
   get mgmt() {
     if (!this.hasMgmtPrivilege) {
@@ -208,29 +218,27 @@ WebappsRegistry.prototype = {
   },
 
   installPackage: function(aURL, aParams) {
     let uri = this._validateURL(aURL);
 
     let request = this.createRequest();
 
     if (this._ensureForeground(request)) {
+      this.addMessageListeners("Webapps:Install:Return:KO");
       cpmm.sendAsyncMessage("Webapps:InstallPackage",
                             this._prepareInstall(uri, request, aParams, true));
     }
 
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
-    this.initDOMRequestHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
-                              "Webapps:GetInstalled:Return:OK",
-                              "Webapps:GetSelf:Return:OK",
-                              "Webapps:CheckInstalled:Return:OK" ]);
+    this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK");
 
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
     this._id = util.outerWindowID;
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
                           { messages: ["Webapps:Install:Return:OK"]});
 
     let principal = aWindow.document.nodePrincipal;
@@ -333,26 +341,25 @@ WebappsApplication.prototype = {
     this._onprogress = null;
     this._ondownloadsuccess = null;
     this._ondownloaderror = null;
     this._ondownloadavailable = null;
     this._ondownloadapplied = null;
 
     this._downloadError = null;
 
-    this.initDOMRequestHelper(aWindow, ["Webapps:OfflineCache",
-                              "Webapps:CheckForUpdate:Return:OK",
-                              "Webapps:CheckForUpdate:Return:KO",
-                              "Webapps:Launch:Return:OK",
-                              "Webapps:Launch:Return:KO",
-                              "Webapps:PackageEvent",
-                              "Webapps:ClearBrowserData:Return",
-                              "Webapps:Connect:Return:OK",
-                              "Webapps:Connect:Return:KO",
-                              "Webapps:GetConnections:Return:OK"]);
+    this.initDOMRequestHelper(aWindow, [
+      "Webapps:OfflineCache",
+      "Webapps:CheckForUpdate:Return:OK",
+      "Webapps:CheckForUpdate:Return:KO",
+      "Webapps:PackageEvent",
+      "Webapps:Connect:Return:OK",
+      "Webapps:Connect:Return:KO",
+      "Webapps:GetConnections:Return:OK"
+    ]);
 
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
                           {
                             messages: ["Webapps:OfflineCache",
                                        "Webapps:PackageEvent",
                                        "Webapps:CheckForUpdate:Return:OK"],
                             app: {
                               id: this.id,
@@ -436,30 +443,33 @@ WebappsApplication.prototype = {
                           { manifestURL: this.manifestURL,
                             oid: this._id,
                             requestID: this.getRequestId(request) });
     return request;
   },
 
   launch: function(aStartPoint) {
     let request = this.createRequest();
+    this.addMessageListeners(["Webapps:Launch:Return:OK",
+                              "Webapps:Launch:Return:KO"]);
     cpmm.sendAsyncMessage("Webapps:Launch", { origin: this.origin,
                                               manifestURL: this.manifestURL,
                                               startPoint: aStartPoint || "",
                                               oid: this._id,
                                               timestamp: Date.now(),
                                               requestID: this.getRequestId(request) });
     return request;
   },
 
   clearBrowserData: function() {
     let request = this.createRequest();
     let browserChild =
       BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
     if (browserChild) {
+      this.addMessageListeners("Webapps:ClearBrowserData:Return");
       browserChild.messageManager.sendAsyncMessage(
         "Webapps:ClearBrowserData",
         { manifestURL: this.manifestURL,
           oid: this._id,
           requestID: this.getRequestId(request) }
       );
     } else {
       let runnable = {
@@ -531,19 +541,23 @@ WebappsApplication.prototype = {
     // ondownload* callbacks should be triggered on all app instances
     if ((msg.oid != this._id || !req) &&
         aMessage.name !== "Webapps:OfflineCache" &&
         aMessage.name !== "Webapps:PackageEvent" &&
         aMessage.name !== "Webapps:CheckForUpdate:Return:OK")
       return;
     switch (aMessage.name) {
       case "Webapps:Launch:Return:KO":
+        this.removeMessageListeners(["Webapps:Launch:Return:OK",
+                                     "Webapps:Launch:Return:KO"]);
         Services.DOMRequest.fireError(req, "APP_INSTALL_PENDING");
         break;
       case "Webapps:Launch:Return:OK":
+        this.removeMessageListeners(["Webapps:Launch:Return:OK",
+                                     "Webapps:Launch:Return:KO"]);
         Services.DOMRequest.fireSuccess(req, null);
         break;
       case "Webapps:CheckForUpdate:Return:KO":
         Services.DOMRequest.fireError(req, msg.error);
         break;
       case "Webapps:CheckForUpdate:Return:OK":
         if (msg.manifestURL != this.manifestURL)
           return;
@@ -643,16 +657,17 @@ WebappsApplication.prototype = {
           case "applied":
             manifestCache.evict(this.manifestURL, this.innerWindowID);
             this._manifest = msg.manifest;
             this._fireEvent("downloadapplied", this._ondownloadapplied);
             break;
         }
         break;
       case "Webapps:ClearBrowserData:Return":
+        this.removeMessageListeners(aMessage.name);
         Services.DOMRequest.fireSuccess(req, null);
         break;
       case "Webapps:Connect:Return:OK":
         let messagePorts = [];
         msg.messagePortIDs.forEach(function(aPortID) {
           let port = new this._window.MozInterAppMessagePort(aPortID);
           messagePorts.push(port);
         }, this);
@@ -686,23 +701,24 @@ WebappsApplication.prototype = {
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application"})
 }
 
 /**
   * mozIDOMApplicationMgmt object
   */
 function WebappsApplicationMgmt(aWindow) {
-  this.initDOMRequestHelper(aWindow, ["Webapps:GetAll:Return:OK",
-                            "Webapps:GetAll:Return:KO",
-                            "Webapps:Uninstall:Return:OK",
-                            "Webapps:Uninstall:Broadcast:Return:OK",
-                            "Webapps:Uninstall:Return:KO",
-                            "Webapps:Install:Return:OK",
-                            "Webapps:GetNotInstalled:Return:OK"]);
+  this.initDOMRequestHelper(aWindow, [
+    { name: "Webapps:GetAll:Return:OK", strongRef: true },
+    { name: "Webapps:GetAll:Return:KO", strongRef: true },
+    { name: "Webapps:Uninstall:Return:OK", strongRef: true },
+    { name: "Webapps:Uninstall:Broadcast:Return:OK", strongRef: true },
+    { name: "Webapps:Uninstall:Return:KO", strongRef: true },
+    { name: "Webapps:Install:Return:OK", strongRef: true },
+    { name: "Webapps:GetNotInstalled:Return:OK", strongRef: true }]);
 
   cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
                         {
                           messages: ["Webapps:Install:Return:OK",
                                      "Webapps:Uninstall:Return:OK",
                                      "Webapps:Uninstall:Broadcast:Return:OK"]
                         }
                        );
--- a/dom/apps/tests/file_app.template.html
+++ b/dom/apps/tests/file_app.template.html
@@ -47,17 +47,18 @@ function go() {
   request.onerror = cbError;
 }
 
 function checkApp(app) {
   // If the app is installed, |app| will be non-null. If it is, verify its state.
   installed(!!app);
   if (app) {
     var appName = "Really Rapid Release (APPTYPETOKEN)";
-    is(app.manifest.name, appName, "Manifest name should be correct");
+    var manifest = SpecialPowers.wrap(app.manifest);
+    is(manifest.name, appName, "Manifest name should be correct");
     is(app.origin, "http://test", "App origin should be correct");
     is(app.installOrigin, "http://mochi.test:8888", "Install origin should be correct");
   }
   finish();
 }
 
 </script>
 </head>
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -1,230 +1,286 @@
 /* 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/. */
 
 /**
- * Helper object for APIs that deal with DOMRequests and Promises and need to
- * release them when the window goes out of scope.
+ * Helper object for APIs that deal with DOMRequests and Promises.
+ * It allows objects inheriting from it to create and keep track of DOMRequests
+ * and Promises objects in the common scenario where requests are created in
+ * the child, handed out to content and delivered to the parent within an async
+ * message (containing the identifiers of these requests). The parent may send
+ * messages back as answers to different requests and the child will use this
+ * helper to get the right request object. This helper also takes care of
+ * releasing the requests objects when the window goes out of scope.
+ *
+ * DOMRequestIPCHelper also deals with message listeners, allowing to add them
+ * to the child side of frame and process message manager and removing them
+ * when needed.
  */
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
+const Cr = Components.results;
 
 this.EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
-/**
- * We use DOMRequestIpcHelperMessageListener to avoid leaking objects which
- * "inherit" from DOMRequestIpcHelper.
- *
- * The issue is that the message manager will hold a strong ref to the message
- * listener we register with it.  But we don't want to hold a strong ref to the
- * DOMRequestIpcHelper object, because that object may be arbitrarily large.
- *
- * So instead the message manager holds a strong ref to the
- * DOMRequestIpcHelperMessageListener, and that holds a /weak/ ref to its
- * DOMRequestIpcHelper.
- *
- * Additionally, we want to unhook all of these message listeners when the
- * appropriate window is destroyed.  We use DOMRequestIpcHelperMessageListener
- * for this, too.
- */
-this.DOMRequestIpcHelperMessageListener = function(aHelper, aWindow, aMessages) {
-  this._weakHelper = Cu.getWeakReference(aHelper);
-
-  this._messages = aMessages;
-  this._messages.forEach(function(msgName) {
-    cpmm.addWeakMessageListener(msgName, this);
-  }, this);
-
-  Services.obs.addObserver(this, "inner-window-destroyed", /* weakRef */ true);
-
-  // aWindow may be null; in that case, the DOMRequestIpcHelperMessageListener
-  // is not tied to a particular window and lives forever.
-  if (aWindow) {
-    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-    this._innerWindowID = util.currentInnerWindowID;
-  }
+this.DOMRequestIpcHelper = function DOMRequestIpcHelper() {
+  // _listeners keeps a list of messages for which we added a listener and the
+  // kind of listener that we added (strong or weak). It's an object of this
+  // form:
+  //  {
+  //    "message1": true,
+  //    "messagen": false
+  //  }
+  //
+  // where each property is the name of the message and its value is a boolean
+  // that indicates if the listener is strong or not.
+  this._listeners = null;
+  this._requests = null;
+  this._window = null;
 }
 
-DOMRequestIpcHelperMessageListener.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
-                                         Ci.nsIObserver,
-                                         Ci.nsISupportsWeakReference]),
+DOMRequestIpcHelper.prototype = {
+  /**
+   * An object which "inherits" from DOMRequestIpcHelper, declares its own
+   * queryInterface method and adds at least one weak listener to the Message
+   * Manager MUST implement Ci.nsISupportsWeakReference.
+   */
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+
+   /**
+   *  'aMessages' is expected to be an array of either:
+   *  - objects of this form:
+   *    {
+   *      name: "messageName",
+   *      strongRef: false
+   *    }
+   *    where 'name' is the message identifier and 'strongRef' a boolean
+   *    indicating if the listener should be a strong referred one or not.
+   *
+   *  - or only strings containing the message name, in which case the listener
+   *    will be added as a weak reference by default.
+   */
+  addMessageListeners: function(aMessages) {
+    if (!aMessages) {
+      return;
+    }
+
+    if (!this._listeners) {
+      this._listeners = {};
+    }
+
+    if (!Array.isArray(aMessages)) {
+      aMessages = [aMessages];
+    }
+
+    aMessages.forEach((aMsg) => {
+      let name = aMsg.name || aMsg;
+      // If the listener is already set and it is of the same type we just
+      // bail out. If it is not of the same type, we throw an exception.
+      if (this._listeners[name] != undefined) {
+        if (!!aMsg.strongRef == this._listeners[name]) {
+          return;
+        } else {
+          throw Cr.NS_ERROR_FAILURE;
+        }
+      }
+
+      aMsg.strongRef ? cpmm.addMessageListener(name, this)
+                     : cpmm.addWeakMessageListener(name, this);
+      this._listeners[name] = !!aMsg.strongRef;
+    });
+  },
+
+  /**
+   * 'aMessages' is expected to be a string or an array of strings containing
+   * the message names of the listeners to be removed.
+   */
+  removeMessageListeners: function(aMessages) {
+    if (!this._listeners || !aMessages) {
+      return;
+    }
+
+    if (!Array.isArray(aMessages)) {
+      aMessages = [aMessages];
+    }
+
+    aMessages.forEach((aName) => {
+      if (this._listeners[aName] == undefined) {
+        return;
+      }
+
+      this._listeners[aName] ? cpmm.removeMessageListener(aName, this)
+                             : cpmm.removeWeakMessageListener(aName, this);
+      delete this._listeners[aName];
+    });
+  },
+
+  /**
+   * Initialize the helper adding the corresponding listeners to the messages
+   * provided as the second parameter.
+   *
+   * 'aMessages' is expected to be an array of either:
+   *
+   *  - objects of this form:
+   *    {
+   *      name: 'messageName',
+   *      strongRef: false
+   *    }
+   *    where 'name' is the message identifier and 'strongRef' a boolean
+   *    indicating if the listener should be a strong referred one or not.
+   *
+   *  - or only strings containing the message name, in which case the listener
+   *    will be added as a weak referred one by default.
+   */
+  initDOMRequestHelper: function(aWindow, aMessages) {
+    if (aMessages) {
+      this.addMessageListeners(aMessages);
+    }
+
+    this._id = this._getRandomId();
+
+    this._window = aWindow;
+    if (this._window) {
+      // We don't use this.innerWindowID, but other classes rely on it.
+      let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIDOMWindowUtils);
+      this.innerWindowID = util.currentInnerWindowID;
+    }
+
+    Services.obs.addObserver(this, "inner-window-destroyed", false);
+  },
+
+  destroyDOMRequestHelper: function() {
+    Services.obs.removeObserver(this, "inner-window-destroyed");
+
+    if (this._listeners) {
+      Object.keys(this._listeners).forEach((aName) => {
+        this._listeners[aName] ? cpmm.removeMessageListener(aName, this)
+                               : cpmm.removeWeakMessageListener(aName, this);
+        delete this._listeners[aName];
+      });
+    }
+
+    this._listeners = null;
+    this._requests = null;
+    this._window = null;
+  },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic !== "inner-window-destroyed") {
       return;
     }
 
     let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    if (wId != this._innerWindowID) {
+    if (wId != this.innerWindowID) {
       return;
     }
 
-    this.destroy();
-  },
-
-  receiveMessage: function(aMsg) {
-    let helper = this._weakHelper.get();
-    if (helper) {
-      helper.receiveMessage(aMsg);
-    } else {
-      this.destroy();
-    }
-  },
-
-  destroy: function() {
-    // DOMRequestIpcHelper.destroy() calls back into this function.
-    if (this._destroyed) {
-      return;
-    }
-    this._destroyed = true;
-
-    Services.obs.removeObserver(this, "inner-window-destroyed");
-
-    this._messages.forEach(function(msgName) {
-      cpmm.removeWeakMessageListener(msgName, this);
-    }, this);
-    this._messages = null;
-
-    let helper = this._weakHelper.get();
-    if (helper) {
-      helper.destroyDOMRequestHelper();
-    }
-  }
-}
-
-this.DOMRequestIpcHelper = function DOMRequestIpcHelper() {
-}
-
-DOMRequestIpcHelper.prototype = {
-  /**
-   * An object which "inherits" from DOMRequestIpcHelper and declares its own
-   * queryInterface method MUST implement Ci.nsISupportsWeakReference.
-   */
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
-
-  initDOMRequestHelper: function(aWindow, aMessages) {
-    this._DOMRequestIpcHelperMessageListener =
-      new DOMRequestIpcHelperMessageListener(this, aWindow, aMessages);
-
-    this._window = aWindow;
-    this._requests = {};
-    this._id = this._getRandomId();
-
-    if (this._window) {
-      // We don't use this.innerWindowID, but other classes rely on it.
-      let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIDOMWindowUtils);
-      this.innerWindowID = util.currentInnerWindowID;
-    }
+    this.destroyDOMRequestHelper();
   },
 
   getRequestId: function(aRequest) {
+    if (!this._requests) {
+      this._requests = {};
+    }
+
     let id = "id" + this._getRandomId();
     this._requests[id] = aRequest;
     return id;
   },
 
   getPromiseResolverId: function(aPromiseResolver) {
     // Delegates to getRequest() since the lookup table is agnostic about
     // storage.
     return this.getRequestId(aPromiseResolver);
   },
 
   getRequest: function(aId) {
-    if (this._requests[aId])
+    if (this._requests && this._requests[aId]) {
       return this._requests[aId];
+    }
   },
 
   getPromiseResolver: function(aId) {
     // Delegates to getRequest() since the lookup table is agnostic about
     // storage.
     return this.getRequest(aId);
   },
 
   removeRequest: function(aId) {
-    if (this._requests[aId])
+    if (this._requests && this._requests[aId]) {
       delete this._requests[aId];
+    }
   },
 
   removePromiseResolver: function(aId) {
     // Delegates to getRequest() since the lookup table is agnostic about
     // storage.
     this.removeRequest(aId);
   },
 
   takeRequest: function(aId) {
-    if (!this._requests[aId])
+    if (!this._requests || !this._requests[aId]) {
       return null;
+    }
     let request = this._requests[aId];
     delete this._requests[aId];
     return request;
   },
 
   takePromiseResolver: function(aId) {
     // Delegates to getRequest() since the lookup table is agnostic about
     // storage.
     return this.takeRequest(aId);
   },
 
   _getRandomId: function() {
-    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
-  },
-
-  destroyDOMRequestHelper: function() {
-    // This function is re-entrant --
-    // DOMRequestIpcHelperMessageListener.destroy() calls back into this
-    // function, and this.uninit() may also call it.
-    if (this._destroyed) {
-      return;
-    }
-    this._destroyed = true;
-
-    this._DOMRequestIpcHelperMessageListener.destroy();
-    this._requests = {};
-    this._window = null;
-
-    if(this.uninit) {
-      this.uninit();
-    }
+    return Cc["@mozilla.org/uuid-generator;1"]
+             .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
   },
 
   createRequest: function() {
     return Services.DOMRequest.createRequest(this._window);
   },
 
   /**
    * createPromise() creates a new Promise, with `aPromiseInit` as the
    * PromiseInit callback. The promise constructor is obtained from the
    * reference to window owned by this DOMRequestIPCHelper.
    */
   createPromise: function(aPromiseInit) {
     return new this._window.Promise(aPromiseInit);
   },
 
   forEachRequest: function(aCallback) {
-    Object.keys(this._requests).forEach(function(k) {
-      if (this.getRequest(k) instanceof this._window.DOMRequest) {
-        aCallback(k);
+    if (!this._requests) {
+      return;
+    }
+
+    Object.keys(this._requests).forEach((aKey) => {
+      if (this.getRequest(aKey) instanceof this._window.DOMRequest) {
+        aCallback(aKey);
       }
-    }, this);
+    });
   },
 
   forEachPromiseResolver: function(aCallback) {
-    Object.keys(this._requests).forEach(function(k) {
-      if ("resolve" in this.getPromiseResolver(k) &&
-          "reject" in this.getPromiseResolver(k)) {
-        aCallback(k);
+    if (!this._requests) {
+      return;
+    }
+
+    Object.keys(this._requests).forEach((aKey) => {
+      if ("resolve" in this.getPromiseResolver(aKey) &&
+          "reject" in this.getPromiseResolver(aKey)) {
+        aCallback(aKey);
       }
-    }, this);
+    });
   },
 }
--- a/dom/base/test/test_domrequesthelper.xul
+++ b/dom/base/test/test_domrequesthelper.xul
@@ -12,113 +12,349 @@
   <title>DOMRequestHelper Test</title>
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <script type="application/javascript">
   <![CDATA[
     Components.utils.import("resource://gre/modules/DOMRequestHelper.jsm");
+
     function DummyHelperSubclass() {
-      this.initDOMRequestHelper(window, []);
     }
-
     DummyHelperSubclass.prototype = {
       __proto__: DOMRequestIpcHelper.prototype
     };
 
     var dummy = new DummyHelperSubclass();
 
-    function createPromise() {
-      ok(Promise, "Promise object should exist");
+    /**
+     * Init & destroy.
+     */
+    function initDOMRequestHelperTest(aMessages) {
+      is(dummy._requests, undefined, "Request is undefined");
+      is(dummy._messages, undefined, "Messages is undefined");
+      is(dummy._window, undefined, "Window is undefined");
+
+      dummy.initDOMRequestHelper(window, aMessages);
 
-      var promise = dummy.createPromise(function(resolve, reject) {
-        resolve(true);
-      });
-      ok(promise instanceof Promise, "returned a Promise");
-      promise.then(runTest);
+      ok(dummy._window, "Window exists");
+      is(dummy._window, window, "Correct window");
+      if (aMessages) {
+        is(typeof dummy._listeners, "object", "Listeners is an object");
+      }
+    }
+
+    function destroyDOMRequestHelperTest() {
+      dummy.destroyDOMRequestHelper();
+
+      is(dummy._requests, undefined, "Request is undefined");
+      is(dummy._messages, undefined, "Messages is undefined");
+      is(dummy._window, undefined, "Window is undefined");
     }
 
-    function getResolver() {
-      var id;
-      var resolver;
-      var promise = dummy.createPromise(function(resolve, reject) {
-        var r = { resolve: resolve, reject: reject };
-        id = dummy.getPromiseResolverId(r);
-        resolver = r;
-        ok(typeof id === "string", "id should be string");
-        r.resolve(true);
-      }).then(function(unused) {
-        var r = dummy.getPromiseResolver(id);
-        ok(resolver === r, "get should succeed");
-        runTest();
+    /**
+     * Message listeners.
+     */
+    function checkMessageListeners(aExpectedListeners, aCount) {
+      ok(true, "Checking message listeners\n" + "Expected listeners " +
+         JSON.stringify(aExpectedListeners) + " \nExpected count " + aCount);
+      let count = 0;
+      Object.keys(dummy._listeners).forEach(function(name) {
+        count++;
+        is(aExpectedListeners[name], dummy._listeners[name],
+           "Message found " + name + " - Same listeners");
       });
+      is(aCount, count, "Correct number of listeners");
+    }
+
+    function addMessageListenersTest(aMessages, aExpectedListeners, aCount) {
+      dummy.addMessageListeners(aMessages);
+      ok(true, JSON.stringify(dummy._listeners));
+      checkMessageListeners(aExpectedListeners, aCount);
+    }
+
+    function removeMessageListenersTest(aMessages, aExpectedListeners, aCount) {
+      dummy.removeMessageListeners(aMessages);
+      checkMessageListeners(aExpectedListeners, aCount);
     }
 
-    function removeResolver() {
-      var id;
-      var promise = dummy.createPromise(function(resolve, reject) {
-        var r = { resolve: resolve, reject: reject };
-        id = dummy.getPromiseResolverId(r);
-        ok(typeof id === "string", "id should be string");
-
-        var resolver = dummy.getPromiseResolver(id);
-        ok(resolver === r, "resolver get should succeed");
-
-        r.resolve(true);
-      }).then(function(unused) {
-        dummy.removePromiseResolver(id);
-        var resolver = dummy.getPromiseResolver(id);
-        ok(resolver === undefined, "removeResolver: get should fail");
-        runTest();
-      });
-    }
+    /**
+     * Test steps.
+     */
+    var tests = [
+      function() {
+        ok(true, "== InitDOMRequestHelper no messages");
+        initDOMRequestHelperTest(null);
+        next();
+      },
+      function() {
+        ok(true, "== DestroyDOMRequestHelper");
+        destroyDOMRequestHelperTest();
+        next();
+      },
+      function() {
+        ok(true, "== InitDOMRequestHelper empty array");
+        initDOMRequestHelperTest([]);
+        checkMessageListeners({}, 0);
+        next();
+      },
+      function() {
+        ok(true, "== DestroyDOMRequestHelper");
+        destroyDOMRequestHelperTest();
+        next();
+      },
+      function() {
+        ok(true, "== InitDOMRequestHelper with strings array");
+        initDOMRequestHelperTest(["name1", "nameN"]);
+        checkMessageListeners({"name1": false, "nameN": false}, 2);
+        next();
+      },
+      function() {
+        ok(true, "== DestroyDOMRequestHelper");
+        destroyDOMRequestHelperTest();
+        next();
+      },
+      function() {
+        ok(true, "== InitDOMRequestHelper with objects array");
+        initDOMRequestHelperTest([{
+          name: "name1",
+          strongRef: false
+        }, {
+          name: "nameN",
+          strongRef: true
+        }]);
+        checkMessageListeners({"name1": false, "nameN": true}, 2);
+        next();
+      },
+      function() {
+        ok(true, "== AddMessageListeners empty array");
+        addMessageListenersTest([], {"name1": false, "nameN": true}, 2);
+        next();
+      },
+      function() {
+        ok(true, "== AddMessageListeners null");
+        addMessageListenersTest(null, {"name1": false, "nameN": true}, 2);
+        next();
+      },
+      function() {
+        ok(true, "== AddMessageListeners new listener, string only");
+        addMessageListenersTest("name2", {
+          "name1": false,
+          "name2": false,
+          "nameN": true
+        }, 3);
+        next();
+      },
+      function() {
+        ok(true, "== AddMessageListeners new listeners, strings array");
+        addMessageListenersTest(["name3", "name4"], {
+          "name1": false,
+          "name2": false,
+          "name3": false,
+          "name4": false,
+          "nameN": true
+        }, 5);
+        next();
+      },
+      function() {
+        ok(true, "== AddMessageListeners new listeners, objects array");
+        addMessageListenersTest([{
+          name: "name5",
+          strongRef: true
+        }, {
+          name: "name6",
+          strongRef: false
+        }], {
+          "name1": false,
+          "name2": false,
+          "name3": false,
+          "name4": false,
+          "name5": true,
+          "name6": false,
+          "nameN": true
+        }, 7);
+        next();
+      },
+      function() {
+        ok(true, "== RemoveMessageListeners, null");
+        removeMessageListenersTest(null, {
+          "name1": false,
+          "name2": false,
+          "name3": false,
+          "name4": false,
+          "name5": true,
+          "name6": false,
+          "nameN": true
+        }, 7);
+        next();
+      },
+      function() {
+        ok(true, "== RemoveMessageListeners, one message");
+        removeMessageListenersTest("name1", {
+          "name2": false,
+          "name3": false,
+          "name4": false,
+          "name5": true,
+          "name6": false,
+          "nameN": true
+        }, 6);
+        next();
+      },
+      function() {
+        ok(true, "== RemoveMessageListeners, array of messages");
+        removeMessageListenersTest(["name2", "name3"], {
+          "name4": false,
+          "name5": true,
+          "name6": false,
+          "nameN": true
+        }, 4);
+        next();
+      },
+      function() {
+        ok(true, "== RemoveMessageListeners, unknown message");
+        removeMessageListenersTest("unknown", {
+          "name4": false,
+          "name5": true,
+          "name6": false,
+          "nameN": true
+        }, 4);
+        next();
+      },
+      function() {
+        try {
+          ok(true, "== AddMessageListeners, same message, same kind");
+          addMessageListenersTest("name4", {
+            "name4": false,
+            "name5": true,
+            "name6": false,
+            "nameN": true
+          }, 4);
+          next();
+        } catch (ex) {
+          ok(false, "Unexpected exception " + ex);
+        }
+      },
+      function() {
+        ok(true, "== AddMessageListeners, same message, different kind");
+        try {
+          addMessageListenersTest({name: "name4", strongRef: true}, {
+            "name4": true,
+            "name5": true,
+            "name6": false,
+            "nameN": true
+          }, 4);
+          ok(false, "Should have thrown an exception");
+        } catch (ex) {
+          ok(true, "Expected exception");
+          next();
+        }
+      },
+      function() {
+        ok(true, "== Test createRequest()");
+        ok(DOMRequest, "DOMRequest object exists");
+        var req = dummy.createRequest();
+        ok(req instanceof DOMRequest, "Returned a DOMRequest");
+        next();
+      },
+      function() {
+        ok(true, "== Test getRequestId(), removeRequest() and getRequest()");
+        var req = dummy.createRequest();
+        var id = dummy.getRequestId(req);
+        is(typeof id, "string", "id is a string");
+        var req_ = dummy.getRequest(id);
+        is(req, req_, "Got correct request");
+        dummy.removeRequest(id);
+        req = dummy.getRequest(id);
+        is(req, null, "No request");
+        next();
+      },
+      function() {
+        ok(true, "== Test createPromise()");
+        ok(Promise, "Promise object exists");
+        var promise = dummy.createPromise(function(resolve, reject) {
+          resolve(true);
+        });
+        ok(promise instanceof Promise, "Returned a Promise");
+        promise.then(next);
+      },
+      function() {
+        ok(true, "== Test getResolver()");
+        var id;
+        var resolver;
+        var promise = dummy.createPromise(function(resolve, reject) {
+          var r = { resolve: resolve, reject: reject };
+          id = dummy.getPromiseResolverId(r);
+          resolver = r;
+          ok(typeof id === "string", "id is a string");
+          r.resolve(true);
+        }).then(function(unused) {
+          var r = dummy.getPromiseResolver(id);
+          ok(resolver === r, "Get succeeded");
+          next();
+        });
+      },
+      function() {
+        ok(true, "== Test removeResolver");
+        var id;
+        var promise = dummy.createPromise(function(resolve, reject) {
+          var r = { resolve: resolve, reject: reject };
+          id = dummy.getPromiseResolverId(r);
+          ok(typeof id === "string", "id is a string");
 
-    function takeResolver() {
-      var id;
-      var resolver;
-      var promise = dummy.createPromise(function(resolve, reject) {
-        var r = { resolve: resolve, reject: reject };
-        id = dummy.getPromiseResolverId(r);
-        resolver = r;
-        ok(typeof id === "string", "id should be string");
-
-        var gotR = dummy.getPromiseResolver(id);
-        ok(gotR === r, "resolver get should succeed");
+          var resolver = dummy.getPromiseResolver(id);
+          ok(true, "Got resolver " + JSON.stringify(resolver));
+          ok(resolver === r, "Resolver get succeeded");
 
-        r.resolve(true);
-      }).then(function(unused) {
-        var r = dummy.takePromiseResolver(id);
-        ok(resolver === r, "take should succeed");
+          r.resolve(true);
+        }).then(function(unused) {
+          dummy.removePromiseResolver(id);
+          var resolver = dummy.getPromiseResolver(id);
+          ok(resolver === undefined, "removeResolver: get failed");
+          next();
+        });
+      },
+      function() {
+        ok(true, "== Test takeResolver");
+        var id;
+        var resolver;
+        var promise = dummy.createPromise(function(resolve, reject) {
+          var r = { resolve: resolve, reject: reject };
+          id = dummy.getPromiseResolverId(r);
+          resolver = r;
+          ok(typeof id === "string", "id is a string");
 
-        r = dummy.getPromiseResolver(id);
-        ok(r === undefined, "takeResolver: get should fail");
-        runTest();
-      });
-    }
+          var gotR = dummy.getPromiseResolver(id);
+          ok(gotR === r, "resolver get succeeded");
+
+          r.resolve(true);
+        }).then(function(unused) {
+          var r = dummy.takePromiseResolver(id);
+          ok(resolver === r, "take should succeed");
 
-    var tests = [ createPromise,
-                  getResolver,
-                  removeResolver,
-                  takeResolver,
-                ];
+          r = dummy.getPromiseResolver(id);
+          ok(r === undefined, "takeResolver: get failed");
+          next();
+        });
+      }
+    ];
 
-    function runTest() {
+    function next() {
       if (!tests.length) {
         SimpleTest.finish();
         return;
       }
 
       var test = tests.shift();
       test();
     }
 
     function start() {
       SimpleTest.waitForExplicitFinish();
-      runTest();
+      next();
     }
   ]]>
   </script>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
     <p id="display"></p>
     <div id="content" style="display: none"></div>
     <pre id="test"></pre>
--- a/dom/imptests/failures/editing/conformancetest/test_runtest.html.json
+++ b/dom/imptests/failures/editing/conformancetest/test_runtest.html.json
@@ -2881,17 +2881,16 @@
   "[[\"defaultparagraphseparator\",\"div\"],[\"forwarddelete\",\"\"]] \"<div>foo[]</div><div>bar</div>\" queryCommandValue(\"defaultparagraphseparator\") after":true,
   "[[\"defaultparagraphseparator\",\"p\"],[\"forwarddelete\",\"\"]] \"<div>foo[]</div><div>bar</div>\": execCommand(\"defaultparagraphseparator\", false, \"p\") return value":true,
   "[[\"defaultparagraphseparator\",\"p\"],[\"forwarddelete\",\"\"]] \"<div>foo[]</div><div>bar</div>\" queryCommandValue(\"defaultparagraphseparator\") before":true,
   "[[\"defaultparagraphseparator\",\"p\"],[\"forwarddelete\",\"\"]] \"<div>foo[]</div><div>bar</div>\" queryCommandValue(\"defaultparagraphseparator\") after":true,
   "[[\"forwarddelete\",\"\"]] \"<p>foo[]<hr><p>bar\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<p>foo[]</p><br><p>bar</p>\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<p>foo[]</p><br><br><p>bar</p>\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<p>foo[]</p><img src=/img/lion.svg><p>bar\" compare innerHTML":true,
-  "[[\"forwarddelete\",\"\"]] \"foo[]<img src=/img/lion.svg>bar\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"foo []&nbsp;\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"foo []<span>&nbsp;</span> bar\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"foo <span>&nbsp;</span>[] bar\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<b>foo[] </b> bar\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<pre>foo[] &nbsp;bar</pre>\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<div style=white-space:pre>foo[] &nbsp;bar</div>\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<div style=white-space:pre-wrap>foo[] &nbsp;bar</div>\" compare innerHTML":true,
   "[[\"forwarddelete\",\"\"]] \"<div style=white-space:pre-line>foo []&nbsp;</div>\" compare innerHTML":true,
--- a/dom/inputmethod/MozKeyboard.js
+++ b/dom/inputmethod/MozKeyboard.js
@@ -470,17 +470,18 @@ MozInputContext.prototype = {
   _window: null,
   _context: null,
   _contextId: -1,
 
   classID: Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIB2GInputContext,
-    Ci.nsIObserver
+    Ci.nsIObserver,
+    Ci.nsISupportsWeakReference
   ]),
 
   classInfo: XPCOMUtils.generateCI({
     "classID": Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"),
     "contractID": "@mozilla.org/b2g-inputcontext;1",
     "interfaces": [Ci.nsIB2GInputContext],
     "flags": Ci.nsIClassInfo.DOM_OBJECT,
     "classDescription": "B2G Input Context"
--- a/dom/phonenumberutils/PhoneNumberService.js
+++ b/dom/phonenumberutils/PhoneNumberService.js
@@ -86,12 +86,13 @@ PhoneNumberService.prototype = {
     this.initDOMRequestHelper(aWindow, [
       "PhoneNumberService:FuzzyMatch:Return:OK",
       "PhoneNumberService:FuzzyMatch:Return:KO"
     ]);
   },
 
   classID : Components.ID("{e2768710-eb17-11e2-91e2-0800200c9a66}"),
   contractID : "@mozilla.org/phoneNumberService;1",
-  QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
+  QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer,
+                                          Ci.nsISupportsWeakReference]),
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PhoneNumberService]);
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -539,16 +539,17 @@ var interfaceNamesInGlobalScope =
     "TextDecoder",
     "TextEncoder",
     "TextMetrics",
     "TimeEvent",
     "TimeRanges",
     "Touch",
     "TouchEvent",
     "TouchList",
+    "TrackEvent",
     "TransitionEvent",
     {name: "TreeColumn", xbl: true},
     {name: "TreeColumns", xbl: true},
     {name: "TreeContentView", xbl: true},
     {name: "TreeSelection", xbl: true},
     "TreeWalker",
     "UIEvent",
     "UndoManager",
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TrackEvent.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+
+[Constructor(DOMString type, optional TrackEventInit eventInitDict)]
+interface TrackEvent : Event
+{
+  readonly attribute TextTrack? track;
+};
+
+dictionary TrackEventInit : EventInit
+{
+  TextTrack? track = null;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -543,16 +543,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'DataStoreChangeEvent.webidl',
     'DeviceLightEvent.webidl',
     'DeviceProximityEvent.webidl',
     'ErrorEvent.webidl',
     'MediaStreamEvent.webidl',
     'MozInterAppMessageEvent.webidl',
     'RTCDataChannelEvent.webidl',
     'RTCPeerConnectionIceEvent.webidl',
+    'TrackEvent.webidl',
     'UserProximityEvent.webidl',
 ]
 
 if CONFIG['MOZ_GAMEPAD']:
     GENERATED_EVENTS_WEBIDL_FILES += [
         'GamepadAxisMoveEvent.webidl',
         'GamepadButtonEvent.webidl',
         'GamepadEvent.webidl',
--- a/image/test/mochitest/test_bug733553.html
+++ b/image/test/mochitest/test_bug733553.html
@@ -46,16 +46,17 @@ var testParts = [
 
 // We'll append the part number to this, and tell the informant
 const BASE_URL = "bug733553-informant.sjs?";
 
 function initializeOnload() {
   var firstimg = document.createElement('img');
   firstimg.addEventListener("load", imageLoad, false);
   firstimg.addEventListener("error", imageLoad, false);
+  firstimg.alt = "";
   firstimg.src = "bug733553.sjs";
   document.getElementById('content').appendChild(firstimg);
 
   // Really ready for first, but who's counting
   readyForNext();
 }
 
 function readyForNext() {
--- a/image/test/reftest/bmp/bmp-corrupted/wrapper.html
+++ b/image/test/reftest/bmp/bmp-corrupted/wrapper.html
@@ -15,13 +15,14 @@
 </head>
 <body>
 <img id="image1">
 <script>
   // Use as "wrapper.html?image.png"
   var imgURL = document.location.search.substr(1);
   document.images[0].onload = onImageLoad;
   document.images[0].onerror = onImageLoad;
+  document.images[0].alt = "";
   document.images[0].src = imgURL;
 </script>
 </body>
 </html>
 
--- a/image/test/reftest/ico/ico-bmp-corrupted/wrapper.html
+++ b/image/test/reftest/ico/ico-bmp-corrupted/wrapper.html
@@ -15,13 +15,14 @@
 </head>
 <body>
 <img id="image1">
 <script>
   // Use as "wrapper.html?image.png
   var imgURL = document.location.search.substr(1);
   document.images[0].onload = onImageLoad;
   document.images[0].onerror = onImageLoad;
+  document.images[0].alt = "";
   document.images[0].src = imgURL;
 </script>
 </body>
 </html>
 
--- a/image/test/reftest/ico/ico-png/wrapper.html
+++ b/image/test/reftest/ico/ico-png/wrapper.html
@@ -15,13 +15,14 @@
 </head>
 <body>
 <img id="image1">
 <script>
   // Use as "wrapper.html?image.png
   var imgURL = document.location.search.substr(1);
   document.images[0].onload = onImageLoad;
   document.images[0].onerror = onImageLoad;
+  document.images[0].alt = "";
   document.images[0].src = imgURL;
 </script>
 </body>
 </html>
 
--- a/image/test/reftest/pngsuite-corrupted/wrapper.html
+++ b/image/test/reftest/pngsuite-corrupted/wrapper.html
@@ -15,13 +15,14 @@
 </head>
 <body>
 <img id="image1">
 <script>
   // Use as "wrapper.html?image.png
   var imgURL = document.location.search.substr(1);
   document.images[0].onload = onImageLoad;
   document.images[0].onerror = onImageLoad;
+  document.images[0].alt = "";
   document.images[0].src = imgURL;
 </script>
 </body>
 </html>
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -834,17 +834,17 @@ static bool
 EmitGetterCall(JSContext *cx, MacroAssembler &masm,
                IonCache::StubAttacher &attacher, JSObject *obj,
                JSObject *holder, HandleShape shape,
                RegisterSet liveRegs, Register object,
                Register scratchReg, TypedOrValueRegister output,
                void *returnAddr)
 {
     // saveLive()
-    masm.PushRegsInMask(liveRegs);
+    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
 
     // Remaining registers should basically be free, but we need to use |object| still
     // so leave it alone.
     RegisterSet regSet(RegisterSet::All());
     regSet.take(AnyRegister(object));
 
     // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
     // to try so hard to not use the stack.  Scratch regs are just taken from the register
@@ -885,17 +885,17 @@ EmitGetterCall(JSContext *cx, MacroAssem
         masm.loadJSContext(argJSContextReg);
         masm.move32(Imm32(0), argUintNReg);
         masm.movePtr(StackPointer, argVpReg);
 
         // Push marking data for later use.
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
-        if (!masm.buildOOLFakeExitFrame(returnAddr))
+        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE);
 
         // Construct and execute call.
         masm.setupUnalignedABICall(3, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
@@ -931,17 +931,17 @@ EmitGetterCall(JSContext *cx, MacroAssem
         masm.Push(propId, scratchReg);
         masm.movePtr(StackPointer, argIdReg);
 
         masm.Push(object);
         masm.movePtr(StackPointer, argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
-        if (!masm.buildOOLFakeExitFrame(returnAddr))
+        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
 
         // Make the call.
         masm.setupUnalignedABICall(4, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
@@ -1287,17 +1287,17 @@ GetPropertyIC::tryAttachTypedArrayLength
 
 static bool
 EmitCallProxyGet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                  PropertyName *name, RegisterSet liveRegs, Register object,
                  TypedOrValueRegister output, void *returnAddr)
 {
     JS_ASSERT(output.hasValue());
     // saveLive()
-    masm.PushRegsInMask(liveRegs);
+    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
 
     // Remaining registers should be free, but we need to use |object| still
     // so leave it alone.
     RegisterSet regSet(RegisterSet::All());
     regSet.take(AnyRegister(object));
 
     // Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
     //            MutableHandleValue vp)
@@ -1324,17 +1324,17 @@ EmitCallProxyGet(JSContext *cx, MacroAss
     // Pushing object and receiver.  Both are the same, so Handle to one is equivalent to
     // handle to other.
     masm.Push(object);
     masm.Push(object);
     masm.movePtr(StackPointer, argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
 
-    if (!masm.buildOOLFakeExitFrame(returnAddr))
+    if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
     masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY);
 
     // Make the call.
     masm.setupUnalignedABICall(5, scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argProxyReg);
@@ -2055,17 +2055,17 @@ IsCacheableSetPropCallPropertyOp(HandleO
 }
 
 static bool
 EmitCallProxySet(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                  HandleId propId, RegisterSet liveRegs, Register object,
                  ConstantOrRegister value, void *returnAddr, bool strict)
 {
     // saveLive()
-    masm.PushRegsInMask(liveRegs);
+    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
 
     // Remaining registers should be free, but we need to use |object| still
     // so leave it alone.
     RegisterSet regSet(RegisterSet::All());
     regSet.take(AnyRegister(object));
 
     // Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
     //            bool strict, MutableHandleValue vp)
@@ -2093,17 +2093,17 @@ EmitCallProxySet(JSContext *cx, MacroAss
     // handle to other.
     masm.Push(object);
     masm.Push(object);
     masm.movePtr(StackPointer, argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
     masm.move32(Imm32(strict? 1 : 0), argStrictReg);
 
-    if (!masm.buildOOLFakeExitFrame(returnAddr))
+    if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
     masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY);
 
     // Make the call.
     masm.setupUnalignedABICall(6, scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argProxyReg);
@@ -2261,17 +2261,17 @@ GenerateCallSetter(JSContext *cx, IonScr
 
         masm.bind(&protoSuccess);
         masm.pop(scratchReg);
     }
 
     // Good to go for invoking setter.
 
     // saveLive()
-    masm.PushRegsInMask(liveRegs);
+    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
 
     // Remaining registers should basically be free, but we need to use |object| still
     // so leave it alone.
     RegisterSet regSet(RegisterSet::All());
     regSet.take(AnyRegister(object));
 
     // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
     // to try so hard to not use the stack.  Scratch regs are just taken from the register
@@ -2314,17 +2314,17 @@ GenerateCallSetter(JSContext *cx, IonScr
         // Preload other regs
         masm.loadJSContext(argJSContextReg);
         masm.move32(Imm32(1), argUintNReg);
 
         // Push data for GC marking
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
-        if (!masm.buildOOLFakeExitFrame(returnAddr))
+        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE);
 
         // Make the call
         masm.setupUnalignedABICall(3, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
@@ -2357,17 +2357,17 @@ GenerateCallSetter(JSContext *cx, IonScr
         masm.Push(propId, argIdReg);
         masm.movePtr(StackPointer, argIdReg);
 
         masm.Push(object);
         masm.movePtr(StackPointer, argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
-        if (!masm.buildOOLFakeExitFrame(returnAddr))
+        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
         masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
 
         // Make the call.
         masm.setupUnalignedABICall(5, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -232,16 +232,31 @@ class MacroAssembler : public MacroAssem
         sps_(nullptr)
     {
 #ifdef JS_CPU_ARM
         initWithAllocator();
         m_buffer.id = 0;
 #endif
     }
 
+    MacroAssembler(JSContext *cx, IonScript *ion)
+      : enoughMemory_(true),
+        embedsNurseryPointers_(false),
+        sps_(NULL)
+    {
+        constructRoot(cx);
+         ionContext_.construct(cx, (js::jit::TempAllocator *)NULL);
+         alloc_.construct(cx);
+#ifdef JS_CPU_ARM
+         initWithAllocator();
+         m_buffer.id = GetIonContext()->getNextAssemblerId();
+#endif
+        setFramePushed(ion->frameSize());
+    }
+
     void setInstrumentation(IonInstrumentation *sps) {
         sps_ = sps;
     }
 
     void resetForNewCodeGenerator() {
         setFramePushed(0);
         moveResolver_.clearTempObjectPool();
     }
@@ -1309,16 +1324,36 @@ class MacroAssembler : public MacroAssem
         return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
                                               IntConversion_ClampToUint8);
     }
     void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
                                   Label *fail)
     {
         convertTypedOrValueToInt(src, temp, output, fail, IntConversion_ClampToUint8);
     }
+
+  public:
+    class AfterICSaveLive {
+        friend class MacroAssembler;
+        AfterICSaveLive()
+        {}
+    };
+
+    AfterICSaveLive icSaveLive(RegisterSet &liveRegs) {
+        PushRegsInMask(liveRegs);
+        return AfterICSaveLive();
+    }
+
+    bool icBuildOOLFakeExitFrame(void *fakeReturnAddr, AfterICSaveLive &aic) {
+        return buildOOLFakeExitFrame(fakeReturnAddr);
+    }
+
+    void icRestoreLive(RegisterSet &liveRegs, AfterICSaveLive &aic) {
+        PopRegsInMask(liveRegs);
+    }
 };
 
 static inline Assembler::DoubleCondition
 JSOpToDoubleCondition(JSOp op)
 {
     switch (op) {
       case JSOP_EQ:
       case JSOP_STRICTEQ:
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1186,17 +1186,16 @@ class MacroAssemblerARMCompat : public M
     }
     void setFramePushed(uint32_t framePushed) {
         framePushed_ = framePushed;
     }
 
     // Builds an exit frame on the stack, with a return address to an internal
     // non-function. Returns offset to be passed to markSafepointAt().
     bool buildFakeExitFrame(const Register &scratch, uint32_t *offset);
-    bool buildOOLFakeExitFrame(void *fakeReturnAddr);
 
     void callWithExitFrame(IonCode *target);
     void callWithExitFrame(IonCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // indep code to make a call
     void callIon(const Register &callee);
 
@@ -1394,16 +1393,19 @@ class MacroAssemblerARMCompat : public M
     // automatically adjusted. It is extremely important that esp-relative
     // addresses are computed *after* setupABICall(). Furthermore, no
     // operations should be emitted while setting arguments.
     void passABIArg(const MoveOperand &from);
     void passABIArg(const Register &reg);
     void passABIArg(const FloatRegister &reg);
     void passABIArg(const ValueOperand &regs);
 
+  protected:
+    bool buildOOLFakeExitFrame(void *fakeReturnAddr);
+
   private:
     void callWithABIPre(uint32_t *stackAdjust);
     void callWithABIPost(uint32_t stackAdjust, Result result);
 
   public:
     // Emits a call to a C/C++ function, resolving all argument moves.
     void callWithABI(void *fun, Result result = GENERAL);
     void callWithABI(AsmJSImmPtr imm, Result result = GENERAL);
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -122,17 +122,17 @@ CodeGeneratorShared::addOutOfLineCode(Ou
 static inline int32_t
 ToStackIndex(LAllocation *a)
 {
     if (a->isStackSlot()) {
         JS_ASSERT(a->toStackSlot()->slot() >= 1);
         return a->toStackSlot()->slot();
     }
     JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index());
-    return -(sizeof(IonJSFrameLayout) + a->toArgument()->index());
+    return -int32_t(sizeof(IonJSFrameLayout) + a->toArgument()->index());
 }
 
 bool
 CodeGeneratorShared::encodeSlots(LSnapshot *snapshot, MResumePoint *resumePoint,
                                  uint32_t *startIndex)
 {
     IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
             resumePoint->numOperands(), (void *) resumePoint, *startIndex);
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -633,41 +633,42 @@ class MacroAssemblerX86Shared : public A
 
         bind(cl.src());
         *offset = currentOffset();
 
         JS_ASSERT(framePushed() == initialDepth + IonExitFrameLayout::Size());
         return addCodeLabel(cl);
     }
 
-    bool buildOOLFakeExitFrame(void *fakeReturnAddr) {
-        uint32_t descriptor = MakeFrameDescriptor(framePushed(), IonFrame_OptimizedJS);
-        Push(Imm32(descriptor));
-        Push(ImmPtr(fakeReturnAddr));
-        return true;
-    }
-
     void callWithExitFrame(IonCode *target) {
         uint32_t descriptor = MakeFrameDescriptor(framePushed(), IonFrame_OptimizedJS);
         Push(Imm32(descriptor));
         call(target);
     }
     void callIon(const Register &callee) {
         call(callee);
     }
 
     void checkStackAlignment() {
         // Exists for ARM compatibility.
     }
 
     CodeOffsetLabel labelForPatch() {
         return CodeOffsetLabel(size());
     }
-    
+
     void abiret() {
         ret();
     }
+
+  protected:
+    bool buildOOLFakeExitFrame(void *fakeReturnAddr) {
+        uint32_t descriptor = MakeFrameDescriptor(framePushed(), IonFrame_OptimizedJS);
+        Push(Imm32(descriptor));
+        Push(ImmPtr(fakeReturnAddr));
+        return true;
+    }
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_MacroAssembler_x86_shared_h */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -539,17 +539,21 @@ TypeSet::unionSets(TypeSet *a, TypeSet *
 //
 // At the end of compilation, when linking the result on the main thread, the
 // list of compiler constraints are read and converted to type constraints and
 // attached to the type sets. If the property type sets have changed so that the
 // assumptions no longer hold then the compilation is aborted and its result
 // discarded.
 
 static LifoAlloc *IonAlloc() {
+#ifdef JS_ION
     return jit::GetIonContext()->temp->lifoAlloc();
+#else
+    MOZ_CRASH();
+#endif
 }
 
 // Superclass of all constraints generated during Ion compilation. These may
 // be allocated off the main thread, using the current Ion context's allocator.
 class types::CompilerConstraint
 {
   public:
     // Property being queried by the compiler.
@@ -570,18 +574,22 @@ class types::CompilerConstraint
     // Generate the type constraint recording the assumption made by this
     // compilation. Returns true if the assumption originally made still holds.
     virtual bool generateTypeConstraint(JSContext *cx, RecompileInfo recompileInfo) = 0;
 };
 
 void
 CompilerConstraintList::add(CompilerConstraint *constraint)
 {
+#ifdef JS_ION
     if (!constraint || !constraint->expected || !constraints.append(constraint))
         setFailed();
+#else
+    MOZ_CRASH();
+#endif
 }
 
 namespace {
 
 template <typename T>
 class CompilerConstraintInstance : public CompilerConstraint
 {
     T data;
@@ -667,35 +675,43 @@ TypeObjectKey::newScript()
             return addendum->asNewScript();
     }
     return NULL;
 }
 
 bool
 TypeObjectKey::unknownProperties()
 {
+#ifdef JS_ION
     JSContext *cx = jit::GetIonContext()->cx;
     TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
     if (!type)
         MOZ_CRASH();
     return type->unknownProperties();
+#else
+    MOZ_CRASH();
+#endif
 }
 
 HeapTypeSetKey
 TypeObjectKey::property(jsid id)
 {
+#ifdef JS_ION
     JSContext *cx = jit::GetIonContext()->cx;
     TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
     if (!type)
         MOZ_CRASH();
     HeapTypeSetKey property;
     property.actualTypes = type->getProperty(cx, id);
     if (!property.actualTypes)
         MOZ_CRASH();
     return property;
+#else
+    MOZ_CRASH();
+#endif
 }
 
 bool
 types::FinishCompilation(JSContext *cx, JSScript *script, jit::ExecutionMode executionMode,
                          CompilerConstraintList *constraints, RecompileInfo *precompileInfo)
 {
     if (constraints->failed())
         return false;
@@ -933,28 +949,32 @@ class ConstraintDataFreezeObjectFlags
     }
 };
 
 } /* anonymous namespace */
 
 bool
 TypeObjectKey::hasFlags(CompilerConstraintList *constraints, TypeObjectFlags flags)
 {
+#ifdef JS_ION
     JS_ASSERT(flags);
 
     JSContext *cx = jit::GetIonContext()->cx;
     TypeObject *type = isSingleObject() ? asSingleObject()->getType(cx) : asTypeObject();
     if (!type)
         MOZ_CRASH();
     if (type->hasAnyFlags(flags))
         return true;
 
     HeapTypeSetKey objectProperty = property(JSID_EMPTY);
     constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, flags)));
     return false;
+#else
+    MOZ_CRASH();
+#endif
 }
 
 void
 TypeObjectKey::watchStateChange(CompilerConstraintList *constraints)
 {
     HeapTypeSetKey objectProperty = property(JSID_EMPTY);
     constraints->add(IonAlloc()->new_<CompilerConstraintInstance<ConstraintDataFreezeObjectFlags> >(objectProperty, ConstraintDataFreezeObjectFlags(this, 0)));
 }
@@ -1648,21 +1668,25 @@ PrototypeHasIndexedProperty(CompilerCons
 
     return false;
 }
 
 bool
 types::ArrayPrototypeHasIndexedProperty(CompilerConstraintList *constraints,
                                         HandleScript script)
 {
+#ifdef JS_ION
     JSObject *proto = script->global().getOrCreateArrayPrototype(jit::GetIonContext()->cx);
     if (!proto)
         return true;
 
     return PrototypeHasIndexedProperty(constraints, proto);
+#else
+    MOZ_CRASH();
+#endif
 }
 
 bool
 types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
                                          TemporaryTypeSet *types)
 {
     const Class *clasp = types->getKnownClass();
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1397,34 +1397,44 @@ TypeNewScript::writeBarrierPre(TypeNewSc
 // still hold.
 bool
 FinishCompilation(JSContext *cx, JSScript *script, jit::ExecutionMode executionMode,
                   CompilerConstraintList *constraints, RecompileInfo *precompileInfo);
 
 class CompilerConstraint;
 class CompilerConstraintList
 {
+#ifdef JS_ION
     // Generated constraints.
     Vector<CompilerConstraint *, 0, jit::IonAllocPolicy> constraints;
+#endif
 
     // OOM during generation of some constraint.
     bool failed_;
 
   public:
     CompilerConstraintList()
       : failed_(false)
     {}
 
     void add(CompilerConstraint *constraint);
 
     size_t length() {
+#ifdef JS_ION
         return constraints.length();
+#else
+        MOZ_CRASH();
+#endif
     }
     CompilerConstraint *get(size_t i) {
+#ifdef JS_ION
         return constraints[i];
+#else
+        MOZ_CRASH();
+#endif
     }
 
     bool failed() {
         return failed_;
     }
     void setFailed() {
         failed_ = true;
     }
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -20,17 +20,19 @@
 #include "jsgc.h"
 #include "jsmath.h"
 #include "jsnativestack.h"
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
-#include "assembler/assembler/MacroAssembler.h"
+#if defined(JS_ION)
+# include "assembler/assembler/MacroAssembler.h"
+#endif
 #include "jit/AsmJSSignalHandlers.h"
 #include "jit/IonCompartment.h"
 #include "jit/PcScriptCache.h"
 #include "js/MemoryMetrics.h"
 #include "yarr/BumpPointerAllocator.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -446,54 +446,47 @@ nsImageFrame::ShouldCreateImageFrameFor(
   }
 
   // Check if we want to use a placeholder box with an icon or just
   // let the presShell make us into inline text.  Decide as follows:
   //
   //  - if our special "force icons" style is set, show an icon
   //  - else if our "do not show placeholders" pref is set, skip the icon
   //  - else:
-  //  - if QuirksMode, and there is no alt attribute, and this is not an
-  //    <object> (which could not possibly have such an attribute), show an
-  //    icon.
+  //  - if there is a src attribute, there is no alt attribute,
+  //    and this is not an <object> (which could not possibly have
+  //    such an attribute), show an icon.
   //  - if QuirksMode, and the IMG has a size show an icon.
   //  - otherwise, skip the icon
   bool useSizedBox;
   
   if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
     useSizedBox = true;
   }
   else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
     useSizedBox = false;
   }
+  else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
+           !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
+           !aElement->IsHTML(nsGkAtoms::object) &&
+           !aElement->IsHTML(nsGkAtoms::input)) {
+    // Use a sized box if we have no alt text.  This means no alt attribute
+    // and the node is not an object or an input (since those always have alt
+    // text).
+    useSizedBox = true;
+  }
+  else if (aStyleContext->PresContext()->CompatibilityMode() !=
+           eCompatibility_NavQuirks) {
+    useSizedBox = false;
+  }
   else {
-    if (aStyleContext->PresContext()->CompatibilityMode() !=
-        eCompatibility_NavQuirks) {
-      useSizedBox = false;
-    }
-    else {
-      // We are in quirks mode, so we can just check the tag name; no need to
-      // check the namespace.
-      nsIAtom *localName = aElement->Tag();
+    // check whether we have fixed size
+    useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
+  }
 
-      // Use a sized box if we have no alt text.  This means no alt attribute
-      // and the node is not an object or an input (since those always have alt
-      // text).
-      if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
-          localName != nsGkAtoms::object &&
-          localName != nsGkAtoms::input) {
-        useSizedBox = true;
-      }
-      else {
-        // check whether we have fixed size
-        useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
-      }
-    }
-  }
-  
   return useSizedBox;
 }
 
 nsresult
 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
@@ -715,19 +708,17 @@ nsImageFrame::EnsureIntrinsicSizeAndRati
     if (mImage) {
       UpdateIntrinsicSize(mImage);
       UpdateIntrinsicRatio(mImage);
     } else {
       // image request is null or image size not known, probably an
       // invalid image specified
       // - make the image big enough for the icon (it may not be
       // used if inline alt expansion is used instead)
-      // XXX: we need this in composer, but it is also good for
-      // XXX: general quirks mode to always have room for the icon
-      if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) {
+      if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
         nscoord edgeLengthToUse =
           nsPresContext::CSSPixelsToAppUnits(
             ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
         mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
         mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
         mIntrinsicRatio.SizeTo(1, 1);
       }
     }
--- a/layout/reftests/image/invalid-url-image-1-ref.html
+++ b/layout/reftests/image/invalid-url-image-1-ref.html
@@ -1,6 +1,7 @@
+<!DOCTYPE html>
 <html>
   <body>
     <img>
     <input type="image">
   </body>
 </html>
--- a/layout/reftests/image/invalid-url-image-1.html
+++ b/layout/reftests/image/invalid-url-image-1.html
@@ -1,6 +1,7 @@
+<!DOCTYPE html>
 <html>
   <body>
-    <img src="http://www.foo  oo.com">
+    <img src="http://www.foo  oo.com" alt="">
     <input type="image" src="http://www.foo  oo.com">
   </body>
 </html>
--- a/memory/jemalloc/moz.build
+++ b/memory/jemalloc/moz.build
@@ -2,41 +2,41 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'jemalloc'
 
 CSRCS += [
-    'arena.c',
-    'atomic.c',
-    'base.c',
-    'bitmap.c',
-    'chunk.c',
-    'chunk_dss.c',
-    'chunk_mmap.c',
-    'ckh.c',
-    'ctl.c',
-    'extent.c',
-    'hash.c',
-    'huge.c',
-    'jemalloc.c',
-    'mb.c',
-    'mutex.c',
-    'prof.c',
-    'quarantine.c',
-    'rtree.c',
-    'stats.c',
-    'tcache.c',
-    'tsd.c',
-    'util.c',
+    'src/src/arena.c',
+    'src/src/atomic.c',
+    'src/src/base.c',
+    'src/src/bitmap.c',
+    'src/src/chunk.c',
+    'src/src/chunk_dss.c',
+    'src/src/chunk_mmap.c',
+    'src/src/ckh.c',
+    'src/src/ctl.c',
+    'src/src/extent.c',
+    'src/src/hash.c',
+    'src/src/huge.c',
+    'src/src/jemalloc.c',
+    'src/src/mb.c',
+    'src/src/mutex.c',
+    'src/src/prof.c',
+    'src/src/quarantine.c',
+    'src/src/rtree.c',
+    'src/src/stats.c',
+    'src/src/tcache.c',
+    'src/src/tsd.c',
+    'src/src/util.c',
 ]
 
 # Only OSX needs the zone allocation implementation,
 # but only if replace-malloc is not enabled.
 if CONFIG['OS_TARGET'] == 'Darwin' and not CONFIG['MOZ_REPLACE_MALLOC']:
     CSRCS += [
-        'zone.c',
+        'src/src/zone.c',
     ]
 
 LIBRARY_NAME = 'jemalloc'
 
--- a/memory/replace/dmd/Makefile.in
+++ b/memory/replace/dmd/Makefile.in
@@ -2,20 +2,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 FORCE_SHARED_LIB= 1
 
 DEFINES 	+= -DMOZ_NO_MOZALLOC
 
-VPATH 		+= $(topsrcdir)/xpcom/base
-VPATH 		+= $(topsrcdir)/nsprpub/lib/libc/src
-VPATH 		+= $(topsrcdir)/mfbt
-
 # Disable mozglue.
 WRAP_LDFLAGS 	=
 MOZ_GLUE_LDFLAGS=
 
 STL_FLAGS 	=
 
 ifeq ($(OS_ARCH),WINNT)
 OS_LIBS         += $(call EXPAND_LIBNAME,dbghelp)
--- a/memory/replace/dmd/moz.build
+++ b/memory/replace/dmd/moz.build
@@ -7,18 +7,18 @@
 MODULE = 'dmd'
 
 EXPORTS += [
     'DMD.h',
 ]
 
 CPP_SOURCES += [
     'DMD.cpp',
-    'HashFunctions.cpp',
-    'nsStackWalk.cpp',
+    '../../../mfbt/HashFunctions.cpp',
+    '../../../xpcom/base/nsStackWalk.cpp',
 ]
 
 CSRCS += [
-    'strcpy.c',
+    '../../../nsprpub/lib/libc/src/strcpy.c',
 ]
 
 LIBRARY_NAME = 'dmd'
 
--- a/memory/replace/jemalloc/moz.build
+++ b/memory/replace/jemalloc/moz.build
@@ -3,18 +3,18 @@
 # 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/.
 
 MODULE = 'memory'
 NO_DIST_INSTALL = True
 
 CSRCS += [
-    'jemalloc_config.c',
-    'mozjemalloc_compat.c',
+    '../../build/jemalloc_config.c',
+    '../../build/mozjemalloc_compat.c',
 ]
 
 # Android doesn't have pthread_atfork, so just implement a dummy function.
 # It shouldn't make much problem, as the use of fork is pretty limited on
 # Android.
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     CSRCS += [
         'pthread_atfork.c',
--- a/security/sandbox/seccomp_filter.h
+++ b/security/sandbox/seccomp_filter.h
@@ -65,16 +65,25 @@
   ALLOW_ARM_SYSCALL(cacheflush), \
   ALLOW_ARM_SYSCALL(usr26), \
   ALLOW_ARM_SYSCALL(usr32), \
   ALLOW_ARM_SYSCALL(set_tls),
 #else
 #define SECCOMP_WHITELIST_ADD_ARM_LAST
 #endif
 
+/* System calls used by the profiler */
+#ifdef MOZ_PROFILING
+#define PROFILING_WHITELIST_ADD \
+  ALLOW_SYSCALL(sigaction), \
+  ALLOW_SYSCALL(tgkill),
+#else
+#define PROFILING_WHITELIST_ADD
+#endif
+
 /* Syscalls specific to arm that should eventually be removed */
 #if defined(__arm__)
 #define SECCOMP_WHITELIST_REMOVE_ARM \
   ALLOW_SYSCALL(fstat64), \
   ALLOW_SYSCALL(stat64), \
   ALLOW_SYSCALL(sigprocmask),
 #else
 #define SECCOMP_WHITELIST_REMOVE_ARM
@@ -146,15 +155,16 @@
   ALLOW_SYSCALL(prctl), \
   ALLOW_SYSCALL(access), \
   ALLOW_SYSCALL(getdents64), \
   ALLOW_SYSCALL(unlink), \
   /* Should remove all of the following in the future, if possible */ \
   ALLOW_SYSCALL(getpriority), \
   ALLOW_SYSCALL(setpriority), \
   ALLOW_SYSCALL(sched_setscheduler), \
+  PROFILING_WHITELIST_ADD \
   /* Always last and always OK calls */ \
   SECCOMP_WHITELIST_ADD_ARM_LAST \
   /* restart_syscall is called internally, generally when debugging */ \
   ALLOW_SYSCALL(restart_syscall), \
   ALLOW_SYSCALL(exit_group), \
   ALLOW_SYSCALL(exit)
 
--- a/uriloader/base/nsIURIContentListener.idl
+++ b/uriloader/base/nsIURIContentListener.idl
@@ -36,19 +36,21 @@ interface nsIURIContentListener : nsISup
   *
   * @param aContentType         Content type of the data.
   * @param aIsContentPreferred  Indicates whether the content should be
   *                             preferred by this listener.
   * @param aRequest             Request that is providing the data.
   * @param aContentHandler      nsIStreamListener that will consume the data.
   *                             This should be set to <code>nullptr</code> if
   *                             this content listener can't handle the content
-  *                             type.
+  *                             type; in this case, doContent should also fail
+  *                             (i.e., return failure nsresult).
   *
-  * @return                     <code>true</code> if the consumer wants to
+  * @return                     <code>true</code> if the load should
+  *                             be aborted and consumer wants to
   *                             handle the load completely by itself.  This
   *                             causes the URI Loader do nothing else...
   *                             <code>false</code> if the URI Loader should
   *                             continue handling the load and call the
   *                             returned streamlistener's methods. 
   */
   boolean doContent(in string aContentType,
                     in boolean aIsContentPreferred,