merge mozilla-inbound to mozilla-central a=merge
authorIris Hsiao <ihsiao@mozilla.com>
Wed, 12 Apr 2017 11:16:46 +0800
changeset 352519 f40e24f40b4c4556944c762d4764eace261297f5
parent 352470 b050e9d52468c52f4a3f47c6082232e5df562a08 (current diff)
parent 352518 8e4c71612bdc8715b4949c4a6861deb656981bdb (diff)
child 352520 402f5142f0d0b2d06dfdc8649ce97185ca257e4b
child 352684 d934b161ab244ba6323ce5f4ba151d8163332f39
child 352944 c4bd1b14fbade8d99bd9aaea6c5eaedab1114f24
push id40548
push userihsiao@mozilla.com
push dateWed, 12 Apr 2017 03:23:16 +0000
treeherderautoland@402f5142f0d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
f40e24f40b4c / 55.0a1 / 20170412100254 / files
nightly linux64
f40e24f40b4c / 55.0a1 / 20170412100254 / files
nightly mac
f40e24f40b4c / 55.0a1 / 20170412030252 / files
nightly win32
f40e24f40b4c / 55.0a1 / 20170412030252 / files
nightly win64
f40e24f40b4c / 55.0a1 / 20170412030252 / 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 mozilla-inbound to mozilla-central a=merge
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -235,16 +235,20 @@ function chromeFileExists(aURI) {
       dump("Checking " + aURI + ": " + e + "\n");
       Cu.reportError(e);
     }
   }
   return available > 0;
 }
 
 add_task(function* checkAllTheCSS() {
+  // Since we later in this test use Services.console.getMessageArray(),
+  // better to not have some messages from previous tests in the array.
+  Services.console.reset();
+
   let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
   // This asynchronously produces a list of URLs (sadly, mostly sync on our
   // test infrastructure because it runs against jarfiles there, and
   // our zipreader APIs are all sync)
   let uris = yield generateURIsFromDirTree(appDir, [".css", ".manifest"]);
 
   // Create a clean iframe to load all the files into. This needs to live at a
   // chrome URI so that it's allowed to load and parse any styles.
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js
@@ -4,33 +4,35 @@ add_task(function* setup() {
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("privacy.firstparty.isolate");
   });
 });
 
 /**
  * For loading the initial about:blank in e10s mode, it will be loaded with
  * NullPrincipal, and we also test if the firstPartyDomain of the origin
- * attributes is NULL_PRINCIPAL_FIRST_PARTY_DOMAIN.
+ * attributes is got from the origin itself.
  */
 add_task(function* test_remote_window_open_aboutBlank() {
   let win = yield BrowserTestUtils.openNewBrowserWindow({ remote: true });
   let browser = win.gBrowser.selectedBrowser;
 
   Assert.ok(browser.isRemoteBrowser, "should be a remote browser");
 
-  let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
-  yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
+  yield ContentTask.spawn(browser, {}, function* () {
     info("origin " + content.document.nodePrincipal.origin);
 
     Assert.ok(content.document.nodePrincipal.isNullPrincipal,
               "The principal of remote about:blank should be a NullPrincipal.");
+
+    let str = content.document.nodePrincipal.originNoSuffix;
+    let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
     Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
-                 expectAttrs.firstPartyDomain,
-                 "remote about:blank should have firstPartyDomain set");
+                 expectDomain,
+                 "remote about:blank should have firstPartyDomain set to " + expectDomain);
   });
 
   win.close();
 });
 
 /**
  * For loading the initial about:blank in non-e10s mode, it will be loaded with
  * a null principal. So we test if it has correct firstPartyDomain set.
@@ -70,25 +72,27 @@ add_task(function* test_remote_window_op
   let browser = win.gBrowser.selectedBrowser;
   let mm = browser.messageManager;
   mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
 
   yield BrowserTestUtils.browserLoaded(browser, false, function(url) {
     return url == "data:text/plain,hello";
   });
 
-  let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
-  yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
+  yield ContentTask.spawn(browser, {}, function* () {
     info("origin: " + content.document.nodePrincipal.origin);
 
     Assert.ok(content.document.nodePrincipal.isNullPrincipal,
               "The principal of data: document should be a NullPrincipal.");
+
+    let str = content.document.nodePrincipal.originNoSuffix;
+    let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
     Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
-                 expectAttrs.firstPartyDomain,
-                 "data: URI should have firstPartyDomain set");
+                 expectDomain,
+                 "data: URI should have firstPartyDomain set to " + expectDomain);
   });
 
   win.close();
 });
 
 /**
  * data: document contains an iframe, and we test that iframe should inherit
  * origin attributes from the data: document.
@@ -98,31 +102,33 @@ add_task(function* test_remote_window_op
   let browser = win.gBrowser.selectedBrowser;
 
   // The iframe test2.html will fetch test2.js, which will have cookies.
   const DATA_URI = `data:text/html,
                     <iframe id="iframe1" src="http://mochi.test:8888/browser/browser/components/originattributes/test/browser/test2.html"></iframe>`;
   browser.loadURI(DATA_URI);
   yield BrowserTestUtils.browserLoaded(browser, true);
 
-  let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
-  yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
+  yield ContentTask.spawn(browser, {}, function* () {
     info("origin " + content.document.nodePrincipal.origin);
 
     let iframe = content.document.getElementById("iframe1");
     info("iframe principal: " + iframe.contentDocument.nodePrincipal.origin);
 
     Assert.ok(content.document.nodePrincipal.isNullPrincipal,
               "The principal of data: document should be a NullPrincipal.");
+
+    let str = content.document.nodePrincipal.originNoSuffix;
+    let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
     Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
-                 expectAttrs.firstPartyDomain,
-                 "data: URI should have firstPartyDomain set");
+                 expectDomain,
+                 "data: URI should have firstPartyDomain set to " + expectDomain);
 
     Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
-                 expectAttrs.firstPartyDomain,
+                 expectDomain,
                  "iframe should inherit firstPartyDomain from parent document.");
     Assert.equal(iframe.contentDocument.cookie, "test2=foo", "iframe should have cookies");
   });
 
   win.close();
 });
 
 /**
--- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_js_uri.js
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_js_uri.js
@@ -8,25 +8,27 @@ add_task(function* setup() {
 
 add_task(function* test_remote_window_open_js_uri() {
   let win = yield BrowserTestUtils.openNewBrowserWindow({ remote: true });
   let browser = win.gBrowser.selectedBrowser;
 
   Assert.ok(browser.isRemoteBrowser, "should be a remote browser");
 
   browser.loadURI(`javascript:1;`);
-  let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
-  yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
+  yield ContentTask.spawn(browser, {}, function* () {
     info("origin " + content.document.nodePrincipal.origin);
 
     Assert.ok(content.document.nodePrincipal.isNullPrincipal,
               "The principal of remote javascript: should be a NullPrincipal.");
+
+    let str = content.document.nodePrincipal.originNoSuffix;
+    let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
     Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
-                 expectAttrs.firstPartyDomain,
-                 "remote javascript: should have firstPartyDomain set");
+                 expectDomain,
+                 "remote javascript: should have firstPartyDomain set to " + expectDomain);
   });
 
   win.close();
 });
 
 add_task(function* test_remote_window_open_js_uri2() {
   let win = yield BrowserTestUtils.openNewBrowserWindow({ remote: true });
   let browser = win.gBrowser.selectedBrowser;
@@ -41,25 +43,29 @@ add_task(function* test_remote_window_op
     alert("connect to http://example.com");
  `);
 
   yield BrowserTestUtils.browserLoaded(browser, true, function(url) {
     info("URL:" + url);
     return url == "http://example.com/";
   });
 
-  let attrs = { firstPartyDomain: "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla" };
-  yield ContentTask.spawn(browser, attrs, function* (expectAttrs) {
+  yield ContentTask.spawn(browser, {}, function* () {
     info("origin " + content.document.nodePrincipal.origin);
 
     Assert.ok(content.document.nodePrincipal.isNullPrincipal,
               "The principal of remote javascript: should be a NullPrincipal.");
+
+    let str = content.document.nodePrincipal.originNoSuffix;
+    let expectDomain = str.substring("moz-nullprincipal:{".length, str.length - 1) + ".mozilla";
     Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
-                 expectAttrs.firstPartyDomain,
-                 "remote javascript: should have firstPartyDomain set");
+                 expectDomain,
+                 "remote javascript: should have firstPartyDomain set to " + expectDomain);
 
     let iframe = content.document.getElementById("iframe1");
-    info("iframe principal: " + iframe.contentDocument.nodePrincipal.origin);
+    Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
+                 expectDomain,
+                 "iframe should have firstPartyDomain set to " + expectDomain);
   });
 
   win.close();
 });
 
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -77,17 +77,17 @@ AC_DEFUN([MOZ_DEBUGGING_OPTS],
 [
 
 if test -z "$MOZ_DEBUG" -o -n "$MOZ_ASAN"; then
     MOZ_NO_DEBUG_RTL=1
 fi
 
 AC_SUBST(MOZ_NO_DEBUG_RTL)
 
-MOZ_DEBUG_ENABLE_DEFS="DEBUG TRACING"
+MOZ_DEBUG_ENABLE_DEFS="DEBUG"
 MOZ_ARG_WITH_STRING(debug-label,
 [  --with-debug-label=LABELS
                           Define DEBUG_<value> for each comma-separated
                           value given.],
 [ for option in `echo $withval | sed 's/,/ /g'`; do
     MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS DEBUG_${option}"
 done])
 
--- a/caps/NullPrincipal.cpp
+++ b/caps/NullPrincipal.cpp
@@ -44,20 +44,19 @@ NullPrincipal::CreateWithInheritedAttrib
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   return nullPrin.forget();
 }
 
 /* static */ already_AddRefed<NullPrincipal>
 NullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty)
 {
   OriginAttributes attrs = nsDocShell::Cast(aDocShell)->GetOriginAttributes();
-  attrs.SetFirstPartyDomain(aIsFirstParty, NS_LITERAL_CSTRING(NULL_PRINCIPAL_FIRST_PARTY_DOMAIN));
 
   RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
-  nsresult rv = nullPrin->Init(attrs);
+  nsresult rv = nullPrin->Init(attrs, aIsFirstParty);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   return nullPrin.forget();
 }
 
 /* static */ already_AddRefed<NullPrincipal>
 NullPrincipal::Create(const OriginAttributes& aOriginAttributes, nsIURI* aURI)
 {
   RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
@@ -89,16 +88,43 @@ NullPrincipal::Init(const OriginAttribut
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   FinishInit(originNoSuffix, aOriginAttributes);
 
   return NS_OK;
 }
 
 nsresult
+NullPrincipal::Init(const OriginAttributes& aOriginAttributes, bool aIsFirstParty)
+{
+  mURI = NullPrincipalURI::Create();
+  NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_AVAILABLE);
+
+  nsAutoCString originNoSuffix;
+  DebugOnly<nsresult> rv = mURI->GetSpec(originNoSuffix);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  nsAutoCString path;
+  rv = mURI->GetPath(path);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  OriginAttributes attrs(aOriginAttributes);
+  if (aIsFirstParty) {
+    // remove the '{}' characters from both ends.
+    path.Mid(path, 1, path.Length() - 2);
+    path.AppendLiteral(".mozilla");
+    attrs.SetFirstPartyDomain(true, path);
+  }
+
+  FinishInit(originNoSuffix, attrs);
+
+  return NS_OK;
+}
+
+nsresult
 NullPrincipal::GetScriptLocation(nsACString &aStr)
 {
   return mURI->GetSpec(aStr);
 }
 
 /**
  * nsIPrincipal implementation
  */
--- a/caps/NullPrincipal.h
+++ b/caps/NullPrincipal.h
@@ -52,17 +52,17 @@ public:
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
 
   static already_AddRefed<NullPrincipal> CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
 
   // Create NullPrincipal with origin attributes from docshell.
   // If aIsFirstParty is true, and the pref 'privacy.firstparty.isolate' is also
   // enabled, the mFirstPartyDomain value of the origin attributes will be set
-  // to NULL_PRINCIPAL_FIRST_PARTY_DOMAIN.
+  // to an unique value.
   static already_AddRefed<NullPrincipal>
   CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty = false);
 
   static already_AddRefed<NullPrincipal>
   Create(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes(),
          nsIURI* aURI = nullptr);
 
   nsresult Init(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes(),
@@ -76,11 +76,17 @@ public:
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override
   {
     return aOther == this;
   }
 
   bool MayLoadInternal(nsIURI* aURI) override;
 
   nsCOMPtr<nsIURI> mURI;
+
+private:
+  // If aIsFirstParty is true, this NullPrincipal will be initialized base on
+  // the aOriginAttributes with FirstPartyDomain set to an unique value, and this
+  // value is generated from mURI.path, with ".mozilla" appending at the end.
+  nsresult Init(const mozilla::OriginAttributes& aOriginAttributes, bool aIsFirstParty);
 };
 
 #endif // NullPrincipal_h__
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -3266,17 +3266,17 @@ nsRange::AutoInvalidateSelection::~AutoI
   NS_ASSERTION(mWasInSelection == mRange->IsInSelection(),
                "Range got unselected in AutoInvalidateSelection block");
   if (!mCommonAncestor) {
     return;
   }
   mIsNested = false;
   ::InvalidateAllFrames(mCommonAncestor);
   nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
-  if (commonAncestor != mCommonAncestor) {
+  if (commonAncestor && commonAncestor != mCommonAncestor) {
     ::InvalidateAllFrames(commonAncestor);
   }
 }
 
 /* static */ already_AddRefed<nsRange>
 nsRange::Constructor(const GlobalObject& aGlobal,
                      ErrorResult& aRv)
 {
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1350972.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+    try { o1 = document.createElement('tr'); } catch(e) {};
+    try { o2 = document.createElement('div'); } catch(e) {};
+    try { o3 = document.createElement('hr'); } catch(e) {};
+    try { o4 = document.createElement('textarea'); } catch(e) {};
+    try { o5 = document.getSelection(); } catch(e) {};
+    try { o6 = document.createRange(); } catch(e) {};
+    try { document.documentElement.appendChild(o2); } catch(e) {};
+    try { document.documentElement.appendChild(o3); } catch(e) {};
+    try { o2.appendChild(o4); } catch(e) {};
+    try { o3.outerHTML = "<noscript contenteditable='true'>"; } catch(e) {};
+    try { o4.select(); } catch(e) {};
+    try { o5.addRange(o6); } catch(e) {};
+    try { document.documentElement.appendChild(o1); } catch(e) {};
+    try { o5.selectAllChildren(o1); } catch(e) {};
+    try { o6.selectNode(o1); } catch(e) {};
+</script>
+</head>
+</html>
\ No newline at end of file
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -76,8 +76,9 @@ load 1228876.html
 load 1230110.html
 load 1237633.html
 load 1281972-1.html
 load 1282894.html
 load 1290904.html
 load 1343886-1.html
 load 1343886-2.xml
 load 1343886-3.xml
+asserts(0-3) load 1350972.html
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -7,16 +7,17 @@
 #include "ServiceWorkerEvents.h"
 
 #include "nsAutoPtr.h"
 #include "nsIConsoleReportCollector.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIOutputStream.h"
 #include "nsIScriptError.h"
+#include "nsITimedChannel.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIUnicodeEncoder.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsNetCID.h"
@@ -103,16 +104,22 @@ CancelChannelRunnable::CancelChannelRunn
   , mStatus(aStatus)
 {
 }
 
 NS_IMETHODIMP
 CancelChannelRunnable::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  // TODO: When bug 1204254 is implemented, this time marker should be moved to
+  // the point where the body of the network request is complete.
+  mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
+  mChannel->SaveTimeStampsToUnderlyingChannel();
+
   mChannel->Cancel(mStatus);
   mRegistration->MaybeScheduleUpdate();
   return NS_OK;
 }
 
 FetchEvent::FetchEvent(EventTarget* aOwner)
   : ExtendableEvent(aOwner)
   , mPreventDefaultLineNumber(0)
@@ -225,16 +232,19 @@ public:
     loadInfo->MaybeIncreaseTainting(mInternalResponse->GetTainting());
 
     rv = mChannel->FinishSynthesizedResponse(mResponseURLSpec);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
       return NS_OK;
     }
 
+    mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
+    mChannel->SaveTimeStampsToUnderlyingChannel();
+
     nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
     if (obsService) {
       obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);
     }
 
     return rv;
   }
   bool CSPPermitsResponse(nsILoadInfo* aLoadInfo)
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -9,16 +9,17 @@
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 #include "nsContentUtils.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIPushErrorReporter.h"
 #include "nsISupportsImpl.h"
+#include "nsITimedChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
@@ -1292,16 +1293,17 @@ class FetchEventRunnable : public Extend
   const nsCString mScriptSpec;
   nsTArray<nsCString> mHeaderNames;
   nsTArray<nsCString> mHeaderValues;
   nsCString mSpec;
   nsCString mFragment;
   nsCString mMethod;
   nsString mClientId;
   bool mIsReload;
+  bool mMarkLaunchServiceWorkerEnd;
   RequestCache mCacheMode;
   RequestMode mRequestMode;
   RequestRedirect mRequestRedirect;
   RequestCredentials mRequestCredentials;
   nsContentPolicyType mContentPolicyType;
   nsCOMPtr<nsIInputStream> mUploadStream;
   nsCString mReferrer;
   ReferrerPolicy mReferrerPolicy;
@@ -1310,23 +1312,25 @@ public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      KeepAliveToken* aKeepAliveToken,
                      nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      // CSP checks might require the worker script spec
                      // later on.
                      const nsACString& aScriptSpec,
                      nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
                      const nsAString& aDocumentId,
-                     bool aIsReload)
+                     bool aIsReload,
+                     bool aMarkLaunchServiceWorkerEnd)
     : ExtendableFunctionalEventWorkerRunnable(
         aWorkerPrivate, aKeepAliveToken, aRegistration)
     , mInterceptedChannel(aChannel)
     , mScriptSpec(aScriptSpec)
     , mClientId(aDocumentId)
     , mIsReload(aIsReload)
+    , mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd)
     , mCacheMode(RequestCache::Default)
     , mRequestMode(RequestMode::No_cors)
     , mRequestRedirect(RequestRedirect::Follow)
     // By default we set it to same-origin since normal HTTP fetches always
     // send credentials to same-origin websites unless explicitly forbidden.
     , mRequestCredentials(RequestCredentials::Same_origin)
     , mContentPolicyType(nsIContentPolicy::TYPE_INVALID)
     , mReferrer(kFETCH_CLIENT_REFERRER_STR)
@@ -1469,16 +1473,22 @@ public:
 
     return NS_OK;
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
+
+    if (mMarkLaunchServiceWorkerEnd) {
+      mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
+    }
+
+    mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now());
     return DispatchFetchEvent(aCx, aWorkerPrivate);
   }
 
   nsresult
   Cancel() override
   {
     nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
     if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) {
@@ -1497,16 +1507,20 @@ private:
     explicit ResumeRequest(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
       : mChannel(aChannel)
     {
     }
 
     NS_IMETHOD Run() override
     {
       AssertIsOnMainThread();
+
+      mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
+      mChannel->SaveTimeStampsToUnderlyingChannel();
+
       nsresult rv = mChannel->ResetInterception();
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                            "Failed to resume intercepted network request");
       return rv;
     }
   };
 
   bool
@@ -1572,16 +1586,18 @@ private:
     if (NS_WARN_IF(result.Failed())) {
       result.SuppressException();
       return false;
     }
 
     event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec);
     event->SetTrusted(true);
 
+    mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now());
+
     nsresult rv2 =
       DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
                                            event, nullptr);
     if ((NS_WARN_IF(NS_FAILED(rv2)) && rv2 != NS_ERROR_XPC_JS_THREW_EXCEPTION) ||
         !event->WaitToRespond()) {
       nsCOMPtr<nsIRunnable> runnable;
       MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
                  "We don't support system-principal serviceworkers");
@@ -1642,31 +1658,43 @@ ServiceWorkerPrivate::SendFetchEvent(nsI
     return NS_OK;
   }
 
   // if the ServiceWorker script fails to load for some reason, just resume
   // the original channel.
   nsCOMPtr<nsIRunnable> failRunnable =
     NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
 
-  nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup);
+  aChannel->SetLaunchServiceWorkerStart(TimeStamp::Now());
+  aChannel->SetDispatchFetchEventStart(TimeStamp::Now());
+
+  bool newWorkerCreated = false;
+  nsresult rv = SpawnWorkerIfNeeded(FetchEvent,
+                                    failRunnable,
+                                    &newWorkerCreated,
+                                    aLoadGroup);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (!newWorkerCreated) {
+    aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
+  }
+
   nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
     new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
 
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(registration, false));
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
 
+
   RefPtr<FetchEventRunnable> r =
     new FetchEventRunnable(mWorkerPrivate, token, handle,
                            mInfo->ScriptSpec(), regInfo,
-                           aDocumentId, aIsReload);
+                           aDocumentId, aIsReload, newWorkerCreated);
   rv = r->Init();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (mInfo->State() == ServiceWorkerState::Activating) {
     mPendingFunctionalEvents.AppendElement(r.forget());
     return NS_OK;
@@ -1679,26 +1707,33 @@ ServiceWorkerPrivate::SendFetchEvent(nsI
   }
 
   return NS_OK;
 }
 
 nsresult
 ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
                                           nsIRunnable* aLoadFailedRunnable,
+                                          bool* aNewWorkerCreated,
                                           nsILoadGroup* aLoadGroup)
 {
   AssertIsOnMainThread();
 
   // XXXcatalinb: We need to have a separate load group that's linked to
   // an existing tab child to pass security checks on b2g.
   // This should be fixed in bug 1125961, but for now we enforce updating
   // the overriden load group when intercepting a fetch.
   MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup);
 
+  // Defaults to no new worker created, but if there is one, we'll set the value
+  // to true at the end of this function.
+  if (aNewWorkerCreated) {
+    *aNewWorkerCreated = false;
+  }
+
   if (mWorkerPrivate) {
     mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
     RenewKeepAliveToken(aWhy);
 
     return NS_OK;
   }
 
   // Sanity check: mSupportsArray should be empty if we're about to
@@ -1795,16 +1830,20 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
                                               false, WorkerTypeService,
                                               mInfo->Scope(), &info, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   RenewKeepAliveToken(aWhy);
 
+  if (aNewWorkerCreated) {
+    *aNewWorkerCreated = true;
+  }
+
   return NS_OK;
 }
 
 void
 ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mWorkerPrivate);
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -191,16 +191,17 @@ private:
   void
   ReleaseToken();
 
   // |aLoadFailedRunnable| is a runnable dispatched to the main thread
   // if the script loader failed for some reason, but can be null.
   nsresult
   SpawnWorkerIfNeeded(WakeUpReason aWhy,
                       nsIRunnable* aLoadFailedRunnable,
+                      bool* aNewWorkerCreated = nullptr,
                       nsILoadGroup* aLoadGroup = nullptr);
 
   ~ServiceWorkerPrivate();
 
   already_AddRefed<KeepAliveToken>
   CreateEventKeepAliveToken();
 
   // The info object owns us. It is possible to outlive it for a brief period
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -1,16 +1,19 @@
 [DEFAULT]
 skip-if = os == 'android'
 support-files =
   chrome_helpers.js
   empty.js
+  fetch.js
+  hello.html
   serviceworker.html
   serviceworkerinfo_iframe.html
   serviceworkermanager_iframe.html
   serviceworkerregistrationinfo_iframe.html
   worker.js
   worker2.js
 
+[test_devtools_serviceworker_interception.html]
 [test_privateBrowsing.html]
 [test_serviceworkerinfo.xul]
 [test_serviceworkermanager.xul]
 [test_serviceworkerregistrationinfo.xul]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
@@ -0,0 +1,168 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1168875 - test devtools serviceworker interception.</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet"
+        type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+// Constants
+const Ci = Components.interfaces;
+const workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
+const workerURL = workerScope + "fetch.js";
+const contentPage = workerScope + "hello.html";
+
+function createTestWindow(aURL) {
+  var mainwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIDocShellTreeItem)
+                         .rootTreeItem
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindow);
+  var win = mainwindow.OpenBrowserWindow(contentPage);
+
+  return new Promise(aResolve => {
+    win.addEventListener("DOMContentLoaded", function callback() {
+      if (win.content.location.href != aURL) {
+        win.gBrowser.loadURI(aURL);
+        return;
+      }
+
+      win.removeEventListener("DOMContentLoaded", callback);
+      aResolve(win.content);
+    });
+  });
+}
+
+function executeTest(aWindow) {
+  var registration;
+
+  return Promise.resolve()
+    // Should not be intercepted.
+    .then(_ => fetchAndCheckTimedChannel(aWindow, false, true, "hello.html"))
+
+    // Regist a service worker.
+    .then(_ => register(aWindow, workerURL, workerScope))
+    .then(r => registration = r)
+
+    // Should be intercpeted and synthesized.
+    .then(_ => fetchAndCheckTimedChannel(aWindow, true, false, "fake.html"))
+
+    // Should be intercepted but still fetch from network.
+    .then(_ => fetchAndCheckTimedChannel(aWindow, true, true,
+                                         "hello.html?ForBypassingHttpCache"))
+
+    // Tear down
+    .then(_ => registration.unregister());
+}
+
+function register(aWindow, aURL, aScope) {
+  return aWindow.navigator.serviceWorker.register(aURL, {scope: aScope})
+    .then(r => {
+      var worker = r.installing;
+      return new Promise(function(aResolve) {
+        worker.onstatechange = function() {
+          if (worker.state == "activated") {
+            aResolve(r);
+          }
+        }
+      });
+    });
+}
+
+function fetchAndCheckTimedChannel(aWindow, aIntercepted, aFetch, aURL) {
+  var resolveFunction;
+  var promise = new Promise(aResolve => resolveFunction = aResolve);
+
+  var topic = aFetch ? "http-on-examine-response"
+                     : "service-worker-synthesized-response";
+
+  function observer(aSubject) {
+    var channel = aSubject.QueryInterface(Ci.nsIChannel);
+    ok(channel.URI.spec.endsWith(aURL));
+
+    var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
+
+    // Check service worker related timings.
+    var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
+                                 end:   tc.launchServiceWorkerEndTime},
+                                {start: tc.dispatchFetchEventStartTime,
+                                 end:   tc.dispatchFetchEventEndTime},
+                                {start: tc.handleFetchEventStartTime,
+                                 end:   tc.handleFetchEventEndTime}];
+    if (aIntercepted) {
+      serviceWorkerTimings.reduce((aPreviousTimings, aCurrentTimings) => {
+        ok(aPreviousTimings.start <= aCurrentTimings.start,
+           "Start time order check.");
+        ok(aPreviousTimings.end <= aCurrentTimings.end,
+           "End time order check.");
+        ok(aCurrentTimings.start <= aCurrentTimings.end,
+           "Start time should be smaller than end time.");
+        return aCurrentTimings;
+      });
+    } else {
+      serviceWorkerTimings.forEach(aTimings => {
+        is(aTimings.start, 0);
+        is(aTimings.end, 0);
+      });
+    }
+
+    // Check network related timings.
+    var networkTimings = [tc.domainLookupStartTime,
+                          tc.domainLookupEndTime,
+                          tc.connectStartTime,
+                          tc.connectEndTime,
+                          tc.requestStartTime,
+                          tc.responseStartTime,
+                          tc.responseEndTime];
+    if (aFetch) {
+      networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
+        ok(aPreviousTiming <= aCurrentTiming);
+        return aCurrentTiming;
+      });
+    } else {
+      networkTimings.forEach(aTiming => is(aTiming, 0));
+    }
+
+    SpecialPowers.removeObserver(observer, topic);
+    resolveFunction();
+  }
+
+  SpecialPowers.addObserver(observer, topic, false);
+
+  // return promise;
+  return Promise.all([aWindow.fetch(aURL), promise]);
+}
+
+function runTest() {
+  return Promise.resolve()
+    .then(_ => createTestWindow(contentPage))
+    .then(w => executeTest(w))
+    .catch(e => ok(false, "Some test failed with error " + e))
+    .then(_ => SimpleTest.finish());
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+  ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+  ["dom.serviceWorkers.enabled", true],
+  ["dom.serviceWorkers.testing.enabled", true],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -73,45 +73,10 @@ DrawEventRecorderFile::OpenNew(const cha
 void
 DrawEventRecorderFile::Close()
 {
   MOZ_ASSERT(mOutputFile.is_open());
 
   mOutputFile.close();
 }
 
-DrawEventRecorderMemory::DrawEventRecorderMemory()
-  : DrawEventRecorderPrivate(nullptr)
-{
-  mOutputStream = &mMemoryStream;
-
-  WriteHeader();
-}
-
-void
-DrawEventRecorderMemory::Flush()
-{
-   mOutputStream->flush();
-}
-
-size_t
-DrawEventRecorderMemory::RecordingSize()
-{
-  return mMemoryStream.tellp();
-}
-
-bool
-DrawEventRecorderMemory::CopyRecording(char* aBuffer, size_t aBufferLen)
-{
-  return !!mMemoryStream.read(aBuffer, aBufferLen);
-}
-
-void
-DrawEventRecorderMemory::WipeRecording()
-{
-  mMemoryStream.str(std::string());
-  mMemoryStream.clear();
-
-  WriteHeader();
-}
-
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -98,59 +98,12 @@ public:
   void Close();
 
 private:
   virtual void Flush();
 
   std::ofstream mOutputFile;
 };
 
-class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
-{
-public:
-  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory)
-
-  /**
-   * Constructs a DrawEventRecorder that stores the recording in memory.
-   */
-  DrawEventRecorderMemory();
-
-  /**
-   * @return the current size of the recording (in chars).
-   */
-  size_t RecordingSize();
-
-  /**
-   * Copies at most aBufferLen chars of the recording into aBuffer.
-   *
-   * @param aBuffer buffer to receive the recording chars
-   * @param aBufferLen length of aBuffer
-   * @return true if copied successfully
-   */
-  bool CopyRecording(char* aBuffer, size_t aBufferLen);
-
-  /**
-   * Wipes the internal recording buffer, but the recorder does NOT forget which
-   * objects it has recorded. This can be used so that a recording can be copied
-   * and processed in chunks, releasing memory as it goes.
-   */
-  void WipeRecording();
-
-  /**
-   * Gets a readable reference of the underlying stream, reset to the beginning.
-   */
-  std::istream& GetInputStream() {
-    mMemoryStream.seekg(0);
-    return mMemoryStream;
-  }
-
-private:
-  ~DrawEventRecorderMemory() {};
-
-  void Flush() final;
-
-  std::stringstream mMemoryStream;
-};
-
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */
--- a/image/imgICache.idl
+++ b/image/imgICache.idl
@@ -26,16 +26,26 @@ interface imgICache : nsISupports
    * Evict images from the cache.
    *
    * @param chrome If TRUE,  evict only chrome images.
    *               If FALSE, evict everything except chrome images.
    */
   void clearCache(in boolean chrome);
 
   /**
+   * Evict images from the cache.
+   *
+   * @param uri The URI to remove.
+   * @param doc The document to remove the cache entry for.
+   * @throws NS_ERROR_NOT_AVAILABLE if \a uri was unable to be removed from
+   * the cache.
+   */
+  [noscript] void removeEntry(in nsIURI uri, [optional] in nsIDOMDocument doc);
+
+  /**
    * Find Properties
    * Used to get properties such as 'type' and 'content-disposition'
    * 'type' is a nsISupportsCString containing the images' mime type such as
    * 'image/png'
    * 'content-disposition' will be a nsISupportsCString containing the header
    * If you call this before any data has been loaded from a URI, it will
    * succeed, but come back empty.
    *
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -1365,16 +1365,39 @@ imgLoader::ClearCache(bool chrome)
   if (chrome) {
     return ClearChromeImageCache();
   }
   return ClearImageCache();
 
 }
 
 NS_IMETHODIMP
+imgLoader::RemoveEntry(nsIURI* aURI,
+                       nsIDOMDocument* aDOMDoc)
+{
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
+  if (aURI) {
+    OriginAttributes attrs;
+    if (doc) {
+      nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
+      if (principal) {
+        attrs = principal->OriginAttributesRef();
+      }
+    }
+
+    nsresult rv = NS_OK;
+    ImageCacheKey key(aURI, attrs, doc, rv);
+    if (NS_SUCCEEDED(rv) && RemoveFromCache(key)) {
+      return NS_OK;
+    }
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
 imgLoader::FindEntryProperties(nsIURI* uri,
                                nsIDOMDocument* aDOMDoc,
                                nsIProperties** _retval)
 {
   *_retval = nullptr;
 
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
 
@@ -2164,16 +2187,28 @@ imgLoader::LoadImage(nsIURI* aURI,
       if (entry->HasNoProxies()) {
         LOG_FUNC_WITH_PARAM(gImgLog,
           "imgLoader::LoadImage() adding proxyless entry", "uri", key.Spec());
         MOZ_ASSERT(!request->HasCacheEntry(),
           "Proxyless entry's request has cache entry!");
         request->SetCacheEntry(entry);
 
         if (mCacheTracker) {
+          if (MOZ_UNLIKELY(!entry->GetExpirationState()->IsTracked())) {
+            bool inCache = false;
+            RefPtr<imgCacheEntry> e;
+            if (cache.Get(key, getter_AddRefs(e)) && e) {
+              inCache = (e == entry);
+            }
+            gfxCriticalNoteOnce << "entry with no proxies is no in tracker "
+                                << "request->HasConsumers() "
+                                << (request->HasConsumers() ? "true" : "false")
+                                << " inCache " << (inCache ? "true" : "false");
+          }
+
           mCacheTracker->MarkUsed(entry);
         }
       }
 
       entry->Touch();
 
     } else {
       // We can't use this entry. We'll try to load it off the network, and if
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -2409,26 +2409,26 @@ js::AsyncFromSyncIteratorMethod(JSContex
     }
 
     RootedObject resultObj(cx, &resultVal.toObject());
 
     // Following step numbers are for 6.1.3.2.1.
     // For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20.
 
     // Steps 7-8.
-    RootedValue value(cx);
-    if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
-        return AbruptRejectPromise(cx, args, resultPromise, nullptr);
-
-    // Steps 9-10.
     RootedValue doneVal(cx);
     if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
         return AbruptRejectPromise(cx, args, resultPromise, nullptr);
     bool done = ToBoolean(doneVal);
 
+    // Steps 9-10.
+    RootedValue value(cx);
+    if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+        return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
     // Step 11.
     Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
     if (!promise)
         return false;
 
     // Step 12.
     if (!ResolvePromiseInternal(cx, promise, value))
         return false;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -239,19 +239,19 @@ class LoopControl : public BreakableCont
         MOZ_ASSERT(is<LoopControl>());
 
         LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing());
 
         stackDepth_ = bce->stackDepth;
         loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;
 
         int loopSlots;
-        if (loopKind == StatementKind::Spread || loopKind == StatementKind::ForOfLoop)
+        if (loopKind == StatementKind::Spread)
             loopSlots = 3;
-        else if (loopKind == StatementKind::ForInLoop)
+        else if (loopKind == StatementKind::ForInLoop || loopKind == StatementKind::ForOfLoop)
             loopSlots = 2;
         else
             loopSlots = 0;
 
         MOZ_ASSERT(loopSlots <= stackDepth_);
 
         if (enclosingLoop) {
             canIonOsr_ = (enclosingLoop->canIonOsr_ &&
@@ -264,16 +264,30 @@ class LoopControl : public BreakableCont
     uint32_t loopDepth() const {
         return loopDepth_;
     }
 
     bool canIonOsr() const {
         return canIonOsr_;
     }
 
+    MOZ_MUST_USE bool emitSpecialBreakForDone(BytecodeEmitter* bce) {
+        // This doesn't pop stack values, nor handle any other controls.
+        // Should be called on the toplevel of the loop.
+        MOZ_ASSERT(bce->stackDepth == stackDepth_);
+        MOZ_ASSERT(bce->innermostNestableControl == this);
+
+        if (!bce->newSrcNote(SRC_BREAK))
+            return false;
+        if (!bce->emitJump(JSOP_GOTO, &breaks))
+            return false;
+
+        return true;
+    }
+
     MOZ_MUST_USE bool patchBreaksAndContinues(BytecodeEmitter* bce) {
         MOZ_ASSERT(continueTarget.offset != -1);
         if (!patchBreaks(bce))
             return false;
         bce->patchJumpsToTarget(continues, continueTarget);
         return true;
     }
 };
@@ -2075,41 +2089,39 @@ class ForOfLoopControl : public LoopCont
         ptrdiff_t start = bce->offset();
         if (!bce->emitIteratorClose(iterKind_, completionKind, allowSelfHosted_))
             return false;
         ptrdiff_t end = bce->offset();
         return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
     }
 
     bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) {
-        // Pop unnecessary values from the stack.  Effectively this means
+        // Pop unnecessary value from the stack.  Effectively this means
         // leaving try-catch block.  However, the performing IteratorClose can
         // reach the depth for try-catch, and effectively re-enter the
         // try-catch block.
-        if (!bce->emitPopN(2))                            // ITER
+        if (!bce->emit1(JSOP_POP))                        // ITER
             return false;
 
         // Clear ITER slot on the stack to tell catch block to avoid performing
         // IteratorClose again.
         if (!bce->emit1(JSOP_UNDEFINED))                  // ITER UNDEF
             return false;
         if (!bce->emit1(JSOP_SWAP))                       // UNDEF ITER
             return false;
 
         if (!emitIteratorClose(bce))                      // UNDEF
             return false;
 
         if (isTarget) {
             // At the level of the target block, there's bytecode after the
             // loop that will pop the iterator and the value, so push
-            // undefineds to balance the stack.
+            // an undefined to balance the stack.
             if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF
                 return false;
-            if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF UNDEF
-                return false;
         } else {
             if (!bce->emit1(JSOP_POP))                    //
                 return false;
         }
 
         return true;
     }
 };
@@ -2775,17 +2787,17 @@ NonLocalExitControl::prepareForNonLocalJ
             if (emitIteratorClose) {
                 if (!flushPops(bce_))
                     return false;
 
                 ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
                 if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
                     return false;
             } else {
-                npops += 3;
+                npops += 2;
             }
             break;
 
           case StatementKind::ForInLoop:
             if (!flushPops(bce_))
                 return false;
 
             // The iterator and the current value are on the stack.
@@ -2800,17 +2812,17 @@ NonLocalExitControl::prepareForNonLocalJ
         }
     }
 
     if (!flushPops(bce_))
         return false;
 
     if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
         ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
-        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF
+        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF
             return false;
     }
 
     EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope;
     for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
         if (!leaveScope(es))
             return false;
     }
@@ -7021,147 +7033,157 @@ BytecodeEmitter::emitForOf(ParseNode* fo
             return false;
     } else {
         if (!emitIterator())                              // ITER
             return false;
     }
 
     int32_t iterDepth = stackDepth;
 
-    // For-of loops have both the iterator, the result, and the result.value
-    // on the stack. Push undefineds to balance the stack.
-    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT
-        return false;
-    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT UNDEF
+    // For-of loops have both the iterator and the result.value on the stack.
+    // Push an undefined to balance the stack.
+    if (!emit1(JSOP_UNDEFINED))                           // ITER UNDEF
         return false;
 
     ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind);
 
     // Annotate so IonMonkey can find the loop-closing jump.
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR_OF, &noteIndex))
         return false;
 
     JumpList initialJump;
-    if (!emitJump(JSOP_GOTO, &initialJump))               // ITER RESULT UNDEF
+    if (!emitJump(JSOP_GOTO, &initialJump))               // ITER UNDEF
         return false;
 
     JumpTarget top{ -1 };
-    if (!emitLoopHead(nullptr, &top))                     // ITER RESULT UNDEF
+    if (!emitLoopHead(nullptr, &top))                     // ITER UNDEF
         return false;
 
     // If the loop had an escaping lexical declaration, replace the current
     // environment with an dead zoned one to implement TDZ semantics.
     if (headLexicalEmitterScope) {
         // The environment chain only includes an environment for the for-of
         // loop head *if* a scope binding is captured, thereby requiring
         // recreation each iteration. If a lexical scope exists for the head,
         // it must be the innermost one. If that scope has closed-over
         // bindings inducing an environment, recreate the current environment.
         DebugOnly<ParseNode*> forOfTarget = forOfHead->pn_kid1;
         MOZ_ASSERT(forOfTarget->isKind(PNK_LET) || forOfTarget->isKind(PNK_CONST));
         MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope);
         MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical);
 
         if (headLexicalEmitterScope->hasEnvironment()) {
-            if (!emit1(JSOP_RECREATELEXICALENV))          // ITER RESULT UNDEF
+            if (!emit1(JSOP_RECREATELEXICALENV))          // ITER UNDEF
                 return false;
         }
 
         // For uncaptured bindings, put them back in TDZ.
         if (!headLexicalEmitterScope->deadZoneFrameSlots(this))
             return false;
     }
 
     JumpList beq;
     JumpTarget breakTarget{ -1 };
     {
 #ifdef DEBUG
         auto loopDepth = this->stackDepth;
 #endif
 
+        if (!emit1(JSOP_POP))                             // ITER
+            return false;
+        if (!emit1(JSOP_DUP))                             // ITER ITER
+            return false;
+
+        if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter))
+            return false;                                 // ITER RESULT
+
+        if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT DONE
+            return false;
+
+        IfThenElseEmitter ifDone(this);
+
+        if (!ifDone.emitIf())                             // ITER RESULT
+            return false;
+
+        // Remove RESULT from the stack to release it.
+        if (!emit1(JSOP_POP))                             // ITER
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                       // ITER UNDEF
+            return false;
+
+        // If the iteration is done, leave loop here, instead of the branch at
+        // the end of the loop.
+        if (!loopInfo.emitSpecialBreakForDone(this))      // ITER UNDEF
+            return false;
+
+        if (!ifDone.emitEnd())                            // ITER RESULT
+            return false;
+
         // Emit code to assign result.value to the iteration variable.
         //
         // Note that ES 13.7.5.13, step 5.c says getting result.value does not
         // call IteratorClose, so start JSTRY_ITERCLOSE after the GETPROP.
-        if (!emit1(JSOP_POP))                             // ITER RESULT
-            return false;
-        if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE
+        if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER VALUE
             return false;
 
         if (!loopInfo.emitBeginCodeNeedingIteratorClose(this))
             return false;
 
-        if (!emitInitializeForInOrOfTarget(forOfHead))    // ITER RESULT VALUE
+        if (!emitInitializeForInOrOfTarget(forOfHead))    // ITER VALUE
             return false;
 
         MOZ_ASSERT(stackDepth == loopDepth,
                    "the stack must be balanced around the initializing "
                    "operation");
 
         // Remove VALUE from the stack to release it.
-        if (!emit1(JSOP_POP))                             // ITER RESULT
-            return false;
-        if (!emit1(JSOP_UNDEFINED))                       // ITER RESULT UNDEF
+        if (!emit1(JSOP_POP))                             // ITER
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                       // ITER UNDEF
             return false;
 
         // Perform the loop body.
         ParseNode* forBody = forOfLoop->pn_right;
-        if (!emitTree(forBody))                           // ITER RESULT UNDEF
+        if (!emitTree(forBody))                           // ITER UNDEF
             return false;
 
         MOZ_ASSERT(stackDepth == loopDepth,
                    "the stack must be balanced around the for-of body");
 
         if (!loopInfo.emitEndCodeNeedingIteratorClose(this))
             return false;
 
         // Set offset for continues.
         loopInfo.continueTarget = { offset() };
 
-        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER RESULT UNDEF
-            return false;
-
-        if (!emit1(JSOP_SWAP))                            // ITER UNDEF RESULT
-            return false;
-        if (!emit1(JSOP_POP))                             // ITER UNDEF
-            return false;
-        if (!emitDupAt(1))                                // ITER UNDEF ITER
-            return false;
-
-        if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) // ITER UNDEF RESULT
-            return false;
-
-        if (!emit1(JSOP_SWAP))                            // ITER RESULT UNDEF
-            return false;
-
-        if (!emitDupAt(1))                                // ITER RESULT UNDEF RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT UNDEF DONE
-            return false;
-
+        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER UNDEF
+            return false;
+
+        if (!emit1(JSOP_FALSE))                           // ITER UNDEF FALSE
+            return false;
         if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
-            return false;                                 // ITER RESULT UNDEF
+            return false;                                 // ITER UNDEF
 
         MOZ_ASSERT(this->stackDepth == loopDepth);
     }
 
     // Let Ion know where the closing jump of this loop is.
     if (!setSrcNoteOffset(noteIndex, 0, beq.offset - initialJump.offset))
         return false;
 
     if (!loopInfo.patchBreaksAndContinues(this))
         return false;
 
     if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
         return false;
 
-    return emitPopN(3);                                   //
+    return emitPopN(2);                                   //
 }
 
 bool
 BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitterScope)
 {
     MOZ_ASSERT(forInLoop->isKind(PNK_FOR));
     MOZ_ASSERT(forInLoop->isArity(PN_BINARY));
     MOZ_ASSERT(forInLoop->isOp(JSOP_ITER));
@@ -7561,19 +7583,17 @@ BytecodeEmitter::emitComprehensionForOf(
 
     // Evaluate the expression to the right of 'of'.
     if (!emitTree(forHeadExpr))                // EXPR
         return false;
     if (!emitIterator())                       // ITER
         return false;
 
     // Push a dummy result so that we properly enter iteration midstream.
-    if (!emit1(JSOP_UNDEFINED))                // ITER RESULT
-        return false;
-    if (!emit1(JSOP_UNDEFINED))                // ITER RESULT VALUE
+    if (!emit1(JSOP_UNDEFINED))                // ITER VALUE
         return false;
 
     // Enter the block before the loop body, after evaluating the obj.
     // Initialize let bindings with undefined when entering, as the name
     // assigned to is a plain assignment.
     TDZCheckCache tdzCache(this);
     Maybe<EmitterScope> emitterScope;
     ParseNode* loopVariableName;
@@ -7601,71 +7621,84 @@ BytecodeEmitter::emitComprehensionForOf(
     JumpTarget top{ -1 };
     if (!emitLoopHead(nullptr, &top))
         return false;
 
 #ifdef DEBUG
     int loopDepth = this->stackDepth;
 #endif
 
-    // Emit code to assign result.value to the iteration variable.
-    if (!emit1(JSOP_POP))                                 // ITER RESULT
+    if (!emit1(JSOP_POP))                                 // ITER
+        return false;
+    if (!emit1(JSOP_DUP))                                 // ITER ITER
+        return false;
+    if (!emitIteratorNext(forHead))                       // ITER RESULT
         return false;
     if (!emit1(JSOP_DUP))                                 // ITER RESULT RESULT
         return false;
-    if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // ITER RESULT VALUE
+    if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER RESULT DONE
+        return false;
+
+    IfThenElseEmitter ifDone(this);
+
+    if (!ifDone.emitIf())                                 // ITER RESULT
+        return false;
+
+    // Remove RESULT from the stack to release it.
+    if (!emit1(JSOP_POP))                                 // ITER
+        return false;
+    if (!emit1(JSOP_UNDEFINED))                           // ITER UNDEF
+        return false;
+
+    // If the iteration is done, leave loop here, instead of the branch at
+    // the end of the loop.
+    if (!loopInfo.emitSpecialBreakForDone(this))          // ITER UNDEF
+        return false;
+
+    if (!ifDone.emitEnd())                                // ITER RESULT
+        return false;
+
+    // Emit code to assign result.value to the iteration variable.
+    if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // ITER VALUE
         return false;
 
     // Notice: Comprehension for-of doesn't perform IteratorClose, since it's
     // not in the spec.
-
-    if (!emitAssignment(loopVariableName, JSOP_NOP, nullptr)) // ITER RESULT VALUE
+    if (!emitAssignment(loopVariableName, JSOP_NOP, nullptr)) // ITER VALUE
         return false;
 
     // Remove VALUE from the stack to release it.
-    if (!emit1(JSOP_POP))                                 // ITER RESULT
-        return false;
-    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT UNDEF
+    if (!emit1(JSOP_POP))                                 // ITER
+        return false;
+    if (!emit1(JSOP_UNDEFINED))                           // ITER UNDEF
         return false;
 
     // The stack should be balanced around the assignment opcode sequence.
     MOZ_ASSERT(this->stackDepth == loopDepth);
 
     // Emit code for the loop body.
-    if (!emitTree(forBody))                               // ITER RESULT UNDEF
+    if (!emitTree(forBody))                               // ITER UNDEF
         return false;
 
     // The stack should be balanced around the assignment opcode sequence.
     MOZ_ASSERT(this->stackDepth == loopDepth);
 
     // Set offset for continues.
     loopInfo.continueTarget = { offset() };
 
     if (!emitLoopEntry(forHeadExpr, jmp))
         return false;
 
-    if (!emit1(JSOP_SWAP))                                // ITER UNDEF RESULT
-        return false;
-    if (!emit1(JSOP_POP))                                 // ITER UNDEF
-        return false;
-    if (!emitDupAt(1))                                    // ITER UNDEF ITER
-        return false;
-    if (!emitIteratorNext(forHead))                       // ITER UNDEF RESULT
-        return false;
-    if (!emit1(JSOP_SWAP))                                // ITER RESULT UNDEF
-        return false;
-    if (!emitDupAt(1))                                    // ITER RESULT UNDEF RESULT
-        return false;
-    if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER RESULT UNDEF DONE
+    if (!emit1(JSOP_FALSE))                               // ITER VALUE FALSE
         return false;
 
     JumpList beq;
     JumpTarget breakTarget{ -1 };
-    if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER RESULT UNDEF
-        return false;
+    if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
+        return false;                                     // ITER VALUE
 
     MOZ_ASSERT(this->stackDepth == loopDepth);
 
     // Let Ion know where the closing jump of this loop is.
     if (!setSrcNoteOffset(noteIndex, 0, beq.offset - jmp.offset))
         return false;
 
     if (!loopInfo.patchBreaksAndContinues(this))
@@ -7676,17 +7709,17 @@ BytecodeEmitter::emitComprehensionForOf(
 
     if (emitterScope) {
         if (!emitterScope->leave(this))
             return false;
         emitterScope.reset();
     }
 
     // Pop the result and the iter.
-    return emitPopN(3);                                   //
+    return emitPopN(2);                                   //
 }
 
 bool
 BytecodeEmitter::emitComprehensionForIn(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR));
 
     ParseNode* forHead = pn->pn_left;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5968,21 +5968,16 @@ Parser<ParseHandler>::forHeadStart(Yield
     *forInitialPart = expr(InProhibited, yieldHandling, TripledotProhibited, &possibleError);
     if (!*forInitialPart)
         return false;
 
     bool isForIn, isForOf;
     if (!matchInOrOf(&isForIn, &isForOf))
         return false;
 
-    if (iterKind == IteratorKind::Async && !isForOf) {
-        error(JSMSG_FOR_AWAIT_NOT_OF);
-        return false;
-    }
-
     // If we don't encounter 'in'/'of', we have a for(;;) loop.  We've handled
     // the init expression; the caller handles the rest.  Allow the Operand
     // modifier when regetting: Operand must be used to examine the ';' in
     // |for (;|, and our caller handles this case and that.
     if (!isForIn && !isForOf) {
         if (!possibleError.checkForExpressionError())
             return false;
         *forHeadKind = PNK_FORHEAD;
@@ -6120,25 +6115,29 @@ Parser<ParseHandler>::forStatement(Yield
     if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope,
                       &iteratedExpr))
     {
         return null();
     }
 
     MOZ_ASSERT(headKind == PNK_FORIN || headKind == PNK_FOROF || headKind == PNK_FORHEAD);
 
+    if (iterKind == IteratorKind::Async && headKind != PNK_FOROF) {
+        errorAt(begin, JSMSG_FOR_AWAIT_NOT_OF);
+        return null();
+    }
+    if (isForEach && headKind != PNK_FORIN) {
+        errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
+        return null();
+    }
+
     Node forHead;
     if (headKind == PNK_FORHEAD) {
         Node init = startNode;
 
-        if (isForEach) {
-            errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
-            return null();
-        }
-
         // Look for an operand: |for (;| means we might have already examined
         // this semicolon with that modifier.
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
 
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
 
@@ -6184,21 +6183,16 @@ Parser<ParseHandler>::forStatement(Yield
         // protocol, or a string naming a property) is assigned.
         Node target = startNode;
 
         // Parse the rest of the for-in/of head.
         if (headKind == PNK_FORIN) {
             stmt.refineForKind(StatementKind::ForInLoop);
             iflags |= JSITER_ENUMERATE;
         } else {
-            if (isForEach) {
-                errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
-                return null();
-            }
-
             stmt.refineForKind(StatementKind::ForOfLoop);
         }
 
         // Parser::declaration consumed everything up to the closing ')'.  That
         // token follows an {Assignment,}Expression, so the next token must be
         // consumed as if an operator continued the expression, i.e. as None.
         MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::None, JSMSG_PAREN_AFTER_FOR_CTRL);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1348777.js
@@ -0,0 +1,18 @@
+
+if (typeof TypedObject === 'undefined')
+    quit();
+
+var uint8 = TypedObject.uint8;
+function check(v) {
+    return v.toSource();
+}
+function test() {
+    var fake1 = {};
+    var fake2 = [];
+    fake2.toSource = uint8;
+    var a = [fake1, fake2];
+    for (var i = 0; i < 1000; i++) try {
+        check(a[i % 2]);
+    } catch (e) {}
+}
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/builtin.js
@@ -0,0 +1,132 @@
+let values = [-Infinity, Infinity, NaN, 0, -0, -0.1, 0.1, 0.5, -1.6, 1.6, 13.37];
+
+function unary(name) {
+    print(name);
+
+    let imports = {
+        math: {
+            func: Math[name],
+        }
+    };
+
+    let f32 = x => Math.fround(Math[name](Math.fround(x)));
+    let f64 = Math[name];
+
+    let i = wasmEvalText(`(module
+        (import $f32 "math" "func" (param f32) (result f32))
+        (import $f64 "math" "func" (param f64) (result f64))
+
+        (table $t 10 anyfunc)
+        (type $f_f (func (param f32) (result f32)))
+        (type $d_d (func (param f64) (result f64)))
+        (elem (i32.const 0) $f32 $f64)
+
+        (func (export "f32") (param f32) (result f32)
+            get_local 0
+            call $f32
+        )
+        (func (export "f32_t") (param f32) (result f32)
+            get_local 0
+            i32.const 0
+            call_indirect $f_f
+        )
+        (func (export "f64") (param f64) (result f64)
+            get_local 0
+            call $f64
+        )
+        (func (export "f64_t") (param f64) (result f64)
+            get_local 0
+            i32.const 1
+            call_indirect $d_d
+        )
+    )`, imports).exports;
+
+    for (let v of values) {
+        assertEq(i.f32(v), f32(v));
+        assertEq(i.f32_t(v), f32(v));
+        assertEq(i.f64(v), f64(v));
+        assertEq(i.f64_t(v), f64(v));
+    }
+}
+
+function binary(name) {
+    print(name);
+
+    let imports = {
+        math: {
+            func: Math[name]
+        }
+    };
+
+    let f32 = (x, y) => Math.fround(Math[name](Math.fround(x), Math.fround(y)));
+    let f64 = Math[name];
+
+    let i = wasmEvalText(`(module
+        (import $f32 "math" "func" (param f32) (param f32) (result f32))
+        (import $f64 "math" "func" (param f64) (param f64) (result f64))
+
+        (table $t 10 anyfunc)
+        (type $ff_f (func (param f32) (param f32) (result f32)))
+        (type $dd_d (func (param f64) (param f64) (result f64)))
+        (elem (i32.const 0) $f32 $f64)
+
+        (func (export "f32") (param f32) (param f32) (result f32)
+            get_local 0
+            get_local 1
+            call $f32
+        )
+        (func (export "f32_t") (param f32) (param f32) (result f32)
+            get_local 0
+            get_local 1
+            i32.const 0
+            call_indirect $ff_f
+        )
+        (func (export "f64") (param f64) (param f64) (result f64)
+            get_local 0
+            get_local 1
+            call $f64
+        )
+        (func (export "f64_t") (param f64) (param f64) (result f64)
+            get_local 0
+            get_local 1
+            i32.const 1
+            call_indirect $dd_d
+        )
+    )`, imports).exports;
+
+    for (let v of values) {
+        for (let w of values) {
+            assertEq(i.f32(v, w), f32(v, w));
+            assertEq(i.f64(v, w), f64(v, w));
+        }
+    }
+}
+
+unary('sin');
+unary('sin');
+unary('tan');
+unary('cos');
+unary('exp');
+unary('log');
+unary('asin');
+unary('atan');
+unary('acos');
+unary('log10');
+unary('log2');
+unary('log1p');
+unary('expm1');
+unary('sinh');
+unary('tanh');
+unary('cosh');
+unary('asinh');
+unary('atanh');
+unary('acosh');
+unary('sign');
+unary('trunc');
+unary('cbrt');
+
+binary('atan2');
+binary('hypot');
+binary('pow');
+
+print('done');
--- a/js/src/jit-test/tests/wasm/profiling.js
+++ b/js/src/jit-test/tests/wasm/profiling.js
@@ -8,20 +8,20 @@ try {
 
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 const Table = WebAssembly.Table;
 
 function normalize(stack)
 {
     var wasmFrameTypes = [
-        {re:/^entry trampoline \(in wasm\)$/,             sub:">"},
-        {re:/^wasm-function\[(\d+)\] \(.*\)$/,            sub:"$1"},
-        {re:/^(fast|slow) FFI trampoline \(in wasm\)$/,   sub:"<"},
-        {re:/ \(in wasm\)$/,                              sub:""}
+        {re:/^entry trampoline \(in wasm\)$/,                        sub:">"},
+        {re:/^wasm-function\[(\d+)\] \(.*\)$/,                       sub:"$1"},
+        {re:/^(fast|slow) FFI trampoline (to native |)\(in wasm\)$/, sub:"<"},
+        {re:/ \(in wasm\)$/,                                         sub:""}
     ];
 
     var framesIn = stack.split(',');
     var framesOut = [];
     for (let frame of framesIn) {
         for (let {re, sub} of wasmFrameTypes) {
             if (re.test(frame)) {
                 framesOut.push(frame.replace(re, sub));
@@ -108,16 +108,26 @@ test(
     (import $foo "" "foo")
     (table anyfunc (elem $foo))
     (func $bar (call_indirect 0 (i32.const 0)))
     (export "" $bar)
 )`,
 {"":{foo:()=>{}}},
 ["", ">", "1,>", "0,1,>", "<,0,1,>", "0,1,>", "1,>", ">", ""]);
 
+test(`(module
+    (import $f32 "Math" "sin" (param f32) (result f32))
+    (func (export "") (param f32) (result f32)
+        get_local 0
+        call $f32
+    )
+)`,
+this,
+["", ">", "1,>", "<,1,>", "1,>", ">", ""]);
+
 function testError(code, error, expect)
 {
     enableGeckoProfiling();
     var f = wasmEvalText(code).exports[""];
     enableSingleStepProfiling();
     assertThrowsInstanceOf(f, error);
     assertEqStacks(disableSingleStepProfiling(), expect);
     disableGeckoProfiling();
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -511,20 +511,19 @@ HasLiveStackValueAtDepth(JSScript* scrip
         switch (tn->kind) {
           case JSTRY_FOR_IN:
             // For-in loops have only the iterator on stack.
             if (stackDepth == tn->stackDepth)
                 return true;
             break;
 
           case JSTRY_FOR_OF:
-            // For-of loops have the iterator, the result object, and the value
-            // of the result object on stack. The iterator is below the result
-            // object and the value.
-            if (stackDepth == tn->stackDepth - 2)
+            // For-of loops have the iterator and the result.value on stack.
+            // The iterator is below the result.value.
+            if (stackDepth == tn->stackDepth - 1)
                 return true;
             break;
 
           case JSTRY_DESTRUCTURING_ITERCLOSE:
             // Destructuring code that need to call IteratorClose have both
             // the iterator and the "done" value on the stack.
             if (stackDepth == tn->stackDepth || stackDepth == tn->stackDepth - 1)
                 return true;
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -932,17 +932,17 @@ ControlFlowGenerator::processWhileOrForI
     // Verify that the IFNE goes back to a loophead op.
     MOZ_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
     MOZ_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
 
     jsbytecode* loopEntry = pc + GetJumpOffset(pc);
 
     size_t stackPhiCount;
     if (SN_TYPE(sn) == SRC_FOR_OF)
-        stackPhiCount = 3;
+        stackPhiCount = 2;
     else if (SN_TYPE(sn) == SRC_FOR_IN)
         stackPhiCount = 1;
     else
         stackPhiCount = 0;
 
     // Skip past the JSOP_LOOPHEAD for the body start.
     jsbytecode* loopHead = GetNextPc(pc);
     jsbytecode* bodyStart = GetNextPc(loopHead);
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -764,21 +764,21 @@ PropertyNameToExtraName(PropertyName* na
 #define TRACK_SNAPSHOTS 1
 
 // Make sure registers are not modified between an instruction and
 // its OsiPoint.
 #define CHECK_OSIPOINT_REGISTERS 1
 
 #endif // DEBUG
 
-enum {
+enum ABIArgType {
     ArgType_General = 0x1,
     ArgType_Double  = 0x2,
     ArgType_Float32 = 0x3,
-    ArgType_Int64 = 0x4,
+    ArgType_Int64   = 0x4,
 
     RetType_Shift   = 0x0,
     ArgType_Shift   = 0x3,
     ArgType_Mask    = 0x7
 };
 
 enum ABIFunctionType
 {
@@ -823,16 +823,19 @@ enum ABIFunctionType
     // double f(double, int)
     Args_Double_DoubleInt = Args_Double_None |
         (ArgType_General << (ArgType_Shift * 1)) |
         (ArgType_Double << (ArgType_Shift * 2)),
 
     // double f(double, double)
     Args_Double_DoubleDouble = Args_Double_Double | (ArgType_Double << (ArgType_Shift * 2)),
 
+    // float f(float, float)
+    Args_Float32_Float32Float32 = Args_Float32_Float32 | (ArgType_Float32 << (ArgType_Shift * 2)),
+
     // double f(int, double)
     Args_Double_IntDouble = Args_Double_None |
         (ArgType_Double << (ArgType_Shift * 1)) |
         (ArgType_General << (ArgType_Shift * 2)),
 
     // int f(int, double)
     Args_Int_IntDouble = Args_General0 |
         (ArgType_Double << (ArgType_Shift * 1)) |
@@ -851,17 +854,16 @@ enum ABIFunctionType
        (ArgType_Double  << (ArgType_Shift * 3)),
 
     // int f(int, double, int, int)
     Args_Int_IntDoubleIntInt = Args_General0 |
         (ArgType_General << (ArgType_Shift * 1)) |
         (ArgType_General << (ArgType_Shift * 2)) |
         (ArgType_Double  << (ArgType_Shift * 3)) |
         (ArgType_General << (ArgType_Shift * 4))
-
 };
 
 enum class BarrierKind : uint32_t {
     // No barrier is needed.
     NoBarrier,
 
     // The barrier only has to check the value's type tag is in the TypeSet.
     // Specific object types don't have to be checked.
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5419,16 +5419,21 @@ MGuardReceiverPolymorphic::congruentTo(c
 void
 InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choiceSet)
 {
     for (size_t i = 0; i < targets.length(); i++) {
         // If the target was inlined, don't erase the entry.
         if (choiceSet[i])
             continue;
 
+        // If the target wasn't a function we would have veto'ed it
+        // and it will not be in the entries list.
+        if (!targets[i]->is<JSFunction>())
+			continue;
+
         JSFunction* target = &targets[i]->as<JSFunction>();
 
         // Eliminate all entries containing the vetoed function from the map.
         size_t j = 0;
         while (j < numEntries()) {
             if (entries_[j]->func == target)
                 entries_.erase(&entries_[j]);
             else
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -2286,16 +2286,29 @@ StackDecrementForCall(uint32_t alignment
 }
 
 static inline MIRType
 ToMIRType(MIRType t)
 {
     return t;
 }
 
+static inline MIRType
+ToMIRType(ABIArgType argType)
+{
+    switch (argType & ArgType_Mask) {
+      case ArgType_General: return MIRType::Int32;
+      case ArgType_Double:  return MIRType::Double;
+      case ArgType_Float32: return MIRType::Float32;
+      case ArgType_Int64:   return MIRType::Int64;
+      default: break;
+    }
+    MOZ_CRASH("unexpected argType");
+}
+
 template <class VecT>
 class ABIArgIter
 {
     ABIArgGenerator gen_;
     const VecT& types_;
     unsigned i_;
 
     void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); }
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -2441,16 +2441,17 @@ typedef double (*Prototype_Double_Double
 typedef double (*Prototype_Double_Int)(int32_t arg0);
 typedef double (*Prototype_Double_IntInt)(int32_t arg0, int32_t arg1);
 typedef int32_t (*Prototype_Int_Double)(double arg0);
 typedef int64_t (*Prototype_Int64_Double)(double arg0);
 typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t arg2);
 typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
                                                  int32_t arg3);
 typedef float (*Prototype_Float32_Float32)(float arg0);
+typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
 typedef float (*Prototype_Float32_IntInt)(int arg0, int arg1);
 
 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
 typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
 
 typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2);
@@ -2629,16 +2630,31 @@ Simulator::softwareInterrupt(SimInstruct
             else
                 fval0 = mozilla::BitwiseCast<float>(arg0);
             Prototype_Float32_Float32 target = reinterpret_cast<Prototype_Float32_Float32>(external);
             float fresult = target(fval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultFloat(fresult);
             break;
           }
+          case Args_Float32_Float32Float32: {
+            float fval0, fval1;
+            if (UseHardFpABI()) {
+                get_float_from_s_register(0, &fval0);
+                get_float_from_s_register(1, &fval1);
+            } else {
+                fval0 = mozilla::BitwiseCast<float>(arg0);
+                fval1 = mozilla::BitwiseCast<float>(arg1);
+            }
+            Prototype_Float32_Float32Float32 target = reinterpret_cast<Prototype_Float32_Float32Float32>(external);
+            float fresult = target(fval0, fval1);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResultFloat(fresult);
+            break;
+          }
           case Args_Float32_IntInt: {
             Prototype_Float32_IntInt target = reinterpret_cast<Prototype_Float32_IntInt>(external);
             float fresult = target(arg0, arg1);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultFloat(fresult);
             break;
           }
           case Args_Double_Int: {
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -156,16 +156,24 @@ IsCompilingWasm()
 }
 #endif
 
 // Pointer to be embedded as an immediate in an instruction.
 struct ImmPtr
 {
     void* value;
 
+    struct NoCheckToken {};
+
+    explicit ImmPtr(void* value, NoCheckToken) : value(value)
+    {
+        // A special unchecked variant for contexts where we know it is safe to
+        // use an immptr. This is assuming the caller knows what they're doing.
+    }
+
     explicit ImmPtr(const void* value) : value(const_cast<void*>(value))
     {
         // To make code serialization-safe, wasm compilation should only
         // compile pointer immediates using a SymbolicAddress.
         MOZ_ASSERT(!IsCompilingWasm());
     }
 
     template <class R>
@@ -197,17 +205,16 @@ struct ImmPtr
     }
 
     template <class R, class A1, class A2, class A3, class A4>
     explicit ImmPtr(R (*pf)(A1, A2, A3, A4))
       : value(JS_FUNC_TO_DATA_PTR(void*, pf))
     {
         MOZ_ASSERT(!IsCompilingWasm());
     }
-
 };
 
 // The same as ImmPtr except that the intention is to patch this
 // instruction. The initial value of the immediate is 'addr' and this value is
 // either clobbered or used in the patching process.
 struct PatchedImmPtr {
     void* value;
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -589,12 +589,12 @@ MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_N
 MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE,     0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
 MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
 
 // Iterator
 MSG_DEF(JSMSG_RETURN_NOT_CALLABLE,     0, JSEXN_TYPEERR, "property 'return' of iterator is not callable")
 MSG_DEF(JSMSG_ITERATOR_NO_THROW,       0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
 
 // Async Iteration
-MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF,        0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
+MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF,        0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
 MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR,  0, JSEXN_TYPEERR, "Not an async generator")
 MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR,   0, JSEXN_TYPEERR, "Not an async from sync iterator")
 MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -371,16 +371,17 @@ UNIFIED_SOURCES += [
     'wasm/WasmCompartment.cpp',
     'wasm/WasmCompile.cpp',
     'wasm/WasmFrameIterator.cpp',
     'wasm/WasmGenerator.cpp',
     'wasm/WasmInstance.cpp',
     'wasm/WasmIonCompile.cpp',
     'wasm/WasmJS.cpp',
     'wasm/WasmModule.cpp',
+    'wasm/WasmRuntime.cpp',
     'wasm/WasmSignalHandlers.cpp',
     'wasm/WasmStubs.cpp',
     'wasm/WasmTable.cpp',
     'wasm/WasmTextToBinary.cpp',
     'wasm/WasmTextUtils.cpp',
     'wasm/WasmTypes.cpp',
     'wasm/WasmValidate.cpp'
 ]
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_2018/AsyncGenerators/for-await-bad-syntax.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(release_or_beta)
+
+var AsyncGenerator = async function*(){}.constructor;
+
+function assertSyntaxError(code) {
+    var functionCode = `async function* f() { ${code} }`;
+    assertThrowsInstanceOf(() => AsyncGenerator(code), SyntaxError, "AsyncGenerator:" + code);
+    assertThrowsInstanceOf(() => eval(functionCode), SyntaxError, "eval:" + functionCode);
+    var ieval = eval;
+    assertThrowsInstanceOf(() => ieval(functionCode), SyntaxError, "indirect eval:" + functionCode);
+}
+
+assertSyntaxError(`for await (;;) ;`);
+
+for (var decl of ["", "var", "let", "const"]) {
+    for (var head of ["a", "a = 0", "a, b", "[a]", "[a] = 0", "{a}", "{a} = 0"]) {
+        // Ends with C-style for loop syntax.
+        assertSyntaxError(`for await (${decl} ${head} ;;) ;`);
+
+        // Ends with for-in loop syntax.
+        assertSyntaxError(`for await (${decl} ${head} in null) ;`);
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
new file mode 100644
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -865,17 +865,16 @@ skip-if(!xulRuntime.shell) script test26
 # Tests disabled due to invalid test expectations  #
 ####################################################
 
 # https://github.com/tc39/test262/pull/947
 skip script test262/language/statements/async-generator/yield-star-async-next.js
 skip script test262/language/statements/async-generator/yield-star-async-return.js
 skip script test262/language/statements/async-generator/yield-star-async-throw.js
 skip script test262/language/module-code/namespace/internals/delete-non-exported.js
-
-# https://github.com/tc39/test262/pull/947
 skip script test262/intl402/NumberFormat/11.1.1_32.js
+skip script test262/intl402/DateTimeFormat/prototype/formatToParts/length.js
+skip script test262/intl402/PluralRules/this-not-ignored.js
 
-# https://github.com/tc39/test262/pull/947
-skip script test262/intl402/DateTimeFormat/prototype/formatToParts/length.js
-
-# https://github.com/tc39/test262/pull/947
-skip script test262/intl402/PluralRules/this-not-ignored.js
+# https://github.com/tc39/test262/pull/961
+skip script test262/language/statements/async-generator/yield-star-sync-return.js
+skip script test262/language/statements/async-generator/yield-star-sync-throw.js
+skip script test262/language/statements/async-generator/yield-star-sync-next.js
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -262,28 +262,33 @@ JSRuntime::init(JSContext* cx, uint32_t 
         sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
         if (!sharedImmutableStrings_)
             return false;
     }
 
     if (!caches().init())
         return false;
 
+    if (!wasm().init())
+        return false;
+
     return true;
 }
 
 void
 JSRuntime::destroyRuntime()
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
     MOZ_ASSERT(childRuntimeCount == 0);
     MOZ_ASSERT(initialized_);
 
     sharedIntlData.ref().destroyInstance();
 
+    wasm().destroy();
+
     if (gcInitialized) {
         /*
          * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
          * list is empty in CancelOffThreadParses.
          */
         JSContext* cx = TlsContext.get();
         if (JS::IsIncrementalGCInProgress(cx))
             FinishGC(cx);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -49,16 +49,17 @@
 #include "vm/DateTime.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/MallocProvider.h"
 #include "vm/Scope.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Stack.h"
 #include "vm/Stopwatch.h"
 #include "vm/Symbol.h"
+#include "wasm/WasmRuntime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
 namespace js {
 
@@ -530,16 +531,23 @@ struct JSRuntime : public js::MallocProv
 
   public:
     js::UnprotectedData<JS::BuildIdOp> buildIdOp;
 
     /* AsmJSCache callbacks are runtime-wide. */
     js::UnprotectedData<JS::AsmJSCacheOps> asmJSCacheOps;
 
   private:
+    // All runtime data needed for wasm and defined in wasm/WasmRuntime.h.
+    js::ActiveThreadData<js::wasm::Runtime> wasmRuntime_;
+
+  public:
+    js::wasm::Runtime& wasm() { return wasmRuntime_.ref(); }
+
+  private:
     js::UnprotectedData<const JSPrincipals*> trustedPrincipals_;
   public:
     void setTrustedPrincipals(const JSPrincipals* p) { trustedPrincipals_ = p; }
     const JSPrincipals* trustedPrincipals() const { return trustedPrincipals_; }
 
     js::ActiveThreadData<const JSWrapObjectCallbacks*> wrapObjectCallbacks;
     js::ActiveThreadData<js::PreserveWrapperCallback> preserveWrapperCallback;
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1832,16 +1832,17 @@ void
 JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
 {
     MOZ_ASSERT(!done());
     MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
 
     if (activation_->isWasm()) {
         new (storage()) wasm::ProfilingFrameIterator(*activation_->asWasm(), state);
         // Set savedPrevJitTop_ to the actual jitTop_ from the runtime.
+        AutoNoteSingleThreadedRegion anstr;
         savedPrevJitTop_ = activation_->cx()->jitTop;
         return;
     }
 
     MOZ_ASSERT(activation_->asJit()->isActive());
     new (storage()) jit::JitProfilingFrameIterator(cx_, state);
 }
 
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -106,17 +106,18 @@ StaticallyLink(CodeSegment& cs, const Li
         else
             Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target));
     }
 
     for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) {
         const Uint32Vector& offsets = linkData.symbolicLinks[imm];
         for (size_t i = 0; i < offsets.length(); i++) {
             uint8_t* patchAt = cs.base() + offsets[i];
-            void* target = AddressOf(imm);
+            ABIFunctionType unused;
+            void* target = wasm::AddressOf(imm, &unused);
             Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
                                                PatchedImmPtr(target),
                                                PatchedImmPtr((void*)-1));
         }
     }
 }
 
 static void
@@ -290,72 +291,16 @@ FuncImport::deserialize(const uint8_t* c
 }
 
 size_t
 FuncImport::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return sig_.sizeOfExcludingThis(mallocSizeOf);
 }
 
-CodeRange::CodeRange(Kind kind, Offsets offsets)
-  : begin_(offsets.begin),
-    ret_(0),
-    end_(offsets.end),
-    funcIndex_(0),
-    funcLineOrBytecode_(0),
-    funcBeginToNormalEntry_(0),
-    kind_(kind)
-{
-    MOZ_ASSERT(begin_ <= end_);
-#ifdef DEBUG
-    switch (kind_) {
-      case Entry:
-      case DebugTrap:
-      case FarJumpIsland:
-      case Inline:
-      case Throw:
-      case Interrupt:
-        break;
-      case Function:
-      case TrapExit:
-      case ImportJitExit:
-      case ImportInterpExit:
-        MOZ_CRASH("should use more specific constructor");
-    }
-#endif
-}
-
-CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
-  : begin_(offsets.begin),
-    ret_(offsets.ret),
-    end_(offsets.end),
-    funcIndex_(0),
-    funcLineOrBytecode_(0),
-    funcBeginToNormalEntry_(0),
-    kind_(kind)
-{
-    MOZ_ASSERT(begin_ < ret_);
-    MOZ_ASSERT(ret_ < end_);
-    MOZ_ASSERT(kind_ == ImportJitExit || kind_ == ImportInterpExit || kind_ == TrapExit);
-}
-
-CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
-  : begin_(offsets.begin),
-    ret_(offsets.ret),
-    end_(offsets.end),
-    funcIndex_(funcIndex),
-    funcLineOrBytecode_(funcLineOrBytecode),
-    funcBeginToNormalEntry_(offsets.normalEntry - begin_),
-    kind_(Function)
-{
-    MOZ_ASSERT(begin_ < ret_);
-    MOZ_ASSERT(ret_ < end_);
-    MOZ_ASSERT(offsets.normalEntry - begin_ <= UINT8_MAX);
-}
-
 static size_t
 StringLengthWithNullChar(const char* chars)
 {
     return chars ? strlen(chars) + 1 : 0;
 }
 
 size_t
 CacheableChars::serializedSize() const
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -213,129 +213,16 @@ class FuncImport
         return pod.jitExitCodeOffset_;
     }
 
     WASM_DECLARE_SERIALIZABLE(FuncImport)
 };
 
 typedef Vector<FuncImport, 0, SystemAllocPolicy> FuncImportVector;
 
-// A CodeRange describes a single contiguous range of code within a wasm
-// module's code segment. A CodeRange describes what the code does and, for
-// function bodies, the name and source coordinates of the function.
-
-class CodeRange
-{
-  public:
-    enum Kind {
-        Function,          // function definition
-        Entry,             // calls into wasm from C++
-        ImportJitExit,     // fast-path calling from wasm into JIT code
-        ImportInterpExit,  // slow-path calling from wasm into C++ interp
-        TrapExit,          // calls C++ to report and jumps to throw stub
-        DebugTrap,         // calls C++ to handle debug event
-        FarJumpIsland,     // inserted to connect otherwise out-of-range insns
-        Inline,            // stub that is jumped-to within prologue/epilogue
-        Throw,             // special stack-unwinding stub
-        Interrupt          // stub executes asynchronously to interrupt wasm
-    };
-
-  private:
-    // All fields are treated as cacheable POD:
-    uint32_t begin_;
-    uint32_t ret_;
-    uint32_t end_;
-    uint32_t funcIndex_;
-    uint32_t funcLineOrBytecode_;
-    uint8_t funcBeginToNormalEntry_;
-    Kind kind_ : 8;
-
-  public:
-    CodeRange() = default;
-    CodeRange(Kind kind, Offsets offsets);
-    CodeRange(Kind kind, CallableOffsets offsets);
-    CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
-
-    // All CodeRanges have a begin and end.
-
-    uint32_t begin() const {
-        return begin_;
-    }
-    uint32_t end() const {
-        return end_;
-    }
-
-    // Other fields are only available for certain CodeRange::Kinds.
-
-    Kind kind() const {
-        return kind_;
-    }
-
-    bool isFunction() const {
-        return kind() == Function;
-    }
-    bool isImportExit() const {
-        return kind() == ImportJitExit || kind() == ImportInterpExit;
-    }
-    bool isTrapExit() const {
-        return kind() == TrapExit;
-    }
-    bool isInline() const {
-        return kind() == Inline;
-    }
-    bool isThunk() const {
-        return kind() == FarJumpIsland;
-    }
-
-    // Every CodeRange except entry and inline stubs are callable and have a
-    // return statement. Asynchronous frame iteration needs to know the offset
-    // of the return instruction to calculate the frame pointer.
-
-    uint32_t ret() const {
-        MOZ_ASSERT(isFunction() || isImportExit() || isTrapExit());
-        return ret_;
-    }
-
-    // Function CodeRanges have two entry points: one for normal calls (with a
-    // known signature) and one for table calls (which involves dynamic
-    // signature checking).
-
-    uint32_t funcTableEntry() const {
-        MOZ_ASSERT(isFunction());
-        return begin_;
-    }
-    uint32_t funcNormalEntry() const {
-        MOZ_ASSERT(isFunction());
-        return begin_ + funcBeginToNormalEntry_;
-    }
-    uint32_t funcIndex() const {
-        MOZ_ASSERT(isFunction());
-        return funcIndex_;
-    }
-    uint32_t funcLineOrBytecode() const {
-        MOZ_ASSERT(isFunction());
-        return funcLineOrBytecode_;
-    }
-
-    // A sorted array of CodeRanges can be looked up via BinarySearch and PC.
-
-    struct PC {
-        size_t offset;
-        explicit PC(size_t offset) : offset(offset) {}
-        bool operator==(const CodeRange& rhs) const {
-            return offset >= rhs.begin() && offset < rhs.end();
-        }
-        bool operator<(const CodeRange& rhs) const {
-            return offset < rhs.begin();
-        }
-    };
-};
-
-WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
-
 // A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
 // shared memory (SharedArrayBuffer).
 
 enum class MemoryUsage
 {
     None = false,
     Unshared = 1,
     Shared = 2
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -548,16 +548,17 @@ ProfilingFrameIterator::initFromExitFP()
       case CodeRange::Function:
         fp = CallerFPFromFP(fp);
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         break;
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
+      case CodeRange::ImportNativeExit:
       case CodeRange::TrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::Inline:
       case CodeRange::Throw:
       case CodeRange::Interrupt:
       case CodeRange::FarJumpIsland:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
@@ -586,56 +587,70 @@ ProfilingFrameIterator::ProfilingFrameIt
     if (activation.exitFP()) {
         initFromExitFP();
         return;
     }
 
     // If pc isn't in the instance's code, we must have exited the code via an
     // exit trampoline or signal handler.
     code_ = activation_->compartment()->wasm.lookupCode(state.pc);
+
+    const CodeRange* codeRange = nullptr;
+    uint8_t* codeBase = nullptr;
     if (!code_) {
-        MOZ_ASSERT(done());
-        return;
+        // Optimized builtin exits (see MaybeGetMatchingBuiltin in
+        // WasmInstance.cpp) are outside module's code.
+        AutoNoteSingleThreadedRegion anstr;
+        if (BuiltinThunk* thunk = activation_->cx()->runtime()->wasm().lookupBuiltin(state.pc)) {
+            codeRange = &thunk->codeRange;
+            codeBase = (uint8_t*) thunk->base;
+        } else {
+            MOZ_ASSERT(done());
+            return;
+        }
+    } else {
+        codeRange = code_->lookupRange(state.pc);
+        codeBase = code_->segment().base();
     }
 
     // When the pc is inside the prologue/epilogue, the innermost call's Frame
     // is not complete and thus fp points to the second-to-innermost call's
     // Frame. Since fp can only tell you about its caller, naively unwinding
     // while pc is in the prologue/epilogue would skip the second-to-innermost
     // call. To avoid this problem, we use the static structure of the code in
     // the prologue and epilogue to do the Right Thing.
     uint8_t* fp = (uint8_t*)state.fp;
     uint8_t* pc = (uint8_t*)state.pc;
     void** sp = (void**)state.sp;
 
-    const CodeRange* codeRange = code_->lookupRange(pc);
-    uint32_t offsetInModule = pc - code_->segment().base();
-    MOZ_ASSERT(offsetInModule >= codeRange->begin());
-    MOZ_ASSERT(offsetInModule < codeRange->end());
+    uint32_t offsetInCode = pc - codeBase;
+    MOZ_ASSERT(offsetInCode >= codeRange->begin());
+    MOZ_ASSERT(offsetInCode < codeRange->end());
 
     // Compute the offset of the pc from the (normal) entry of the code range.
     // The stack state of the pc for the entire table-entry is equivalent to
     // that of the first pc of the normal-entry. Thus, we can simplify the below
     // case analysis by redirecting all pc-in-table-entry cases to the
     // pc-at-normal-entry case.
     uint32_t offsetFromEntry;
     if (codeRange->isFunction()) {
-        if (offsetInModule < codeRange->funcNormalEntry())
+        if (offsetInCode < codeRange->funcNormalEntry())
             offsetFromEntry = 0;
         else
-            offsetFromEntry = offsetInModule - codeRange->funcNormalEntry();
+            offsetFromEntry = offsetInCode - codeRange->funcNormalEntry();
     } else {
-        offsetFromEntry = offsetInModule - codeRange->begin();
+        offsetFromEntry = offsetInCode - codeRange->begin();
     }
 
     switch (codeRange->kind()) {
       case CodeRange::Function:
       case CodeRange::FarJumpIsland:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
+      case CodeRange::ImportNativeExit:
       case CodeRange::TrapExit:
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
         if (offsetFromEntry == BeforePushRetAddr || codeRange->isThunk()) {
             // The return address is still in lr and fp holds the caller's fp.
             callerPC_ = state.lr;
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         } else
@@ -653,21 +668,21 @@ ProfilingFrameIterator::ProfilingFrameIt
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         } else if (offsetFromEntry == PushedFP) {
             // The full Frame has been pushed; fp is still the caller's fp.
             MOZ_ASSERT(fp == CallerFPFromFP(sp));
             callerPC_ = ReturnAddressFromFP(sp);
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
-        } else if (offsetInModule == codeRange->ret() - PoppedFP) {
+        } else if (offsetInCode == codeRange->ret() - PoppedFP) {
             // The callerFP field of the Frame has been popped into fp.
             callerPC_ = sp[1];
             callerFP_ = fp;
-        } else if (offsetInModule == codeRange->ret()) {
+        } else if (offsetInCode == codeRange->ret()) {
             // Both the TLS and callerFP fields have been popped and fp now
             // points to the caller's frame.
             callerPC_ = sp[0];
             callerFP_ = fp;
             AssertMatchesCallSite(*activation_, callerPC_, callerFP_);
         } else {
             // Not in the prologue/epilogue.
             callerPC_ = ReturnAddressFromFP(fp);
@@ -735,16 +750,17 @@ ProfilingFrameIterator::operator++()
     switch (codeRange_->kind()) {
       case CodeRange::Entry:
         MOZ_ASSERT(callerFP_ == nullptr);
         callerPC_ = nullptr;
         break;
       case CodeRange::Function:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
+      case CodeRange::ImportNativeExit:
       case CodeRange::TrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::Inline:
       case CodeRange::FarJumpIsland:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*activation_, callerPC_, CallerFPFromFP(callerFP_));
         callerFP_ = CallerFPFromFP(callerFP_);
@@ -764,36 +780,40 @@ ProfilingFrameIterator::label() const
 
     // Use the same string for both time inside and under so that the two
     // entries will be coalesced by the profiler.
     //
     // NB: these labels are parsed for location by
     //     devtools/client/performance/modules/logic/frame-utils.js
     static const char* importJitDescription = "fast FFI trampoline (in wasm)";
     static const char* importInterpDescription = "slow FFI trampoline (in wasm)";
+    static const char* importNativeDescription = "fast FFI trampoline to native (in wasm)";
     static const char* trapDescription = "trap handling (in wasm)";
     static const char* debugTrapDescription = "debug trap handling (in wasm)";
 
     switch (exitReason_) {
       case ExitReason::None:
         break;
       case ExitReason::ImportJit:
         return importJitDescription;
       case ExitReason::ImportInterp:
         return importInterpDescription;
+      case ExitReason::ImportNative:
+        return importNativeDescription;
       case ExitReason::Trap:
         return trapDescription;
       case ExitReason::DebugTrap:
         return debugTrapDescription;
     }
 
     switch (codeRange_->kind()) {
       case CodeRange::Function:         return code_->profilingLabel(codeRange_->funcIndex());
       case CodeRange::Entry:            return "entry trampoline (in wasm)";
       case CodeRange::ImportJitExit:    return importJitDescription;
+      case CodeRange::ImportNativeExit: return importNativeDescription;
       case CodeRange::ImportInterpExit: return importInterpDescription;
       case CodeRange::TrapExit:         return trapDescription;
       case CodeRange::DebugTrap:        return debugTrapDescription;
       case CodeRange::Inline:           return "inline stub (in wasm)";
       case CodeRange::FarJumpIsland:    return "interstitial (in wasm)";
       case CodeRange::Throw:            MOZ_FALLTHROUGH;
       case CodeRange::Interrupt:        MOZ_CRASH("does not have a frame");
     }
--- a/js/src/wasm/WasmFrameIterator.h
+++ b/js/src/wasm/WasmFrameIterator.h
@@ -84,16 +84,17 @@ class FrameIterator
 
 // An ExitReason describes the possible reasons for leaving compiled wasm code
 // or the state of not having left compiled wasm code (ExitReason::None).
 enum class ExitReason : uint32_t
 {
     None,          // default state, the pc is in wasm code
     ImportJit,     // fast-path call directly into JIT code
     ImportInterp,  // slow-path call into C++ Invoke()
+    ImportNative,  // fast-path call directly into native C++ code
     Trap,          // call to trap handler for the trap in WasmActivation::trap
     DebugTrap      // call to debug trap handler
 };
 
 // Iterates over the frames of a single WasmActivation, given an
 // asynchronously-interrupted thread's state.
 class ProfilingFrameIterator
 {
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -409,16 +409,17 @@ ModuleGenerator::patchCallSites()
                 if (!jumps.emplaceBack(offsets.begin))
                     return false;
             }
             break;
           }
         }
     }
 
+    masm_.flushBuffer();
     return true;
 }
 
 bool
 ModuleGenerator::patchFarJumps(const TrapExitOffsetArray& trapExits, const Offsets& debugTrapStub)
 {
     for (const CallFarJump& farJump : masm_.callFarJumps())
         masm_.patchFarJump(farJump.jump, funcCodeRange(farJump.funcIndex).funcNormalEntry());
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -14,17 +14,19 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmInstance.h"
 
 #include "jit/BaselineJIT.h"
+#include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
+
 #include "wasm/WasmModule.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/ArrayBufferObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -310,16 +312,138 @@ Instance::growMemory_i32(Instance* insta
 /* static */ uint32_t
 Instance::currentMemory_i32(Instance* instance)
 {
     uint32_t byteLength = instance->memoryLength();
     MOZ_ASSERT(byteLength % wasm::PageSize == 0);
     return byteLength / wasm::PageSize;
 }
 
+// asm.js has the ability to call directly into Math builtins, which wasm can't
+// do. Instead, wasm code generators have to pass the builtins as function
+// imports, resulting in slow import calls.
+//
+// However, we can optimize this by detecting that an import is just a JSNative
+// builtin and have wasm call straight to the builtin's C++ code, since the
+// ABIs perfectly match at the call site.
+//
+// Even though we could call into float32 variants of the math functions, we
+// do not do it, so as not to change the results.
+
+#define FOREACH_UNCACHED_MATH_BUILTIN(_) \
+    _(math_sin, MathSin)           \
+    _(math_tan, MathTan)           \
+    _(math_cos, MathCos)           \
+    _(math_exp, MathExp)           \
+    _(math_log, MathLog)           \
+    _(math_asin, MathASin)         \
+    _(math_atan, MathATan)         \
+    _(math_acos, MathACos)         \
+    _(math_log10, MathLog10)       \
+    _(math_log2, MathLog2)         \
+    _(math_log1p, MathLog1P)       \
+    _(math_expm1, MathExpM1)       \
+    _(math_sinh, MathSinH)         \
+    _(math_tanh, MathTanH)         \
+    _(math_cosh, MathCosH)         \
+    _(math_asinh, MathASinH)       \
+    _(math_atanh, MathATanH)       \
+    _(math_acosh, MathACosH)       \
+    _(math_sign, MathSign)         \
+    _(math_trunc, MathTrunc)       \
+    _(math_cbrt, MathCbrt)
+
+#define UNARY_FLOAT_WRAPPER(func)                 \
+    float func##_f32(float x) {                   \
+        return float(func(double(x)));            \
+    }
+
+#define BINARY_FLOAT_WRAPPER(func)                \
+    float func##_f32(float x, float y) {          \
+        return float(func(double(x), double(y))); \
+    }
+
+#define DEFINE_FLOAT_WRAPPER(name, _) UNARY_FLOAT_WRAPPER(name##_uncached)
+FOREACH_UNCACHED_MATH_BUILTIN(DEFINE_FLOAT_WRAPPER)
+
+BINARY_FLOAT_WRAPPER(ecmaAtan2)
+BINARY_FLOAT_WRAPPER(ecmaHypot)
+BINARY_FLOAT_WRAPPER(ecmaPow)
+
+#undef DEFINE_FLOAT_WRAPPER
+#undef BINARY_FLOAT_WRAPPER
+#undef UNARY_FLOAT_WRAPPER
+
+static void*
+IsMatchingBuiltin(HandleFunction f, const Sig& sig)
+{
+    if (!f->isNative() || !f->jitInfo() || f->jitInfo()->type() != JSJitInfo::InlinableNative)
+        return nullptr;
+
+    ExprType ret = sig.ret();
+    const ValTypeVector& args = sig.args();
+
+#define UNARY_BUILTIN(double_func, float_func)                 \
+        if (args.length() != 1)                                \
+            break;                                             \
+        if (args[0] == ValType::F64 && ret == ExprType::F64)   \
+            return JS_FUNC_TO_DATA_PTR(void*, double_func);    \
+        if (args[0] == ValType::F32 && ret == ExprType::F32)   \
+            return JS_FUNC_TO_DATA_PTR(void*, float_func);     \
+        break;
+
+#define BINARY_BUILTIN(double_func, float_func)                                         \
+        if (args.length() != 2)                                                         \
+            break;                                                                      \
+        if (args[0] == ValType::F64 && args[1] == ValType::F64 && ret == ExprType::F64) \
+            return JS_FUNC_TO_DATA_PTR(void*, double_func);                             \
+        if (args[0] == ValType::F32 && args[1] == ValType::F32 && ret == ExprType::F32) \
+            return JS_FUNC_TO_DATA_PTR(void*, float_func);                              \
+        break;
+
+    switch (f->jitInfo()->inlinableNative) {
+#define MAKE_CASE(funcName, inlinableNative)                        \
+      case InlinableNative::inlinableNative:                        \
+        UNARY_BUILTIN(funcName##_uncached, funcName##_uncached_f32)
+
+      FOREACH_UNCACHED_MATH_BUILTIN(MAKE_CASE)
+
+      case InlinableNative::MathATan2:
+        BINARY_BUILTIN(ecmaAtan2, ecmaAtan2_f32)
+      case InlinableNative::MathHypot:
+        BINARY_BUILTIN(ecmaHypot, ecmaHypot_f32)
+      case InlinableNative::MathPow:
+        BINARY_BUILTIN(ecmaPow, ecmaPow_f32)
+
+      default:
+        break;
+    }
+
+#undef MAKE_CASE
+#undef UNARY_BUILTIN
+#undef BINARY_BUILTIN
+#undef FOREACH_UNCACHED_MATH_BUILTIN
+
+    return nullptr;
+}
+
+static void*
+MaybeGetMatchingBuiltin(JSContext* cx, HandleFunction f, const Sig& sig)
+{
+    void* funcPtr = IsMatchingBuiltin(f, sig);
+    if (!funcPtr)
+        return nullptr;
+
+    void* thunkPtr = nullptr;
+    if (!cx->runtime()->wasm().getBuiltinThunk(cx, funcPtr, sig, &thunkPtr))
+        return nullptr;
+
+    return thunkPtr;
+}
+
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    UniqueCode code,
                    UniqueGlobalSegment globals,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports,
                    const ValVector& globalImports)
@@ -350,16 +474,21 @@ Instance::Instance(JSContext* cx,
         if (!isAsmJS() && IsExportedWasmFunction(f)) {
             WasmInstanceObject* calleeInstanceObj = ExportedFunctionToInstanceObject(f);
             const CodeRange& codeRange = calleeInstanceObj->getExportedFunctionCodeRange(f);
             Instance& calleeInstance = calleeInstanceObj->instance();
             import.tls = calleeInstance.tlsData();
             import.code = calleeInstance.codeSegment().base() + codeRange.funcNormalEntry();
             import.baselineScript = nullptr;
             import.obj = calleeInstanceObj;
+        } else if (void* thunk = MaybeGetMatchingBuiltin(cx, f, fi.sig())) {
+            import.tls = tlsData();
+            import.code = thunk;
+            import.baselineScript = nullptr;
+            import.obj = f;
         } else {
             import.tls = tlsData();
             import.code = codeBase() + fi.interpExitCodeOffset();
             import.baselineScript = nullptr;
             import.obj = f;
         }
     }
 
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -72,31 +72,23 @@ class Instance
     SharedTableVector               tables_;
     bool                            enterFrameTrapsEnabled_;
 
     // Internal helpers:
     const void** addressOfSigId(const SigIdDesc& sigId) const;
     FuncImportTls& funcImportTls(const FuncImport& fi);
     TableTls& tableTls(const TableDesc& td) const;
 
-    // Import call slow paths which are called directly from wasm code.
-    friend void* AddressOf(SymbolicAddress);
-    static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
-    static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
-    static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
-    static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
-    static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
-    static uint32_t currentMemory_i32(Instance* instance);
-    bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
-                    MutableHandleValue rval);
-
     // Only WasmInstanceObject can call the private trace function.
     friend class js::WasmInstanceObject;
     void tracePrivate(JSTracer* trc);
 
+    bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
+                    MutableHandleValue rval);
+
   public:
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              UniqueCode code,
              UniqueGlobalSegment globals,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
@@ -160,16 +152,25 @@ class Instance
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        Table::SeenSet* seenTables,
                        size_t* code,
                        size_t* data) const;
+
+  public:
+    // Functions to be called directly from wasm code.
+    static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
+    static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
+    static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
+    static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
+    static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
+    static uint32_t currentMemory_i32(Instance* instance);
 };
 
 typedef UniquePtr<Instance> UniqueInstance;
 
 bool InitInstanceStaticData();
 void ShutDownInstanceStaticData();
 
 } // namespace wasm
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmRuntime.cpp
@@ -0,0 +1,691 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2017 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmRuntime.h"
+
+#include "mozilla/BinarySearch.h"
+
+#include "fdlibm.h"
+
+#include "jslibmath.h"
+
+#include "jit/MacroAssembler.h"
+
+#include "wasm/WasmInstance.h"
+#include "wasm/WasmStubs.h"
+
+#include "vm/Debugger-inl.h"
+#include "vm/Stack-inl.h"
+
+using namespace js;
+using namespace jit;
+using namespace wasm;
+
+using mozilla::BinarySearchIf;
+using mozilla::IsNaN;
+
+static const unsigned BUILTIN_THUNK_LIFO_SIZE = 128;
+static const CodeKind BUILTIN_THUNK_CODEKIND = CodeKind::OTHER_CODE;
+
+#if defined(JS_CODEGEN_ARM)
+extern "C" {
+
+extern MOZ_EXPORT int64_t
+__aeabi_idivmod(int, int);
+
+extern MOZ_EXPORT int64_t
+__aeabi_uidivmod(int, int);
+
+}
+#endif
+
+static void*
+WasmHandleExecutionInterrupt()
+{
+    WasmActivation* activation = JSContext::innermostWasmActivation();
+
+    // wasm::Compartment requires notification when execution is interrupted in
+    // the compartment. Only the innermost compartment has been interrupted;
+    // enclosing compartments necessarily exited through an exit stub.
+    activation->compartment()->wasm.setInterrupted(true);
+    bool success = CheckForInterrupt(activation->cx());
+    activation->compartment()->wasm.setInterrupted(false);
+
+    // Preserve the invariant that having a non-null resumePC means that we are
+    // handling an interrupt.
+    void* resumePC = activation->resumePC();
+    activation->setResumePC(nullptr);
+
+    // Return the resumePC if execution can continue or null if execution should
+    // jump to the throw stub.
+    return success ? resumePC : nullptr;
+}
+
+static bool
+WasmHandleDebugTrap()
+{
+    WasmActivation* activation = JSContext::innermostWasmActivation();
+    MOZ_ASSERT(activation);
+    JSContext* cx = activation->cx();
+
+    FrameIterator iter(activation);
+    MOZ_ASSERT(iter.debugEnabled());
+    const CallSite* site = iter.debugTrapCallsite();
+    MOZ_ASSERT(site);
+    if (site->kind() == CallSite::EnterFrame) {
+        if (!iter.instance()->enterFrameTrapsEnabled())
+            return true;
+        DebugFrame* frame = iter.debugFrame();
+        frame->setIsDebuggee();
+        frame->observe(cx);
+        // TODO call onEnterFrame
+        JSTrapStatus status = Debugger::onEnterFrame(cx, frame);
+        if (status == JSTRAP_RETURN) {
+            // Ignoring forced return (JSTRAP_RETURN) -- changing code execution
+            // order is not yet implemented in the wasm baseline.
+            // TODO properly handle JSTRAP_RETURN and resume wasm execution.
+            JS_ReportErrorASCII(cx, "Unexpected resumption value from onEnterFrame");
+            return false;
+        }
+        return status == JSTRAP_CONTINUE;
+    }
+    if (site->kind() == CallSite::LeaveFrame) {
+        DebugFrame* frame = iter.debugFrame();
+        frame->updateReturnJSValue();
+        bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true);
+        frame->leave(cx);
+        return ok;
+    }
+
+    DebugFrame* frame = iter.debugFrame();
+    Code& code = iter.instance()->code();
+    MOZ_ASSERT(code.hasBreakpointTrapAtOffset(site->lineOrBytecode()));
+    if (code.stepModeEnabled(frame->funcIndex())) {
+        RootedValue result(cx, UndefinedValue());
+        JSTrapStatus status = Debugger::onSingleStep(cx, &result);
+        if (status == JSTRAP_RETURN) {
+            // TODO properly handle JSTRAP_RETURN.
+            JS_ReportErrorASCII(cx, "Unexpected resumption value from onSingleStep");
+            return false;
+        }
+        if (status != JSTRAP_CONTINUE)
+            return false;
+    }
+    if (code.hasBreakpointSite(site->lineOrBytecode())) {
+        RootedValue result(cx, UndefinedValue());
+        JSTrapStatus status = Debugger::onTrap(cx, &result);
+        if (status == JSTRAP_RETURN) {
+            // TODO properly handle JSTRAP_RETURN.
+            JS_ReportErrorASCII(cx, "Unexpected resumption value from breakpoint handler");
+            return false;
+        }
+        if (status != JSTRAP_CONTINUE)
+            return false;
+    }
+    return true;
+}
+
+static WasmActivation*
+WasmHandleThrow()
+{
+    JSContext* cx = TlsContext.get();
+
+    WasmActivation* activation = cx->wasmActivationStack();
+    MOZ_ASSERT(activation);
+
+    // FrameIterator iterates down wasm frames in the activation starting at
+    // WasmActivation::exitFP. Pass Unwind::True to pop WasmActivation::exitFP
+    // once each time FrameIterator is incremented, ultimately leaving exitFP
+    // null when the FrameIterator is done(). This is necessary to prevent a
+    // DebugFrame from being observed again after we just called onLeaveFrame
+    // (which would lead to the frame being re-added to the map of live frames,
+    // right as it becomes trash).
+    FrameIterator iter(activation, FrameIterator::Unwind::True);
+    if (iter.done())
+        return activation;
+
+    // Live wasm code on the stack is kept alive (in wasm::TraceActivations) by
+    // marking the instance of every wasm::Frame found by FrameIterator.
+    // However, as explained above, we're popping frames while iterating which
+    // means that a GC during this loop could collect the code of frames whose
+    // code is still on the stack. This is actually mostly fine: as soon as we
+    // return to the throw stub, the entire stack will be popped as a whole,
+    // returning to the C++ caller. However, we must keep the throw stub alive
+    // itself which is owned by the innermost instance.
+    RootedWasmInstanceObject keepAlive(cx, iter.instance()->object());
+
+    for (; !iter.done(); ++iter) {
+        if (!iter.debugEnabled())
+            continue;
+
+        DebugFrame* frame = iter.debugFrame();
+        frame->clearReturnJSValue();
+
+        // Assume JSTRAP_ERROR status if no exception is pending --
+        // no onExceptionUnwind handlers must be fired.
+        if (cx->isExceptionPending()) {
+            JSTrapStatus status = Debugger::onExceptionUnwind(cx, frame);
+            if (status == JSTRAP_RETURN) {
+                // Unexpected trap return -- raising error since throw recovery
+                // is not yet implemented in the wasm baseline.
+                // TODO properly handle JSTRAP_RETURN and resume wasm execution.
+                JS_ReportErrorASCII(cx, "Unexpected resumption value from onExceptionUnwind");
+            }
+        }
+
+        bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, false);
+        if (ok) {
+            // Unexpected success from the handler onLeaveFrame -- raising error
+            // since throw recovery is not yet implemented in the wasm baseline.
+            // TODO properly handle success and resume wasm execution.
+            JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
+        }
+        frame->leave(cx);
+     }
+
+    return activation;
+}
+
+static void
+WasmReportTrap(int32_t trapIndex)
+{
+    JSContext* cx = JSContext::innermostWasmActivation()->cx();
+
+    MOZ_ASSERT(trapIndex < int32_t(Trap::Limit) && trapIndex >= 0);
+    Trap trap = Trap(trapIndex);
+
+    unsigned errorNumber;
+    switch (trap) {
+      case Trap::Unreachable:
+        errorNumber = JSMSG_WASM_UNREACHABLE;
+        break;
+      case Trap::IntegerOverflow:
+        errorNumber = JSMSG_WASM_INTEGER_OVERFLOW;
+        break;
+      case Trap::InvalidConversionToInteger:
+        errorNumber = JSMSG_WASM_INVALID_CONVERSION;
+        break;
+      case Trap::IntegerDivideByZero:
+        errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
+        break;
+      case Trap::IndirectCallToNull:
+        errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
+        break;
+      case Trap::IndirectCallBadSig:
+        errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
+        break;
+      case Trap::ImpreciseSimdConversion:
+        errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
+        break;
+      case Trap::OutOfBounds:
+        errorNumber = JSMSG_WASM_OUT_OF_BOUNDS;
+        break;
+      case Trap::StackOverflow:
+        errorNumber = JSMSG_OVER_RECURSED;
+        break;
+      default:
+        MOZ_CRASH("unexpected trap");
+    }
+
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
+}
+
+static void
+WasmReportOutOfBounds()
+{
+    JSContext* cx = JSContext::innermostWasmActivation()->cx();
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
+}
+
+static void
+WasmReportUnalignedAccess()
+{
+    JSContext* cx = JSContext::innermostWasmActivation()->cx();
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS);
+}
+
+static int32_t
+CoerceInPlace_ToInt32(MutableHandleValue val)
+{
+    JSContext* cx = JSContext::innermostWasmActivation()->cx();
+
+    int32_t i32;
+    if (!ToInt32(cx, val, &i32))
+        return false;
+    val.set(Int32Value(i32));
+
+    return true;
+}
+
+static int32_t
+CoerceInPlace_ToNumber(MutableHandleValue val)
+{
+    JSContext* cx = JSContext::innermostWasmActivation()->cx();
+
+    double dbl;
+    if (!ToNumber(cx, val, &dbl))
+        return false;
+    val.set(DoubleValue(dbl));
+
+    return true;
+}
+
+static int64_t
+DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    int64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    int64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(x != INT64_MIN || y != -1);
+    MOZ_ASSERT(y != 0);
+    return x / y;
+}
+
+static int64_t
+UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(y != 0);
+    return x / y;
+}
+
+static int64_t
+ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    int64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    int64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(x != INT64_MIN || y != -1);
+    MOZ_ASSERT(y != 0);
+    return x % y;
+}
+
+static int64_t
+UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
+{
+    uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
+    uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
+    MOZ_ASSERT(y != 0);
+    return x % y;
+}
+
+static int64_t
+TruncateDoubleToInt64(double input)
+{
+    // Note: INT64_MAX is not representable in double. It is actually
+    // INT64_MAX + 1.  Therefore also sending the failure value.
+    if (input >= double(INT64_MAX) || input < double(INT64_MIN) || IsNaN(input))
+        return 0x8000000000000000;
+    return int64_t(input);
+}
+
+static uint64_t
+TruncateDoubleToUint64(double input)
+{
+    // Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
+    // Therefore also sending the failure value.
+    if (input >= double(UINT64_MAX) || input <= -1.0 || IsNaN(input))
+        return 0x8000000000000000;
+    return uint64_t(input);
+}
+
+static double
+Int64ToDouble(int32_t x_hi, uint32_t x_lo)
+{
+    int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
+    return double(x);
+}
+
+static float
+Int64ToFloat32(int32_t x_hi, uint32_t x_lo)
+{
+    int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
+    return float(x);
+}
+
+static double
+Uint64ToDouble(int32_t x_hi, uint32_t x_lo)
+{
+    uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
+    return double(x);
+}
+
+static float
+Uint64ToFloat32(int32_t x_hi, uint32_t x_lo)
+{
+    uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
+    return float(x);
+}
+
+template <class F>
+static inline void*
+FuncCast(F* funcPtr, ABIFunctionType abiType)
+{
+    void* pf = JS_FUNC_TO_DATA_PTR(void*, funcPtr);
+#ifdef JS_SIMULATOR
+    pf = Simulator::RedirectNativeFunction(pf, abiType);
+#endif
+    return pf;
+}
+
+void*
+wasm::AddressOf(SymbolicAddress imm, ABIFunctionType* abiType)
+{
+    switch (imm) {
+      case SymbolicAddress::HandleExecutionInterrupt:
+        *abiType = Args_General0;
+        return FuncCast(WasmHandleExecutionInterrupt, *abiType);
+      case SymbolicAddress::HandleDebugTrap:
+        *abiType = Args_General0;
+        return FuncCast(WasmHandleDebugTrap, *abiType);
+      case SymbolicAddress::HandleThrow:
+        *abiType = Args_General0;
+        return FuncCast(WasmHandleThrow, *abiType);
+      case SymbolicAddress::ReportTrap:
+        *abiType = Args_General1;
+        return FuncCast(WasmReportTrap, *abiType);
+      case SymbolicAddress::ReportOutOfBounds:
+        *abiType = Args_General0;
+        return FuncCast(WasmReportOutOfBounds, *abiType);
+      case SymbolicAddress::ReportUnalignedAccess:
+        *abiType = Args_General0;
+        return FuncCast(WasmReportUnalignedAccess, *abiType);
+      case SymbolicAddress::CallImport_Void:
+        *abiType = Args_General4;
+        return FuncCast(Instance::callImport_void, *abiType);
+      case SymbolicAddress::CallImport_I32:
+        *abiType = Args_General4;
+        return FuncCast(Instance::callImport_i32, *abiType);
+      case SymbolicAddress::CallImport_I64:
+        *abiType = Args_General4;
+        return FuncCast(Instance::callImport_i64, *abiType);
+      case SymbolicAddress::CallImport_F64:
+        *abiType = Args_General4;
+        return FuncCast(Instance::callImport_f64, *abiType);
+      case SymbolicAddress::CoerceInPlace_ToInt32:
+        *abiType = Args_General1;
+        return FuncCast(CoerceInPlace_ToInt32, *abiType);
+      case SymbolicAddress::CoerceInPlace_ToNumber:
+        *abiType = Args_General1;
+        return FuncCast(CoerceInPlace_ToNumber, *abiType);
+      case SymbolicAddress::ToInt32:
+        *abiType = Args_Int_Double;
+        return FuncCast<int32_t (double)>(JS::ToInt32, *abiType);
+      case SymbolicAddress::DivI64:
+        *abiType = Args_General4;
+        return FuncCast(DivI64, *abiType);
+      case SymbolicAddress::UDivI64:
+        *abiType = Args_General4;
+        return FuncCast(UDivI64, *abiType);
+      case SymbolicAddress::ModI64:
+        *abiType = Args_General4;
+        return FuncCast(ModI64, *abiType);
+      case SymbolicAddress::UModI64:
+        *abiType = Args_General4;
+        return FuncCast(UModI64, *abiType);
+      case SymbolicAddress::TruncateDoubleToUint64:
+        *abiType = Args_Int64_Double;
+        return FuncCast(TruncateDoubleToUint64, *abiType);
+      case SymbolicAddress::TruncateDoubleToInt64:
+        *abiType = Args_Int64_Double;
+        return FuncCast(TruncateDoubleToInt64, *abiType);
+      case SymbolicAddress::Uint64ToDouble:
+        *abiType = Args_Double_IntInt;
+        return FuncCast(Uint64ToDouble, *abiType);
+      case SymbolicAddress::Uint64ToFloat32:
+        *abiType = Args_Float32_IntInt;
+        return FuncCast(Uint64ToFloat32, *abiType);
+      case SymbolicAddress::Int64ToDouble:
+        *abiType = Args_Double_IntInt;
+        return FuncCast(Int64ToDouble, *abiType);
+      case SymbolicAddress::Int64ToFloat32:
+        *abiType = Args_Float32_IntInt;
+        return FuncCast(Int64ToFloat32, *abiType);
+#if defined(JS_CODEGEN_ARM)
+      case SymbolicAddress::aeabi_idivmod:
+        *abiType = Args_General2;
+        return FuncCast(__aeabi_idivmod, *abiType);
+      case SymbolicAddress::aeabi_uidivmod:
+        *abiType = Args_General2;
+        return FuncCast(__aeabi_uidivmod, *abiType);
+      case SymbolicAddress::AtomicCmpXchg:
+        *abiType = Args_General5;
+        return FuncCast(atomics_cmpxchg_asm_callout, *abiType);
+      case SymbolicAddress::AtomicXchg:
+        *abiType = Args_General4;
+        return FuncCast(atomics_xchg_asm_callout, *abiType);
+      case SymbolicAddress::AtomicFetchAdd:
+        *abiType = Args_General4;
+        return FuncCast(atomics_add_asm_callout, *abiType);
+      case SymbolicAddress::AtomicFetchSub:
+        *abiType = Args_General4;
+        return FuncCast(atomics_sub_asm_callout, *abiType);
+      case SymbolicAddress::AtomicFetchAnd:
+        *abiType = Args_General4;
+        return FuncCast(atomics_and_asm_callout, *abiType);
+      case SymbolicAddress::AtomicFetchOr:
+        *abiType = Args_General4;
+        return FuncCast(atomics_or_asm_callout, *abiType);
+      case SymbolicAddress::AtomicFetchXor:
+        *abiType = Args_General4;
+        return FuncCast(atomics_xor_asm_callout, *abiType);
+#endif
+      case SymbolicAddress::ModD:
+        *abiType = Args_Double_DoubleDouble;
+        return FuncCast(NumberMod, *abiType);
+      case SymbolicAddress::SinD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(sin, *abiType);
+      case SymbolicAddress::CosD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(cos, *abiType);
+      case SymbolicAddress::TanD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(tan, *abiType);
+      case SymbolicAddress::ASinD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::asin, *abiType);
+      case SymbolicAddress::ACosD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::acos, *abiType);
+      case SymbolicAddress::ATanD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::atan, *abiType);
+      case SymbolicAddress::CeilD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::ceil, *abiType);
+      case SymbolicAddress::CeilF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast<float (float)>(fdlibm::ceilf, *abiType);
+      case SymbolicAddress::FloorD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::floor, *abiType);
+      case SymbolicAddress::FloorF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast<float (float)>(fdlibm::floorf, *abiType);
+      case SymbolicAddress::TruncD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::trunc, *abiType);
+      case SymbolicAddress::TruncF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast<float (float)>(fdlibm::truncf, *abiType);
+      case SymbolicAddress::NearbyIntD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::nearbyint, *abiType);
+      case SymbolicAddress::NearbyIntF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast<float (float)>(fdlibm::nearbyintf, *abiType);
+      case SymbolicAddress::ExpD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::exp, *abiType);
+      case SymbolicAddress::LogD:
+        *abiType = Args_Double_Double;
+        return FuncCast<double (double)>(fdlibm::log, *abiType);
+      case SymbolicAddress::PowD:
+        *abiType = Args_Double_DoubleDouble;
+        return FuncCast(ecmaPow, *abiType);
+      case SymbolicAddress::ATan2D:
+        *abiType = Args_Double_DoubleDouble;
+        return FuncCast(ecmaAtan2, *abiType);
+      case SymbolicAddress::GrowMemory:
+        *abiType = Args_General2;
+        return FuncCast(Instance::growMemory_i32, *abiType);
+      case SymbolicAddress::CurrentMemory:
+        *abiType = Args_General1;
+        return FuncCast(Instance::currentMemory_i32, *abiType);
+      case SymbolicAddress::Limit:
+        break;
+    }
+
+    MOZ_CRASH("Bad SymbolicAddress");
+}
+
+static bool
+GenerateBuiltinThunk(JSContext* cx, void* func, ABIFunctionType abiType, UniqueBuiltinThunk* thunk)
+{
+    if (!cx->compartment()->ensureJitCompartmentExists(cx))
+        return false;
+
+    LifoAlloc lifo(BUILTIN_THUNK_LIFO_SIZE);
+    TempAllocator tempAlloc(&lifo);
+    MacroAssembler masm(MacroAssembler::WasmToken(), tempAlloc);
+
+    CallableOffsets offsets = GenerateBuiltinImportExit(masm, abiType, func);
+
+    masm.finish();
+    if (masm.oom())
+        return false;
+
+    // The executable allocator operates on pointer-aligned sizes.
+    uint32_t codeLength = AlignBytes(masm.bytesNeeded(), sizeof(void*));
+
+    ExecutablePool* pool = nullptr;
+    ExecutableAllocator& allocator = cx->runtime()->jitRuntime()->execAlloc();
+    uint8_t* codeBase = (uint8_t*) allocator.alloc(cx, codeLength, &pool, BUILTIN_THUNK_CODEKIND);
+    if (!codeBase)
+        return false;
+
+    {
+        AutoWritableJitCode awjc(cx->runtime(), codeBase, codeLength);
+        AutoFlushICache afc("GenerateBuiltinThunk");
+
+        masm.executableCopy(codeBase);
+        memset(codeBase + masm.bytesNeeded(), 0, codeLength - masm.bytesNeeded());
+
+        MOZ_ASSERT(!masm.numSymbolicAccesses(), "doesn't need any patching");
+    }
+
+    *thunk = js::MakeUnique<BuiltinThunk>(codeBase, codeLength, pool, offsets);
+    return !!*thunk;
+}
+
+struct BuiltinMatcher
+{
+    const uint8_t* address;
+    explicit BuiltinMatcher(const uint8_t* address) : address(address) {}
+    int operator()(const UniqueBuiltinThunk& thunk) const {
+        if (address < thunk->base)
+            return -1;
+        if (uintptr_t(address) >= uintptr_t(thunk->base) + thunk->size)
+            return 1;
+        return 0;
+    }
+};
+
+bool
+wasm::Runtime::getBuiltinThunk(JSContext* cx, void* funcPtr, ABIFunctionType abiType,
+                               void** thunkPtr)
+{
+    TypedFuncPtr lookup(funcPtr, abiType);
+    auto ptr = builtinThunkMap_.lookupForAdd(lookup);
+    if (ptr) {
+        *thunkPtr = ptr->value();
+        return true;
+    }
+
+    UniqueBuiltinThunk thunk;
+    if (!GenerateBuiltinThunk(cx, funcPtr, abiType, &thunk))
+        return false;
+
+    // Maintain sorted order of thunk addresses.
+    size_t i;
+    size_t size = builtinThunkVector_.length();
+    if (BinarySearchIf(builtinThunkVector_, 0, size, BuiltinMatcher(thunk->base), &i))
+        MOZ_CRASH("clobbering memory");
+
+    *thunkPtr = thunk->base;
+
+    return builtinThunkVector_.insert(builtinThunkVector_.begin() + i, Move(thunk)) &&
+           builtinThunkMap_.add(ptr, lookup, *thunkPtr);
+}
+
+static ABIFunctionType
+ToABIFunctionType(const Sig& sig)
+{
+    const ValTypeVector& args = sig.args();
+    ExprType ret = sig.ret();
+
+    uint32_t abiType;
+    switch (ret) {
+      case ExprType::F32: abiType = ArgType_Float32 << RetType_Shift; break;
+      case ExprType::F64: abiType = ArgType_Double << RetType_Shift; break;
+      default:            MOZ_CRASH("unhandled ret type");
+    }
+
+    for (size_t i = 0; i < args.length(); i++) {
+        switch (args[i]) {
+          case ValType::F32: abiType |= (ArgType_Float32 << (ArgType_Shift * (i + 1))); break;
+          case ValType::F64: abiType |= (ArgType_Double << (ArgType_Shift * (i + 1))); break;
+          default:           MOZ_CRASH("unhandled arg type");
+        }
+    }
+
+    return ABIFunctionType(abiType);
+}
+
+bool
+wasm::Runtime::getBuiltinThunk(JSContext* cx, void* funcPtr, const Sig& sig, void** thunkPtr)
+{
+    ABIFunctionType abiType = ToABIFunctionType(sig);
+#ifdef JS_SIMULATOR
+    funcPtr = Simulator::RedirectNativeFunction(funcPtr, abiType);
+#endif
+    return getBuiltinThunk(cx, funcPtr, abiType, thunkPtr);
+}
+
+BuiltinThunk*
+wasm::Runtime::lookupBuiltin(void* pc)
+{
+    size_t index;
+    size_t length = builtinThunkVector_.length();
+    if (!BinarySearchIf(builtinThunkVector_, 0, length, BuiltinMatcher((uint8_t*)pc), &index))
+        return nullptr;
+    return builtinThunkVector_[index].get();
+}
+
+void
+wasm::Runtime::destroy()
+{
+    builtinThunkVector_.clear();
+    if (builtinThunkMap_.initialized())
+        builtinThunkMap_.clear();
+}
+
+BuiltinThunk::~BuiltinThunk()
+{
+    executablePool->release(size, BUILTIN_THUNK_CODEKIND);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmRuntime.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2017 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_runtime_h
+#define wasm_runtime_h
+
+#include "NamespaceImports.h"
+
+#include "jit/IonTypes.h"
+#include "wasm/WasmTypes.h"
+
+using mozilla::HashGeneric;
+
+namespace js {
+
+namespace jit {
+    class ExecutablePool;
+}
+
+namespace wasm {
+
+void*
+AddressOf(SymbolicAddress imm, jit::ABIFunctionType*);
+
+struct TypedFuncPtr
+{
+    void* funcPtr;
+    jit::ABIFunctionType abiType;
+
+    TypedFuncPtr(void* funcPtr, jit::ABIFunctionType abiType)
+      : funcPtr(funcPtr),
+        abiType(abiType)
+    {}
+
+    typedef TypedFuncPtr Lookup;
+    static HashNumber hash(const Lookup& l) {
+        return HashGeneric(l.funcPtr, uint32_t(l.abiType));
+    }
+    static bool match(const TypedFuncPtr& lhs, const Lookup& rhs) {
+        return lhs.funcPtr == rhs.funcPtr && lhs.abiType == rhs.abiType;
+    }
+};
+
+typedef HashMap<TypedFuncPtr, void*, TypedFuncPtr, SystemAllocPolicy> BuiltinThunkMap;
+
+struct BuiltinThunk
+{
+    jit::ExecutablePool* executablePool;
+    CodeRange codeRange;
+    size_t size;
+    uint8_t* base;
+
+    BuiltinThunk(uint8_t* base, size_t size, jit::ExecutablePool* executablePool,
+                 CallableOffsets offsets)
+      : executablePool(executablePool),
+        codeRange(CodeRange(CodeRange::ImportNativeExit, offsets)),
+        size(size),
+        base(base)
+    {}
+    ~BuiltinThunk();
+};
+
+typedef UniquePtr<BuiltinThunk> UniqueBuiltinThunk;
+typedef Vector<UniqueBuiltinThunk, 4, SystemAllocPolicy> BuiltinThunkVector;
+
+// wasm::Runtime contains all the needed information for wasm that has the
+// lifetime of a JSRuntime.
+
+class Runtime
+{
+    BuiltinThunkMap builtinThunkMap_;
+    BuiltinThunkVector builtinThunkVector_;
+
+    bool getBuiltinThunk(JSContext* cx, void* funcPtr, jit::ABIFunctionType type, void** thunkPtr);
+
+  public:
+    bool init() { return builtinThunkMap_.init(); }
+    void destroy();
+
+    bool getBuiltinThunk(JSContext* cx, void* funcPtr, const Sig& sig, void** thunkPtr);
+    BuiltinThunk* lookupBuiltin(void* pc);
+};
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_runtime_h
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -103,17 +103,17 @@ class AutoSetHandlingSegFault
 # define R8_sig(p) ((p)->sc_r8)
 # define R9_sig(p) ((p)->sc_r9)
 # define R10_sig(p) ((p)->sc_r10)
 # define R11_sig(p) ((p)->sc_r11)
 # define R12_sig(p) ((p)->sc_r12)
 # define R13_sig(p) ((p)->sc_r13)
 # define R14_sig(p) ((p)->sc_r14)
 # define R15_sig(p) ((p)->sc_r15)
-#elif defined(__linux__) || defined(SOLARIS)
+#elif defined(__linux__) || defined(__sun)
 # if defined(__linux__)
 #  define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i])
 #  define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_EIP])
 #  define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
 # else
 #  define XMM_sig(p,i) ((p)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[i])
 #  define EIP_sig(p) ((p)->uc_mcontext.gregs[REG_PC])
 #  define EBP_sig(p) ((p)->uc_mcontext.gregs[REG_EBP])
@@ -1525,10 +1525,11 @@ js::wasm::IsPCInWasmCode(void *pc)
         return false;
 
     MOZ_RELEASE_ASSERT(!cx->handlingSegFault);
 
     WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
-    return !!activation->compartment()->wasm.lookupCode(pc);
+    return !!activation->compartment()->wasm.lookupCode(pc) ||
+           !!activation->cx()->runtime()->wasm().lookupBuiltin(pc);
 }
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -27,16 +27,25 @@
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::ArrayLength;
 
 static void
+FinishOffsets(MacroAssembler& masm, Offsets* offsets)
+{
+    // On old ARM hardware, constant pools could be inserted and they need to
+    // be flushed before considering the size of the masm.
+    masm.flushBuffer();
+    offsets->end = masm.size();
+}
+
+static void
 AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0)
 {
     MOZ_ASSERT((sizeof(Frame) + masm.framePushed() + addBeforeAssert) % alignment == 0);
     masm.assertStackAlignment(alignment, addBeforeAssert);
 }
 
 static unsigned
 StackDecrementForCall(MacroAssembler& masm, uint32_t alignment, unsigned bytesToPush)
@@ -326,17 +335,17 @@ wasm::GenerateEntry(MacroAssembler& masm
 
     // Restore clobbered non-volatile registers of the caller.
     masm.PopRegsInMask(NonVolatileRegs);
     MOZ_ASSERT(masm.framePushed() == 0);
 
     masm.move32(Imm32(true), ReturnReg);
     masm.ret();
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 static void
 StackCopy(MacroAssembler& masm, MIRType type, Register scratch, Address src, Address dst)
 {
     if (type == MIRType::Int32) {
         masm.load32(src, scratch);
@@ -493,17 +502,17 @@ wasm::GenerateImportFunction(jit::MacroA
     // Restore the TLS register and pinned regs, per wasm function ABI.
     masm.loadWasmTlsRegFromFrame();
     masm.loadWasmPinnedRegsFromTls();
 
     GenerateFunctionEpilogue(masm, framePushed, &offsets);
 
     masm.wasmEmitTrapOutOfLineCode();
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate callImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 CallableOffsets
 wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
@@ -620,17 +629,17 @@ wasm::GenerateImportInterpExit(MacroAsse
 #if defined(JS_CODEGEN_X64) || \
     defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     MOZ_ASSERT(NonVolatileRegs.has(HeapReg));
 #endif
 
     GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, &offsets);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into a compatible JIT function,
 // having boxed all the ABI arguments into the JIT stack frame layout.
 CallableOffsets
 wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLabel)
@@ -853,16 +862,104 @@ wasm::GenerateImportJitExit(MacroAssembl
         }
 
         masm.jump(&done);
         masm.setFramePushed(0);
     }
 
     MOZ_ASSERT(masm.framePushed() == 0);
 
+    FinishOffsets(masm, &offsets);
+    return offsets;
+}
+
+struct ABIFunctionArgs
+{
+    ABIFunctionType abiType;
+    size_t len;
+
+    explicit ABIFunctionArgs(ABIFunctionType sig)
+      : abiType(ABIFunctionType(sig >> ArgType_Shift))
+    {
+        len = 0;
+        uint32_t i = uint32_t(abiType);
+        while (i) {
+            i = i >> ArgType_Shift;
+            len++;
+        }
+    }
+
+    size_t length() const { return len; }
+
+    MIRType operator[](size_t i) const {
+        MOZ_ASSERT(i < len);
+        uint32_t abi = uint32_t(abiType);
+        while (i--)
+            abi = abi >> ArgType_Shift;
+        return ToMIRType(ABIArgType(abi));
+    }
+};
+
+CallableOffsets
+wasm::GenerateBuiltinImportExit(MacroAssembler& masm, ABIFunctionType abiType, void* func)
+{
+    masm.setFramePushed(0);
+
+    ABIFunctionArgs args(abiType);
+    uint32_t framePushed = StackDecrementForCall(masm, ABIStackAlignment, args);
+
+    CallableOffsets offsets;
+    GenerateExitPrologue(masm, framePushed, ExitReason::ImportNative, &offsets);
+
+    // Copy out and convert caller arguments, if needed.
+    unsigned offsetToCallerStackArgs = sizeof(Frame) + masm.framePushed();
+    Register scratch = ABINonArgReturnReg0;
+    for (ABIArgIter<ABIFunctionArgs> i(args); !i.done(); i++) {
+        if (i->argInRegister()) {
+#ifdef JS_CODEGEN_ARM
+            // Non hard-fp passes the args values in GPRs.
+            if (!UseHardFpABI() && IsFloatingPointType(i.mirType())) {
+                FloatRegister input = i->fpu();
+                if (i.mirType() == MIRType::Float32) {
+                    masm.ma_vxfer(input, Register::FromCode(input.id()));
+                } else if (i.mirType() == MIRType::Double) {
+                    uint32_t regId = input.singleOverlay().id();
+                    masm.ma_vxfer(input, Register::FromCode(regId), Register::FromCode(regId + 1));
+                }
+            }
+#endif
+            continue;
+        }
+
+        Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
+        Address dst(masm.getStackPointer(), i->offsetFromArgBase());
+        StackCopy(masm, i.mirType(), scratch, src, dst);
+    }
+
+    masm.call(ImmPtr(func, ImmPtr::NoCheckToken()));
+
+#if defined(JS_CODEGEN_X86)
+    // x86 passes the return value on the x87 FP stack.
+    Operand op(esp, 0);
+    MIRType retType = ToMIRType(ABIArgType(abiType & ArgType_Mask));
+    if (retType == MIRType::Float32) {
+        masm.fstp32(op);
+        masm.loadFloat32(op, ReturnFloat32Reg);
+    } else if (retType == MIRType::Double) {
+        masm.fstp(op);
+        masm.loadDouble(op, ReturnDoubleReg);
+    }
+#elif defined(JS_CODEGEN_ARM)
+    // Non hard-fp passes the return values in GPRs.
+    MIRType retType = ToMIRType(ABIArgType(abiType & ArgType_Mask));
+    if (!UseHardFpABI() && IsFloatingPointType(retType))
+        masm.ma_vxfer(r0, r1, d0);
+#endif
+
+    GenerateExitEpilogue(masm, framePushed, ExitReason::ImportNative, &offsets);
     offsets.end = masm.currentOffset();
     return offsets;
 }
 
 // Generate a stub that calls into ReportTrap with the right trap reason.
 // This stub is called with ABIStackAlignment by a trap out-of-line path. An
 // exit prologue/epilogue is used so that stack unwinding picks up the
 // current WasmActivation. Unwinding will begin at the caller of this trap exit.
@@ -891,17 +988,17 @@ wasm::GenerateTrapExit(MacroAssembler& m
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(SymbolicAddress::ReportTrap);
 
     masm.jump(throwLabel);
 
     GenerateExitEpilogue(masm, framePushed, ExitReason::Trap, &offsets);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 // Generate a stub which is only used by the signal handlers to handle out of
 // bounds access by experimental SIMD.js and Atomics and unaligned accesses on
 // ARM. This stub is executed by direct PC transfer from the faulting memory
 // access and thus the stack depth is unknown. Since WasmActivation::exitFP is
 // not set before calling the error reporter, the current wasm activation will
@@ -921,17 +1018,17 @@ GenerateGenericMemoryAccessTrap(MacroAss
     // restoring sp.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
     if (ShadowStackSpace)
         masm.subFromStackPtr(Imm32(ShadowStackSpace));
 
     masm.call(reporter);
     masm.jump(throwLabel);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 Offsets
 wasm::GenerateOutOfBoundsExit(MacroAssembler& masm, Label* throwLabel)
 {
     return GenerateGenericMemoryAccessTrap(masm, SymbolicAddress::ReportOutOfBounds, throwLabel);
 }
@@ -1104,17 +1201,17 @@ wasm::GenerateInterruptExit(MacroAssembl
 #elif defined(JS_CODEGEN_ARM64)
     MOZ_CRASH();
 #elif defined (JS_CODEGEN_NONE)
     MOZ_CRASH();
 #else
 # error "Unknown architecture!"
 #endif
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 // Generate a stub that restores the stack pointer to what it was on entry to
 // the wasm activation, sets the return register to 'false' and then executes a
 // return which will return from this wasm activation to the caller. This stub
 // should only be called after the caller has reported an error (or, in the case
 // of the interrupt stub, intends to interrupt execution).
@@ -1142,17 +1239,17 @@ wasm::GenerateThrowStub(MacroAssembler& 
     masm.loadStackPtr(Address(act, WasmActivation::offsetOfEntrySP()));
     masm.Pop(ReturnReg);
     masm.PopRegsInMask(NonVolatileRegs);
     MOZ_ASSERT(masm.framePushed() == 0);
 
     masm.mov(ImmWord(0), ReturnReg);
     masm.ret();
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
 static const LiveRegisterSet AllAllocatableRegs = LiveRegisterSet(
     GeneralRegisterSet(Registers::AllocatableMask),
     FloatRegisterSet(FloatRegisters::AllMask));
 
 // Generate a stub that handle toggable enter/leave frame traps or breakpoints.
@@ -1193,11 +1290,11 @@ wasm::GenerateDebugTrapStub(MacroAssembl
     masm.Pop(scratch);
     masm.moveToStackPtr(scratch);
 
     masm.setFramePushed(framePushed);
     masm.PopRegsInMask(AllAllocatableRegs);
 
     GenerateExitEpilogue(masm, 0, ExitReason::DebugTrap, &offsets);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -18,17 +18,21 @@
 
 #ifndef wasm_stubs_h
 #define wasm_stubs_h
 
 #include "wasm/WasmTypes.h"
 
 namespace js {
 
-namespace jit { class MacroAssembler; class Label; }
+namespace jit {
+    class MacroAssembler;
+    class Label;
+    enum ABIFunctionType;
+}
 
 namespace wasm {
 
 class FuncExport;
 class FuncImport;
 
 extern Offsets
 GenerateEntry(jit::MacroAssembler& masm, const FuncExport& fe);
@@ -39,16 +43,19 @@ GenerateImportFunction(jit::MacroAssembl
 extern CallableOffsets
 GenerateImportInterpExit(jit::MacroAssembler& masm, const FuncImport& fi, uint32_t funcImportIndex,
                          jit::Label* throwLabel);
 
 extern CallableOffsets
 GenerateImportJitExit(jit::MacroAssembler& masm, const FuncImport& fi, jit::Label* throwLabel);
 
 extern CallableOffsets
+GenerateBuiltinImportExit(jit::MacroAssembler& masm, jit::ABIFunctionType abiType, void* func);
+
+extern CallableOffsets
 GenerateTrapExit(jit::MacroAssembler& masm, Trap trap, jit::Label* throwLabel);
 
 extern Offsets
 GenerateOutOfBoundsExit(jit::MacroAssembler& masm, jit::Label* throwLabel);
 
 extern Offsets
 GenerateUnalignedExit(jit::MacroAssembler& masm, jit::Label* throwLabel);
 
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -13,39 +13,26 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmTypes.h"
 
-#include "mozilla/MathAlgorithms.h"
-
-#include "fdlibm.h"
-
-#include "jslibmath.h"
-#include "jsmath.h"
-
-#include "jit/MacroAssembler.h"
-#include "js/Conversions.h"
-#include "vm/Interpreter.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmSerialize.h"
-#include "wasm/WasmSignalHandlers.h"
 
-#include "vm/Debugger-inl.h"
-#include "vm/Stack-inl.h"
+#include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
-using mozilla::IsNaN;
 using mozilla::IsPowerOfTwo;
 
 void
 Val::writePayload(uint8_t* dst) const
 {
     switch (type_) {
       case ValType::I32:
       case ValType::F32:
@@ -62,356 +49,16 @@ Val::writePayload(uint8_t* dst) const
       case ValType::B8x16:
       case ValType::B16x8:
       case ValType::B32x4:
         memcpy(dst, &u, jit::Simd128DataSize);
         return;
     }
 }
 
-#if defined(JS_CODEGEN_ARM)
-extern "C" {
-
-extern MOZ_EXPORT int64_t
-__aeabi_idivmod(int, int);
-
-extern MOZ_EXPORT int64_t
-__aeabi_uidivmod(int, int);
-
-}
-#endif
-
-static void*
-WasmHandleExecutionInterrupt()
-{
-    WasmActivation* activation = JSContext::innermostWasmActivation();
-
-    // wasm::Compartment requires notification when execution is interrupted in
-    // the compartment. Only the innermost compartment has been interrupted;
-    // enclosing compartments necessarily exited through an exit stub.
-    activation->compartment()->wasm.setInterrupted(true);
-    bool success = CheckForInterrupt(activation->cx());
-    activation->compartment()->wasm.setInterrupted(false);
-
-    // Preserve the invariant that having a non-null resumePC means that we are
-    // handling an interrupt.
-    void* resumePC = activation->resumePC();
-    activation->setResumePC(nullptr);
-
-    // Return the resumePC if execution can continue or null if execution should
-    // jump to the throw stub.
-    return success ? resumePC : nullptr;
-}
-
-static bool
-WasmHandleDebugTrap()
-{
-    WasmActivation* activation = JSContext::innermostWasmActivation();
-    MOZ_ASSERT(activation);
-    JSContext* cx = activation->cx();
-
-    FrameIterator iter(activation);
-    MOZ_ASSERT(iter.debugEnabled());
-    const CallSite* site = iter.debugTrapCallsite();
-    MOZ_ASSERT(site);
-    if (site->kind() == CallSite::EnterFrame) {
-        if (!iter.instance()->enterFrameTrapsEnabled())
-            return true;
-        DebugFrame* frame = iter.debugFrame();
-        frame->setIsDebuggee();
-        frame->observe(cx);
-        // TODO call onEnterFrame
-        JSTrapStatus status = Debugger::onEnterFrame(cx, frame);
-        if (status == JSTRAP_RETURN) {
-            // Ignoring forced return (JSTRAP_RETURN) -- changing code execution
-            // order is not yet implemented in the wasm baseline.
-            // TODO properly handle JSTRAP_RETURN and resume wasm execution.
-            JS_ReportErrorASCII(cx, "Unexpected resumption value from onEnterFrame");
-            return false;
-        }
-        return status == JSTRAP_CONTINUE;
-    }
-    if (site->kind() == CallSite::LeaveFrame) {
-        DebugFrame* frame = iter.debugFrame();
-        frame->updateReturnJSValue();
-        bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, true);
-        frame->leave(cx);
-        return ok;
-    }
-
-    DebugFrame* frame = iter.debugFrame();
-    Code& code = iter.instance()->code();
-    MOZ_ASSERT(code.hasBreakpointTrapAtOffset(site->lineOrBytecode()));
-    if (code.stepModeEnabled(frame->funcIndex())) {
-        RootedValue result(cx, UndefinedValue());
-        JSTrapStatus status = Debugger::onSingleStep(cx, &result);
-        if (status == JSTRAP_RETURN) {
-            // TODO properly handle JSTRAP_RETURN.
-            JS_ReportErrorASCII(cx, "Unexpected resumption value from onSingleStep");
-            return false;
-        }
-        if (status != JSTRAP_CONTINUE)
-            return false;
-    }
-    if (code.hasBreakpointSite(site->lineOrBytecode())) {
-        RootedValue result(cx, UndefinedValue());
-        JSTrapStatus status = Debugger::onTrap(cx, &result);
-        if (status == JSTRAP_RETURN) {
-            // TODO properly handle JSTRAP_RETURN.
-            JS_ReportErrorASCII(cx, "Unexpected resumption value from breakpoint handler");
-            return false;
-        }
-        if (status != JSTRAP_CONTINUE)
-            return false;
-    }
-    return true;
-}
-
-static WasmActivation*
-WasmHandleThrow()
-{
-    JSContext* cx = TlsContext.get();
-
-    WasmActivation* activation = cx->wasmActivationStack();
-    MOZ_ASSERT(activation);
-
-    // FrameIterator iterates down wasm frames in the activation starting at
-    // WasmActivation::exitFP. Pass Unwind::True to pop WasmActivation::exitFP
-    // once each time FrameIterator is incremented, ultimately leaving exitFP
-    // null when the FrameIterator is done(). This is necessary to prevent a
-    // DebugFrame from being observed again after we just called onLeaveFrame
-    // (which would lead to the frame being re-added to the map of live frames,
-    // right as it becomes trash).
-    FrameIterator iter(activation, FrameIterator::Unwind::True);
-    if (iter.done())
-        return activation;
-
-    // Live wasm code on the stack is kept alive (in wasm::TraceActivations) by
-    // marking the instance of every wasm::Frame found by FrameIterator.
-    // However, as explained above, we're popping frames while iterating which
-    // means that a GC during this loop could collect the code of frames whose
-    // code is still on the stack. This is actually mostly fine: as soon as we
-    // return to the throw stub, the entire stack will be popped as a whole,
-    // returning to the C++ caller. However, we must keep the throw stub alive
-    // itself which is owned by the innermost instance.
-    RootedWasmInstanceObject keepAlive(cx, iter.instance()->object());
-
-    for (; !iter.done(); ++iter) {
-        if (!iter.debugEnabled())
-            continue;
-
-        DebugFrame* frame = iter.debugFrame();
-        frame->clearReturnJSValue();
-
-        // Assume JSTRAP_ERROR status if no exception is pending --
-        // no onExceptionUnwind handlers must be fired.
-        if (cx->isExceptionPending()) {
-            JSTrapStatus status = Debugger::onExceptionUnwind(cx, frame);
-            if (status == JSTRAP_RETURN) {
-                // Unexpected trap return -- raising error since throw recovery
-                // is not yet implemented in the wasm baseline.
-                // TODO properly handle JSTRAP_RETURN and resume wasm execution.
-                JS_ReportErrorASCII(cx, "Unexpected resumption value from onExceptionUnwind");
-            }
-        }
-
-        bool ok = Debugger::onLeaveFrame(cx, frame, nullptr, false);
-        if (ok) {
-            // Unexpected success from the handler onLeaveFrame -- raising error
-            // since throw recovery is not yet implemented in the wasm baseline.
-            // TODO properly handle success and resume wasm execution.
-            JS_ReportErrorASCII(cx, "Unexpected success from onLeaveFrame");
-        }
-        frame->leave(cx);
-     }
-
-    return activation;
-}
-
-static void
-WasmReportTrap(int32_t trapIndex)
-{
-    JSContext* cx = JSContext::innermostWasmActivation()->cx();
-
-    MOZ_ASSERT(trapIndex < int32_t(Trap::Limit) && trapIndex >= 0);
-    Trap trap = Trap(trapIndex);
-
-    unsigned errorNumber;
-    switch (trap) {
-      case Trap::Unreachable:
-        errorNumber = JSMSG_WASM_UNREACHABLE;
-        break;
-      case Trap::IntegerOverflow:
-        errorNumber = JSMSG_WASM_INTEGER_OVERFLOW;
-        break;
-      case Trap::InvalidConversionToInteger:
-        errorNumber = JSMSG_WASM_INVALID_CONVERSION;
-        break;
-      case Trap::IntegerDivideByZero:
-        errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
-        break;
-      case Trap::IndirectCallToNull:
-        errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
-        break;
-      case Trap::IndirectCallBadSig:
-        errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
-        break;
-      case Trap::ImpreciseSimdConversion:
-        errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
-        break;
-      case Trap::OutOfBounds:
-        errorNumber = JSMSG_WASM_OUT_OF_BOUNDS;
-        break;
-      case Trap::StackOverflow:
-        errorNumber = JSMSG_OVER_RECURSED;
-        break;
-      default:
-        MOZ_CRASH("unexpected trap");
-    }
-
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
-}
-
-static void
-WasmReportOutOfBounds()
-{
-    JSContext* cx = JSContext::innermostWasmActivation()->cx();
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_OUT_OF_BOUNDS);
-}
-
-static void
-WasmReportUnalignedAccess()
-{
-    JSContext* cx = JSContext::innermostWasmActivation()->cx();
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_UNALIGNED_ACCESS);
-}
-
-static int32_t
-CoerceInPlace_ToInt32(MutableHandleValue val)
-{
-    JSContext* cx = JSContext::innermostWasmActivation()->cx();
-
-    int32_t i32;
-    if (!ToInt32(cx, val, &i32))
-        return false;
-    val.set(Int32Value(i32));
-
-    return true;
-}
-
-static int32_t
-CoerceInPlace_ToNumber(MutableHandleValue val)
-{
-    JSContext* cx = JSContext::innermostWasmActivation()->cx();
-
-    double dbl;
-    if (!ToNumber(cx, val, &dbl))
-        return false;
-    val.set(DoubleValue(dbl));
-
-    return true;
-}
-
-static int64_t
-DivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
-{
-    int64_t x = ((uint64_t)x_hi << 32) + x_lo;
-    int64_t y = ((uint64_t)y_hi << 32) + y_lo;
-    MOZ_ASSERT(x != INT64_MIN || y != -1);
-    MOZ_ASSERT(y != 0);
-    return x / y;
-}
-
-static int64_t
-UDivI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
-{
-    uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
-    uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
-    MOZ_ASSERT(y != 0);
-    return x / y;
-}
-
-static int64_t
-ModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
-{
-    int64_t x = ((uint64_t)x_hi << 32) + x_lo;
-    int64_t y = ((uint64_t)y_hi << 32) + y_lo;
-    MOZ_ASSERT(x != INT64_MIN || y != -1);
-    MOZ_ASSERT(y != 0);
-    return x % y;
-}
-
-static int64_t
-UModI64(uint32_t x_hi, uint32_t x_lo, uint32_t y_hi, uint32_t y_lo)
-{
-    uint64_t x = ((uint64_t)x_hi << 32) + x_lo;
-    uint64_t y = ((uint64_t)y_hi << 32) + y_lo;
-    MOZ_ASSERT(y != 0);
-    return x % y;
-}
-
-static int64_t
-TruncateDoubleToInt64(double input)
-{
-    // Note: INT64_MAX is not representable in double. It is actually
-    // INT64_MAX + 1.  Therefore also sending the failure value.
-    if (input >= double(INT64_MAX) || input < double(INT64_MIN) || IsNaN(input))
-        return 0x8000000000000000;
-    return int64_t(input);
-}
-
-static uint64_t
-TruncateDoubleToUint64(double input)
-{
-    // Note: UINT64_MAX is not representable in double. It is actually UINT64_MAX + 1.
-    // Therefore also sending the failure value.
-    if (input >= double(UINT64_MAX) || input <= -1.0 || IsNaN(input))
-        return 0x8000000000000000;
-    return uint64_t(input);
-}
-
-static double
-Int64ToDouble(int32_t x_hi, uint32_t x_lo)
-{
-    int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
-    return double(x);
-}
-
-static float
-Int64ToFloat32(int32_t x_hi, uint32_t x_lo)
-{
-    int64_t x = int64_t((uint64_t(x_hi) << 32)) + int64_t(x_lo);
-    return float(x);
-}
-
-static double
-Uint64ToDouble(int32_t x_hi, uint32_t x_lo)
-{
-    uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
-    return double(x);
-}
-
-static float
-Uint64ToFloat32(int32_t x_hi, uint32_t x_lo)
-{
-    uint64_t x = (uint64_t(x_hi) << 32) + uint64_t(x_lo);
-    return float(x);
-}
-
-template <class F>
-static inline void*
-FuncCast(F* pf, ABIFunctionType type)
-{
-    void *pv = JS_FUNC_TO_DATA_PTR(void*, pf);
-#ifdef JS_SIMULATOR
-    pv = Simulator::RedirectNativeFunction(pv, type);
-#endif
-    return pv;
-}
-
 bool
 wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode)
 {
     switch (callee) {
       case SymbolicAddress::FloorD:
       case SymbolicAddress::FloorF:
         *mode = jit::RoundingMode::Down;
         return true;
@@ -427,135 +74,16 @@ wasm::IsRoundingFunction(SymbolicAddress
       case SymbolicAddress::NearbyIntF:
         *mode = jit::RoundingMode::NearestTiesToEven;
         return true;
       default:
         return false;
     }
 }
 
-void*
-wasm::AddressOf(SymbolicAddress imm)
-{
-    switch (imm) {
-      case SymbolicAddress::HandleExecutionInterrupt:
-        return FuncCast(WasmHandleExecutionInterrupt, Args_General0);
-      case SymbolicAddress::HandleDebugTrap:
-        return FuncCast(WasmHandleDebugTrap, Args_General0);
-      case SymbolicAddress::HandleThrow:
-        return FuncCast(WasmHandleThrow, Args_General0);
-      case SymbolicAddress::ReportTrap:
-        return FuncCast(WasmReportTrap, Args_General1);
-      case SymbolicAddress::ReportOutOfBounds:
-        return FuncCast(WasmReportOutOfBounds, Args_General0);
-      case SymbolicAddress::ReportUnalignedAccess:
-        return FuncCast(WasmReportUnalignedAccess, Args_General0);
-      case SymbolicAddress::CallImport_Void:
-        return FuncCast(Instance::callImport_void, Args_General4);
-      case SymbolicAddress::CallImport_I32:
-        return FuncCast(Instance::callImport_i32, Args_General4);
-      case SymbolicAddress::CallImport_I64:
-        return FuncCast(Instance::callImport_i64, Args_General4);
-      case SymbolicAddress::CallImport_F64:
-        return FuncCast(Instance::callImport_f64, Args_General4);
-      case SymbolicAddress::CoerceInPlace_ToInt32:
-        return FuncCast(CoerceInPlace_ToInt32, Args_General1);
-      case SymbolicAddress::CoerceInPlace_ToNumber:
-        return FuncCast(CoerceInPlace_ToNumber, Args_General1);
-      case SymbolicAddress::ToInt32:
-        return FuncCast<int32_t (double)>(JS::ToInt32, Args_Int_Double);
-      case SymbolicAddress::DivI64:
-        return FuncCast(DivI64, Args_General4);
-      case SymbolicAddress::UDivI64:
-        return FuncCast(UDivI64, Args_General4);
-      case SymbolicAddress::ModI64:
-        return FuncCast(ModI64, Args_General4);
-      case SymbolicAddress::UModI64:
-        return FuncCast(UModI64, Args_General4);
-      case SymbolicAddress::TruncateDoubleToUint64:
-        return FuncCast(TruncateDoubleToUint64, Args_Int64_Double);
-      case SymbolicAddress::TruncateDoubleToInt64:
-        return FuncCast(TruncateDoubleToInt64, Args_Int64_Double);
-      case SymbolicAddress::Uint64ToDouble:
-        return FuncCast(Uint64ToDouble, Args_Double_IntInt);
-      case SymbolicAddress::Uint64ToFloat32:
-        return FuncCast(Uint64ToFloat32, Args_Float32_IntInt);
-      case SymbolicAddress::Int64ToDouble:
-        return FuncCast(Int64ToDouble, Args_Double_IntInt);
-      case SymbolicAddress::Int64ToFloat32:
-        return FuncCast(Int64ToFloat32, Args_Float32_IntInt);
-#if defined(JS_CODEGEN_ARM)
-      case SymbolicAddress::aeabi_idivmod:
-        return FuncCast(__aeabi_idivmod, Args_General2);
-      case SymbolicAddress::aeabi_uidivmod:
-        return FuncCast(__aeabi_uidivmod, Args_General2);
-      case SymbolicAddress::AtomicCmpXchg:
-        return FuncCast(atomics_cmpxchg_asm_callout, Args_General5);
-      case SymbolicAddress::AtomicXchg:
-        return FuncCast(atomics_xchg_asm_callout, Args_General4);
-      case SymbolicAddress::AtomicFetchAdd:
-        return FuncCast(atomics_add_asm_callout, Args_General4);
-      case SymbolicAddress::AtomicFetchSub:
-        return FuncCast(atomics_sub_asm_callout, Args_General4);
-      case SymbolicAddress::AtomicFetchAnd:
-        return FuncCast(atomics_and_asm_callout, Args_General4);
-      case SymbolicAddress::AtomicFetchOr:
-        return FuncCast(atomics_or_asm_callout, Args_General4);
-      case SymbolicAddress::AtomicFetchXor:
-        return FuncCast(atomics_xor_asm_callout, Args_General4);
-#endif
-      case SymbolicAddress::ModD:
-        return FuncCast(NumberMod, Args_Double_DoubleDouble);
-      case SymbolicAddress::SinD:
-        return FuncCast<double (double)>(sin, Args_Double_Double);
-      case SymbolicAddress::CosD:
-        return FuncCast<double (double)>(cos, Args_Double_Double);
-      case SymbolicAddress::TanD:
-        return FuncCast<double (double)>(tan, Args_Double_Double);
-      case SymbolicAddress::ASinD:
-        return FuncCast<double (double)>(fdlibm::asin, Args_Double_Double);
-      case SymbolicAddress::ACosD:
-        return FuncCast<double (double)>(fdlibm::acos, Args_Double_Double);
-      case SymbolicAddress::ATanD:
-        return FuncCast<double (double)>(fdlibm::atan, Args_Double_Double);
-      case SymbolicAddress::CeilD:
-        return FuncCast<double (double)>(fdlibm::ceil, Args_Double_Double);
-      case SymbolicAddress::CeilF:
-        return FuncCast<float (float)>(fdlibm::ceilf, Args_Float32_Float32);
-      case SymbolicAddress::FloorD:
-        return FuncCast<double (double)>(fdlibm::floor, Args_Double_Double);
-      case SymbolicAddress::FloorF:
-        return FuncCast<float (float)>(fdlibm::floorf, Args_Float32_Float32);
-      case SymbolicAddress::TruncD:
-        return FuncCast<double (double)>(fdlibm::trunc, Args_Double_Double);
-      case SymbolicAddress::TruncF:
-        return FuncCast<float (float)>(fdlibm::truncf, Args_Float32_Float32);
-      case SymbolicAddress::NearbyIntD:
-        return FuncCast<double (double)>(fdlibm::nearbyint, Args_Double_Double);
-      case SymbolicAddress::NearbyIntF:
-        return FuncCast<float (float)>(fdlibm::nearbyintf, Args_Float32_Float32);
-      case SymbolicAddress::ExpD:
-        return FuncCast<double (double)>(fdlibm::exp, Args_Double_Double);
-      case SymbolicAddress::LogD:
-        return FuncCast<double (double)>(fdlibm::log, Args_Double_Double);
-      case SymbolicAddress::PowD:
-        return FuncCast(ecmaPow, Args_Double_DoubleDouble);
-      case SymbolicAddress::ATan2D:
-        return FuncCast(ecmaAtan2, Args_Double_DoubleDouble);
-      case SymbolicAddress::GrowMemory:
-        return FuncCast<uint32_t (Instance*, uint32_t)>(Instance::growMemory_i32, Args_General2);
-      case SymbolicAddress::CurrentMemory:
-        return FuncCast<uint32_t (Instance*)>(Instance::currentMemory_i32, Args_General1);
-      case SymbolicAddress::Limit:
-        break;
-    }
-
-    MOZ_CRASH("Bad SymbolicAddress");
-}
-
 static uint32_t
 GetCPUID()
 {
     enum Arch {
         X86 = 0x1,
         X64 = 0x2,
         ARM = 0x3,
         MIPS = 0x4,
@@ -1134,8 +662,81 @@ DebugFrame::observe(JSContext* cx)
 void
 DebugFrame::leave(JSContext* cx)
 {
     if (observing_) {
        instance()->code().adjustEnterAndLeaveFrameTrapsState(cx, /* enabled = */ false);
        observing_ = false;
     }
 }
+
+CodeRange::CodeRange(Kind kind, Offsets offsets)
+  : begin_(offsets.begin),
+    ret_(0),
+    end_(offsets.end),
+    funcIndex_(0),
+    funcLineOrBytecode_(0),
+    funcBeginToNormalEntry_(0),
+    kind_(kind)
+{
+    MOZ_ASSERT(begin_ <= end_);
+#ifdef DEBUG
+    switch (kind_) {
+      case Entry:
+      case DebugTrap:
+      case FarJumpIsland:
+      case Inline:
+      case Throw:
+      case Interrupt:
+        break;
+      case Function:
+      case TrapExit:
+      case ImportJitExit:
+      case ImportNativeExit:
+      case ImportInterpExit:
+        MOZ_CRASH("should use more specific constructor");
+    }
+#endif
+}
+
+CodeRange::CodeRange(Kind kind, CallableOffsets offsets)
+  : begin_(offsets.begin),
+    ret_(offsets.ret),
+    end_(offsets.end),
+    funcIndex_(0),
+    funcLineOrBytecode_(0),
+    funcBeginToNormalEntry_(0),
+    kind_(kind)
+{
+    MOZ_ASSERT(begin_ < ret_);
+    MOZ_ASSERT(ret_ < end_);
+#ifdef DEBUG
+    switch (kind_) {
+      case TrapExit:
+      case ImportJitExit:
+      case ImportNativeExit:
+      case ImportInterpExit:
+        break;
+      case Entry:
+      case DebugTrap:
+      case FarJumpIsland:
+      case Inline:
+      case Throw:
+      case Interrupt:
+      case Function:
+        MOZ_CRASH("should use more specific constructor");
+    }
+#endif
+}
+
+CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
+  : begin_(offsets.begin),
+    ret_(offsets.ret),
+    end_(offsets.end),
+    funcIndex_(funcIndex),
+    funcLineOrBytecode_(funcLineOrBytecode),
+    funcBeginToNormalEntry_(offsets.normalEntry - begin_),
+    kind_(Function)
+{
+    MOZ_ASSERT(begin_ < ret_);
+    MOZ_ASSERT(ret_ < end_);
+    MOZ_ASSERT(offsets.normalEntry - begin_ <= UINT8_MAX);
+}
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -92,17 +92,16 @@ typedef UniquePtr<Bytes> UniqueBytes;
 typedef Vector<char, 0, SystemAllocPolicy> UTF8Bytes;
 
 typedef int8_t I8x16[16];
 typedef int16_t I16x8[8];
 typedef int32_t I32x4[4];
 typedef float F32x4[4];
 
 class Code;
-class CodeRange;
 class GlobalSegment;
 class Memory;
 class Module;
 class Instance;
 class Table;
 
 // To call Vector::podResizeToFit, a type must specialize mozilla::IsPod
 // which is pretty verbose to do within js::wasm, so factor that process out
@@ -815,16 +814,130 @@ struct FuncOffsets : CallableOffsets
     uint32_t normalEntry;
 
     void offsetBy(uint32_t offset) {
         CallableOffsets::offsetBy(offset);
         normalEntry += offset;
     }
 };
 
+// A CodeRange describes a single contiguous range of code within a wasm
+// module's code segment. A CodeRange describes what the code does and, for
+// function bodies, the name and source coordinates of the function.
+
+class CodeRange
+{
+  public:
+    enum Kind {
+        Function,          // function definition
+        Entry,             // calls into wasm from C++
+        ImportJitExit,     // fast-path calling from wasm into JIT code
+        ImportInterpExit,  // slow-path calling from wasm into C++ interp
+        ImportNativeExit,  // fast-path calling from wasm into a C++ native
+        TrapExit,          // calls C++ to report and jumps to throw stub
+        DebugTrap,         // calls C++ to handle debug event
+        FarJumpIsland,     // inserted to connect otherwise out-of-range insns
+        Inline,            // stub that is jumped-to within prologue/epilogue
+        Throw,             // special stack-unwinding stub
+        Interrupt          // stub executes asynchronously to interrupt wasm
+    };
+
+  private:
+    // All fields are treated as cacheable POD:
+    uint32_t begin_;
+    uint32_t ret_;
+    uint32_t end_;
+    uint32_t funcIndex_;
+    uint32_t funcLineOrBytecode_;
+    uint8_t funcBeginToNormalEntry_;
+    Kind kind_ : 8;
+
+  public:
+    CodeRange() = default;
+    CodeRange(Kind kind, Offsets offsets);
+    CodeRange(Kind kind, CallableOffsets offsets);
+    CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
+
+    // All CodeRanges have a begin and end.
+
+    uint32_t begin() const {
+        return begin_;
+    }
+    uint32_t end() const {
+        return end_;
+    }
+
+    // Other fields are only available for certain CodeRange::Kinds.
+
+    Kind kind() const {
+        return kind_;
+    }
+
+    bool isFunction() const {
+        return kind() == Function;
+    }
+    bool isImportExit() const {
+        return kind() == ImportJitExit || kind() == ImportInterpExit || kind() == ImportNativeExit;
+    }
+    bool isTrapExit() const {
+        return kind() == TrapExit;
+    }
+    bool isInline() const {
+        return kind() == Inline;
+    }
+    bool isThunk() const {
+        return kind() == FarJumpIsland;
+    }
+
+    // Every CodeRange except entry and inline stubs are callable and have a
+    // return statement. Asynchronous frame iteration needs to know the offset
+    // of the return instruction to calculate the frame pointer.
+
+    uint32_t ret() const {
+        MOZ_ASSERT(isFunction() || isImportExit() || isTrapExit());
+        return ret_;
+    }
+
+    // Function CodeRanges have two entry points: one for normal calls (with a
+    // known signature) and one for table calls (which involves dynamic
+    // signature checking).
+
+    uint32_t funcTableEntry() const {
+        MOZ_ASSERT(isFunction());
+        return begin_;
+    }
+    uint32_t funcNormalEntry() const {
+        MOZ_ASSERT(isFunction());
+        return begin_ + funcBeginToNormalEntry_;
+    }
+    uint32_t funcIndex() const {
+        MOZ_ASSERT(isFunction());
+        return funcIndex_;
+    }
+    uint32_t funcLineOrBytecode() const {
+        MOZ_ASSERT(isFunction());
+        return funcLineOrBytecode_;
+    }
+
+    // A sorted array of CodeRanges can be looked up via BinarySearch and PC.
+
+    struct PC {
+        size_t offset;
+        explicit PC(size_t offset) : offset(offset) {}
+        bool operator==(const CodeRange& rhs) const {
+            return offset >= rhs.begin() && offset < rhs.end();
+        }
+        bool operator<(const CodeRange& rhs) const {
+            return offset < rhs.begin();
+        }
+    };
+};
+
+WASM_DECLARE_POD_VECTOR(CodeRange, CodeRangeVector)
+
 // A wasm::Trap represents a wasm-defined trap that can occur during execution
 // which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap
 // symbolically, passing the bytecode offset to report as the trap offset. The
 // generated jump will be bound to a tiny stub which fills the offset and
 // then jumps to a per-Trap shared stub at the end of the module.
 
 enum class Trap
 {
@@ -1014,19 +1127,16 @@ enum class SymbolicAddress
     GrowMemory,
     CurrentMemory,
     Limit
 };
 
 bool
 IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode);
 
-void*
-AddressOf(SymbolicAddress imm);
-
 // Assumptions captures ambient state that must be the same when compiling and
 // deserializing a module for the compiled code to be valid. If it's not, then
 // the module must be recompiled from scratch.
 
 struct Assumptions
 {
     uint32_t              cpuId;
     JS::BuildIdCharVector buildId;
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -1,13 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import sys
+import logging
 import os
 import time
 import tempfile
 import traceback
 import urllib2
 
 import mozdevice
 import mozinfo
@@ -335,16 +336,18 @@ def run_test_harness(parser, options):
         'deviceRoot': options.remoteTestRoot,
         'host': options.deviceIP,
         'port': options.devicePort,
     }
 
     dm_args['adbPath'] = options.adb_path
     if not dm_args['host']:
         dm_args['deviceSerial'] = options.deviceSerial
+    if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
+        dm_args['logLevel'] = logging.DEBUG
 
     try:
         dm = mozdevice.DroidADB(**dm_args)
     except mozdevice.DMError:
         traceback.print_exc()
         print ("Automation Error: exception while initializing devicemanager.  "
                "Most likely the device is not in a testable state.")
         return 1
--- a/mobile/android/app/moz.build
+++ b/mobile/android/app/moz.build
@@ -1,14 +1,41 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('findbugs-exclude.xml'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('lint*'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('mobile*'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('ua-update.json.in'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('assets/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('omnijar/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('src/androidTest/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
+with Files('src/test/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
 for var in ('APP_NAME', 'APP_VERSION'):
     DEFINES[var] = CONFIG['MOZ_%s' % var]
 
 for var in ('MOZ_UPDATER', 'MOZ_APP_UA_NAME', 'ANDROID_PACKAGE_NAME'):
     DEFINES[var] = CONFIG[var]
 
 for var in ('MOZ_ANDROID_GCM', ):
     if CONFIG[var]:
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -1,14 +1,125 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('*.java.*'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('*Manifest*'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('adjust-sdk-sandbox.token'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('android-services.mozbuild'):
+    BUG_COMPONENT = ('Android Background Services', 'Android Sync')
+
+with Files('geckoview.ddf'):
+    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+
+with Files('crashreporter/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('java/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('java/org/mozilla/gecko/activitystream/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
+
+with Files('java/org/mozilla/gecko/cleanup/**'):
+    BUG_COMPONENT = ('Android Background Services', 'Firefox Health Report Service')
+
+with Files('java/org/mozilla/gecko/distribution/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Distributions')
+
+with Files('java/org/mozilla/gecko/firstrun/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'First Run')
+
+with Files('java/org/mozilla/gecko/home/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
+
+with Files('java/org/mozilla/gecko/icons/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Favicon Handling')
+
+with Files('java/org/mozilla/gecko/javaaddons/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('java/org/mozilla/gecko/mdns/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('java/org/mozilla/gecko/media/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
+
+with Files('java/org/mozilla/gecko/mdns/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Settings and Preferences')
+
+with Files('java/org/mozilla/gecko/reader/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Reader View')
+
+with Files('java/org/mozilla/gecko/restrictions/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Family Friendly Browsing')
+
+with Files('java/org/mozilla/gecko/telemetry/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Metrics')
+
+with Files('java/org/mozilla/gecko/text/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('java/org/mozilla/gecko/webapps/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Web Apps')
+
+with Files('java/org/mozilla/gecko/*LocaleManager*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Locale switching and selection')
+
+with Files('java/org/mozilla/gecko/*ChromeCast*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Screencasting')
+
+with Files('java/org/mozilla/gecko/*DynamicToolbar*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Graphics, Panning and Zooming')
+
+with Files('java/org/mozilla/gecko/*Presentation*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Screencasting')
+
+with Files('java/org/mozilla/gecko/*GuestSession*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Profile Handling')
+
+with Files('locales/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('resources/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('resources/anim/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Overlays')
+
+with Files('resources/raw/*favicon*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Favicon Handling')
+
+with Files('resources/xml*/*preference*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Settings and Preferences')
+
+with Files('resources/menu/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('resources/menu/*home*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
+
+with Files('resources/menu/*activitystream*'):
+    BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
+
+with Files('resources/menu/browsersearch_contextmenu.xml'):
+    BUG_COMPONENT = ('Firefox for Android', 'Awesomescreen')
+
 DIRS += ['locales']
 
 GENERATED_FILES += [
     '../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java',
     'generated/preprocessed/org/mozilla/gecko/AdjustConstants.java',
     'generated/preprocessed/org/mozilla/gecko/AppConstants.java',
 ]
 w = GENERATED_FILES['../geckoview/generated/preprocessed/org/mozilla/geckoview/BuildConfig.java']
--- a/mobile/android/chrome/moz.build
+++ b/mobile/android/chrome/moz.build
@@ -1,14 +1,21 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# NOTE: I think there are a few other possible components in this directory
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('geckoview/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+
 DIRS += ['geckoview']
 
 DEFINES['AB_CD'] = CONFIG['MOZ_UI_LOCALE']
 DEFINES['PACKAGE'] = 'browser'
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
 DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
 
--- a/mobile/android/components/moz.build
+++ b/mobile/android/components/moz.build
@@ -1,14 +1,20 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('extensions/**'):
+    BUG_COMPONENT = ('Toolkit', 'WebExtensions: Android')
+
 XPIDL_SOURCES += [
     'SessionStore.idl',
 ]
 
 XPIDL_MODULE = 'MobileComponents'
 
 EXTRA_COMPONENTS += [
     'AboutRedirector.js',
--- a/mobile/android/extensions/moz.build
+++ b/mobile/android/extensions/moz.build
@@ -1,11 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
 # Only include the following system add-ons if building Aurora or Nightly
 if not CONFIG['RELEASE_OR_BETA']:
     DIRS += [
         'flyweb',
     ]
--- a/mobile/android/fonts/moz.build
+++ b/mobile/android/fonts/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
 if not CONFIG['MOZ_ANDROID_EXCLUDE_FONTS']:
     RESOURCE_FILES.fonts += [
         'CharisSILCompact-B.ttf',
         'CharisSILCompact-BI.ttf',
         'CharisSILCompact-I.ttf',
         'CharisSILCompact-R.ttf',
         'ClearSans-Bold.ttf',
         'ClearSans-BoldItalic.ttf',
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -34,17 +34,17 @@ public class GeckoViewActivity extends A
 
         setContentView(R.layout.geckoview_activity);
 
         mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
         mGeckoView.setChromeDelegate(new MyGeckoViewChrome());
         mGeckoView.setContentListener(new MyGeckoViewContent());
         mGeckoView.setProgressListener(new MyGeckoViewProgress());
 
-        final GeckoProfile profile = GeckoProfile.get(getApplicationContext());
+        final GeckoProfile profile = GeckoProfile.get(this);
 
         GeckoThread.initMainProcess(profile, /* args */ null, /* debugging */ false);
         GeckoThread.launch();
 
         Uri u = getIntent().getData();
         if (u != null) {
             mGeckoView.loadUri(u.toString());
         } else {
--- a/mobile/android/installer/moz.build
+++ b/mobile/android/installer/moz.build
@@ -1,6 +1,8 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
--- a/mobile/android/javaaddons/moz.build
+++ b/mobile/android/javaaddons/moz.build
@@ -1,11 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
 jar = add_java_jar('javaaddons-1.0')
 jar.sources = [
     'java/org/mozilla/javaaddons/JavaAddonInterfaceV1.java',
 ]
 jar.javac_flags += ['-Xlint:all']
--- a/mobile/android/locales/moz.build
+++ b/mobile/android/locales/moz.build
@@ -1,7 +1,10 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
 JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -1,14 +1,27 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# Most files are General, a few exceptions
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('DownloadNotifications.jsm'):
+    BUG_COMPONENT = ('Firefox for Android', 'Download Manager')
+
+with Files('HomeProvider.jsm'):
+    BUG_COMPONENT = ('Firefox for Android', 'Data Providers')
+
+with Files('geckoview/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+
 DIRS += ['geckoview']
 
 EXTRA_JS_MODULES += [
     'Accounts.jsm',
     'dbg-browser-actors.js',
     'DelayedInit.jsm',
     'DownloadNotifications.jsm',
     'FxAccountsWebChannel.jsm',
--- a/mobile/android/moz.build
+++ b/mobile/android/moz.build
@@ -1,14 +1,59 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('bouncer/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Distributions')
+
+with Files('branding/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('build/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('config/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('docs/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
+with Files('geckoview/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+
+with Files('geckoview/src/main/aidl/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
+
+with Files('geckoview/src/main/java/org/mozilla/gecko/mozglue/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
+
+with Files('geckoview_example/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+
+with Files('gradle/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
+
+with Files('search/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Search Activity')
+
+with Files('services/**'):
+    BUG_COMPONENT = ('Android Background Services', 'Android Sync')
+
+with Files('themes/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Theme and Visual Design')
+
+with Files('thirdparty/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'General')
+
 CONFIGURE_SUBST_FILES += ['installer/Makefile']
 
 DIRS += [
     '../locales',
     'locales',
 ]
 
 if CONFIG['MOZ_ANDROID_MLS_STUMBLER']:
--- a/mobile/android/stumbler/moz.build
+++ b/mobile/android/stumbler/moz.build
@@ -1,12 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Android Background Services', 'Geolocation')
+
 include('stumbler_sources.mozbuild')
 
 stumbler_jar = add_java_jar('stumbler')
 stumbler_jar.sources += stumbler_sources
 stumbler_jar.extra_jars += [CONFIG['ANDROID_SUPPORT_V4_AAR_LIB']]
 stumbler_jar.javac_flags += ['-Xlint:all']
--- a/mobile/android/tests/background/moz.build
+++ b/mobile/android/tests/background/moz.build
@@ -1,9 +1,12 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Android Background Services', 'Build & Test')
+
 TEST_DIRS += [
     'junit3',
 ]
--- a/mobile/android/tests/browser/moz.build
+++ b/mobile/android/tests/browser/moz.build
@@ -1,14 +1,27 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
+with Files('chrome/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
+with Files('junit3/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
+# Ideally split this up, but testing catches many files
+with Files('robocop/**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
 MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
 
 if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']:
     TEST_DIRS += [
         'junit3',
     ]
 
 TEST_DIRS += [
--- a/mobile/android/tests/javaaddons/moz.build
+++ b/mobile/android/tests/javaaddons/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
 ANDROID_APK_NAME = 'javaaddons-test'
 ANDROID_APK_PACKAGE = 'org.mozilla.javaaddons.test'
 
 jar = add_java_jar('javaaddons-test')
 jar.extra_jars += [
     TOPOBJDIR + '/mobile/android/javaaddons/javaaddons-1.0.jar',
 ]
 jar.javac_flags += ['-Xlint:all']
--- a/mobile/android/tests/moz.build
+++ b/mobile/android/tests/moz.build
@@ -1,14 +1,18 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# catch all for new files
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Testing')
+
 if not CONFIG['MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE']:
     TEST_DIRS += [
         'background',
     ]
 
 TEST_DIRS += [
     'browser',
     'javaaddons', # Must be built before browser/robocop/roboextender.
--- a/mobile/locales/moz.build
+++ b/mobile/locales/moz.build
@@ -1,7 +1,10 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files('**'):
+    BUG_COMPONENT = ('Firefox for Android', 'Locale Switching')
+
 JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
--- a/modules/fdlibm/patches/12_define_u_int32_t_and_u_int64_t_on_windows.patch
+++ b/modules/fdlibm/patches/12_define_u_int32_t_and_u_int64_t_on_windows.patch
@@ -1,22 +1,24 @@
 diff --git a/modules/fdlibm/src/math_private.h b/modules/fdlibm/src/math_private.h
 --- a/modules/fdlibm/src/math_private.h
 +++ b/modules/fdlibm/src/math_private.h
-@@ -33,16 +33,21 @@
+@@ -33,16 +33,23 @@
   * to dig two 32 bit words out of the 64 bit IEEE floating point
   * value.  That is non-ANSI, and, moreover, the gcc instruction
   * scheduler gets it wrong.  We instead use the following macros.
   * Unlike the original code, we determine the endianness at compile
   * time, not at run time; I don't see much benefit to selecting
   * endianness at run time.
   */
  
-+#ifdef WIN32
++#ifndef u_int32_t
 +#define u_int32_t uint32_t
++#endif
++#ifndef u_int64_t
 +#define u_int64_t uint64_t
 +#endif
 +
  /*
   * A union which permits us to convert between a double and two 32 bit
   * ints.
   */
  
--- a/modules/fdlibm/src/math_private.h
+++ b/modules/fdlibm/src/math_private.h
@@ -33,18 +33,20 @@
  * to dig two 32 bit words out of the 64 bit IEEE floating point
  * value.  That is non-ANSI, and, moreover, the gcc instruction
  * scheduler gets it wrong.  We instead use the following macros.
  * Unlike the original code, we determine the endianness at compile
  * time, not at run time; I don't see much benefit to selecting
  * endianness at run time.
  */
 
-#ifdef WIN32
+#ifndef u_int32_t
 #define u_int32_t uint32_t
+#endif
+#ifndef u_int64_t
 #define u_int64_t uint64_t
 #endif
 
 /*
  * A union which permits us to convert between a double and two 32 bit
  * ints.
  */
 
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/PContent.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #include "nsXULAppAPI.h"
 
 #include "mozilla/Preferences.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDataHashtable.h"
 #include "nsDirectoryServiceDefs.h"
@@ -473,20 +474,21 @@ Preferences::IsServiceAvailable()
   return !!sPreferences;
 }
 
 // static
 bool
 Preferences::InitStaticMembers()
 {
 #ifndef MOZ_B2G
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
 #endif
 
   if (!sShutdown && !sPreferences) {
+    MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIPrefService> prefService =
       do_GetService(NS_PREFSERVICE_CONTRACTID);
   }
 
   return sPreferences != nullptr;
 }
 
 // static
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -9,22 +9,26 @@
 interface nsIChannel;
 interface nsIConsoleReportCollector;
 interface nsIOutputStream;
 interface nsIURI;
 
 %{C++
 #include "nsIConsoleReportCollector.h"
 namespace mozilla {
+class TimeStamp;
+
 namespace dom {
 class ChannelInfo;
 }
 }
 %}
 
+native TimeStamp(mozilla::TimeStamp);
+
 [ptr] native ChannelInfo(mozilla::dom::ChannelInfo);
 
 /**
  * Interface to allow implementors of nsINetworkInterceptController to control the behaviour
  * of intercepted channels without tying implementation details of the interception to
  * the actual channel. nsIInterceptedChannel is expected to be implemented by objects
  * which do not implement nsIChannel.
  */
@@ -92,16 +96,40 @@ interface nsIInterceptedChannel : nsISup
      * Get the internal load type from the underlying channel.
      */
     [noscript]
     readonly attribute nsContentPolicyType internalContentPolicyType;
 
     [noscript]
     readonly attribute nsIConsoleReportCollector consoleReportCollector;
 
+    /**
+     * Save the timestamps of various service worker interception phases.
+     */
+    [noscript]
+    void SetLaunchServiceWorkerStart(in TimeStamp aTimeStamp);
+
+    [noscript]
+    void SetLaunchServiceWorkerEnd(in TimeStamp aTimeStamp);
+
+    [noscript]
+    void SetDispatchFetchEventStart(in TimeStamp aTimeStamp);
+
+    [noscript]
+    void SetDispatchFetchEventEnd(in TimeStamp aTimeStamp);
+
+    [noscript]
+    void SetHandleFetchEventStart(in TimeStamp aTimeStamp);
+
+    [noscript]
+    void SetHandleFetchEventEnd(in TimeStamp aTimeStamp);
+
+    [noscript]
+    void SaveTimeStampsToUnderlyingChannel();
+
 %{C++
     already_AddRefed<nsIConsoleReportCollector>
     GetConsoleReportCollector()
     {
       nsCOMPtr<nsIConsoleReportCollector> reporter;
       GetConsoleReportCollector(getter_AddRefs(reporter));
       return reporter.forget();
     }
--- a/netwerk/base/nsITimedChannel.idl
+++ b/netwerk/base/nsITimedChannel.idl
@@ -21,16 +21,25 @@ interface nsITimedChannel : nsISupports 
   attribute boolean timingEnabled;
 
   // The number of redirects
   attribute uint16_t redirectCount;
 
   [noscript] readonly attribute TimeStamp channelCreation;
   [noscript] readonly attribute TimeStamp asyncOpen;
 
+  // The following are only set when the request is intercepted by a service
+  // worker no matter the response is synthesized.
+  [noscript] attribute TimeStamp launchServiceWorkerStart;
+  [noscript] attribute TimeStamp launchServiceWorkerEnd;
+  [noscript] attribute TimeStamp dispatchFetchEventStart;
+  [noscript] attribute TimeStamp dispatchFetchEventEnd;
+  [noscript] attribute TimeStamp handleFetchEventStart;
+  [noscript] attribute TimeStamp handleFetchEventEnd;
+
   // The following are only set when the document is not (only) read from the
   // cache
   [noscript] readonly attribute TimeStamp domainLookupStart;
   [noscript] readonly attribute TimeStamp domainLookupEnd;
   [noscript] readonly attribute TimeStamp connectStart;
   [noscript] readonly attribute TimeStamp connectEnd;
   [noscript] readonly attribute TimeStamp requestStart;
   [noscript] readonly attribute TimeStamp responseStart;
@@ -61,16 +70,22 @@ interface nsITimedChannel : nsISupports 
   // The following are only set if the document is (partially) read from the
   // cache
   [noscript] readonly attribute TimeStamp cacheReadStart;
   [noscript] readonly attribute TimeStamp cacheReadEnd;
 
   // All following are PRTime versions of the above.
   readonly attribute PRTime channelCreationTime;
   readonly attribute PRTime asyncOpenTime;
+  readonly attribute PRTime launchServiceWorkerStartTime;
+  readonly attribute PRTime launchServiceWorkerEndTime;
+  readonly attribute PRTime dispatchFetchEventStartTime;
+  readonly attribute PRTime dispatchFetchEventEndTime;
+  readonly attribute PRTime handleFetchEventStartTime;
+  readonly attribute PRTime handleFetchEventEndTime;
   readonly attribute PRTime domainLookupStartTime;
   readonly attribute PRTime domainLookupEndTime;
   readonly attribute PRTime connectStartTime;
   readonly attribute PRTime connectEndTime;
   readonly attribute PRTime requestStartTime;
   readonly attribute PRTime responseStartTime;
   readonly attribute PRTime responseEndTime;
   readonly attribute PRTime cacheReadStartTime;
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -651,20 +651,16 @@ bool NS_HasBeenCrossOrigin(nsIChannel* a
 // should also be changed.
 #define NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN \
   "safebrowsing.86868755-6b82-4842-b301-72671a0db32e.mozilla"
 
 // Unique first-party domain for separating about uri.
 #define ABOUT_URI_FIRST_PARTY_DOMAIN \
   "about.ef2a7dd5-93bc-417f-a698-142c3116864f.mozilla"
 
-// Unique first-party domain for separating null principal.
-#define NULL_PRINCIPAL_FIRST_PARTY_DOMAIN \
-  "1f1841ad-0395-48ba-aec4-c98ee3f6e614.mozilla"
-
 /**
  * Determines whether appcache should be checked for a given URI.
  */
 bool NS_ShouldCheckAppCache(nsIURI *aURI, bool usePrivateBrowsing);
 
 bool NS_ShouldCheckAppCache(nsIPrincipal *aPrincipal, bool usePrivateBrowsing);
 
 /**
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -14,16 +14,17 @@ include URIParams;
 include IPCStream;
 include PBackgroundSharedTypes;
 
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
 using struct nsHttpAtom from "nsHttp.h";
 using class nsHttpResponseHead from "nsHttpResponseHead.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // LoadInfo IPDL structs
 //-----------------------------------------------------------------------------
 
@@ -127,16 +128,22 @@ struct HttpChannelOpenArgs
   bool                        blockAuthPrompt;
   bool                        suspendAfterSynthesizeResponse;
   bool                        allowStaleCacheContent;
   nsCString                   contentTypeHint;
   uint64_t                    channelId;
   uint64_t                    contentWindowId;
   nsCString                   preferredAlternativeType;
   uint64_t                    topLevelOuterContentWindowId;
+  TimeStamp                   launchServiceWorkerStart;
+  TimeStamp                   launchServiceWorkerEnd;
+  TimeStamp                   dispatchFetchEventStart;
+  TimeStamp                   dispatchFetchEventEnd;
+  TimeStamp                   handleFetchEventStart;
+  TimeStamp                   handleFetchEventEnd;
 };
 
 struct HttpChannelConnectArgs
 {
   uint32_t registrarId;
   bool shouldIntercept;
 };
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3613,16 +3613,94 @@ HttpBaseChannel::TimingAllowCheck(nsIPri
     return NS_OK;
   }
 
   *_retval = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::GetLaunchServiceWorkerStart(TimeStamp* _retval) {
+  MOZ_ASSERT(_retval);
+  *_retval = mLaunchServiceWorkerStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) {
+  mLaunchServiceWorkerStart = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetLaunchServiceWorkerEnd(TimeStamp* _retval) {
+  MOZ_ASSERT(_retval);
+  *_retval = mLaunchServiceWorkerEnd;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) {
+  mLaunchServiceWorkerEnd = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetDispatchFetchEventStart(TimeStamp* _retval) {
+  MOZ_ASSERT(_retval);
+  *_retval = mDispatchFetchEventStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetDispatchFetchEventStart(TimeStamp aTimeStamp) {
+  mDispatchFetchEventStart = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetDispatchFetchEventEnd(TimeStamp* _retval) {
+  MOZ_ASSERT(_retval);
+  *_retval = mDispatchFetchEventEnd;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetDispatchFetchEventEnd(TimeStamp aTimeStamp) {
+  mDispatchFetchEventEnd = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetHandleFetchEventStart(TimeStamp* _retval) {
+  MOZ_ASSERT(_retval);
+  *_retval = mHandleFetchEventStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetHandleFetchEventStart(TimeStamp aTimeStamp) {
+  mHandleFetchEventStart = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetHandleFetchEventEnd(TimeStamp* _retval) {
+  MOZ_ASSERT(_retval);
+  *_retval = mHandleFetchEventEnd;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetHandleFetchEventEnd(TimeStamp aTimeStamp) {
+  mHandleFetchEventEnd = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) {
   *_retval = mTransactionTimings.domainLookupStart;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetDomainLookupEnd(TimeStamp* _retval) {
   *_retval = mTransactionTimings.domainLookupEnd;
@@ -3696,16 +3774,22 @@ HttpBaseChannel::Get##name##Time(PRTime*
     }                                                          \
     *_retval = mChannelCreationTime +                          \
         (PRTime) ((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \
     return NS_OK;                                              \
 }
 
 IMPL_TIMING_ATTR(ChannelCreation)
 IMPL_TIMING_ATTR(AsyncOpen)
+IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
+IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
+IMPL_TIMING_ATTR(DispatchFetchEventStart)
+IMPL_TIMING_ATTR(DispatchFetchEventEnd)
+IMPL_TIMING_ATTR(HandleFetchEventStart)
+IMPL_TIMING_ATTR(HandleFetchEventEnd)
 IMPL_TIMING_ATTR(DomainLookupStart)
 IMPL_TIMING_ATTR(DomainLookupEnd)
 IMPL_TIMING_ATTR(ConnectStart)
 IMPL_TIMING_ATTR(ConnectEnd)
 IMPL_TIMING_ATTR(RequestStart)
 IMPL_TIMING_ATTR(ResponseStart)
 IMPL_TIMING_ATTR(ResponseEnd)
 IMPL_TIMING_ATTR(CacheReadStart)
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -555,16 +555,22 @@ protected:
   // the response of the last redirect.
   mozilla::TimeStamp                mRedirectEndTimeStamp;
 
   PRTime                            mChannelCreationTime;
   TimeStamp                         mChannelCreationTimestamp;
   TimeStamp                         mAsyncOpenTime;
   TimeStamp                         mCacheReadStart;
   TimeStamp                         mCacheReadEnd;
+  TimeStamp                         mLaunchServiceWorkerStart;
+  TimeStamp                         mLaunchServiceWorkerEnd;
+  TimeStamp                         mDispatchFetchEventStart;
+  TimeStamp                         mDispatchFetchEventEnd;
+  TimeStamp                         mHandleFetchEventStart;
+  TimeStamp                         mHandleFetchEventEnd;
   // copied from the transaction before we null out mTransaction
   // so that the timing can still be queried from OnStopRequest
   TimingStruct                      mTransactionTimings;
 
   bool                              mForcePending;
 
   bool mCorsIncludeCredentials;
   uint32_t mCorsMode;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2418,16 +2418,23 @@ HttpChannelChild::ContinueAsyncOpen()
     return NS_ERROR_FAILURE;
   }
 
   ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
   if (cc->IsShuttingDown()) {
     return NS_ERROR_FAILURE;
   }
 
+  openArgs.launchServiceWorkerStart() = mLaunchServiceWorkerStart;
+  openArgs.launchServiceWorkerEnd()   = mLaunchServiceWorkerEnd;
+  openArgs.dispatchFetchEventStart()  = mDispatchFetchEventStart;
+  openArgs.dispatchFetchEventEnd()    = mDispatchFetchEventEnd;
+  openArgs.handleFetchEventStart()    = mHandleFetchEventStart;
+  openArgs.handleFetchEventEnd()      = mHandleFetchEventEnd;
+
   // This must happen before the constructor message is sent. Otherwise messages
   // from the parent could arrive quickly and be delivered to the wrong event
   // target.
   SetEventTarget();
 
   // The socket transport in the chrome process now holds a logical ref to us
   // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -127,17 +127,23 @@ HttpChannelParent::Init(const HttpChanne
                        a.appCacheClientID(), a.allowSpdy(), a.allowAltSvc(), a.beConservative(),
                        a.loadInfo(), a.synthesizedResponseHead(),
                        a.synthesizedSecurityInfoSerialization(),
                        a.cacheKey(), a.requestContextID(), a.preflightArgs(),
                        a.initialRwin(), a.blockAuthPrompt(),
                        a.suspendAfterSynthesizeResponse(),
                        a.allowStaleCacheContent(), a.contentTypeHint(),
                        a.channelId(), a.contentWindowId(), a.preferredAlternativeType(),
-                       a.topLevelOuterContentWindowId());
+                       a.topLevelOuterContentWindowId(),
+                       a.launchServiceWorkerStart(),
+                       a.launchServiceWorkerEnd(),
+                       a.dispatchFetchEventStart(),
+                       a.dispatchFetchEventEnd(),
+                       a.handleFetchEventStart(),
+                       a.handleFetchEventEnd());
   }
   case HttpChannelCreationArgs::THttpChannelConnectArgs:
   {
     const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
     return ConnectChannel(cArgs.registrarId(), cArgs.shouldIntercept());
   }
   default:
     NS_NOTREACHED("unknown open type");
@@ -326,17 +332,23 @@ HttpChannelParent::DoAsyncOpen(  const U
                                  const uint32_t&            aInitialRwin,
                                  const bool&                aBlockAuthPrompt,
                                  const bool&                aSuspendAfterSynthesizeResponse,
                                  const bool&                aAllowStaleCacheContent,
                                  const nsCString&           aContentTypeHint,
                                  const uint64_t&            aChannelId,
                                  const uint64_t&            aContentWindowId,
                                  const nsCString&           aPreferredAlternativeType,
-                                 const uint64_t&            aTopLevelOuterContentWindowId)
+                                 const uint64_t&            aTopLevelOuterContentWindowId,
+                                 const TimeStamp&           aLaunchServiceWorkerStart,
+                                 const TimeStamp&           aLaunchServiceWorkerEnd,
+                                 const TimeStamp&           aDispatchFetchEventStart,
+                                 const TimeStamp&           aDispatchFetchEventEnd,
+                                 const TimeStamp&           aHandleFetchEventStart,
+                                 const TimeStamp&           aHandleFetchEventEnd)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
     // null deref here.
     return false;
   }
   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
@@ -535,16 +547,23 @@ HttpChannelParent::DoAsyncOpen(  const U
   mChannel->SetAllowSTS(allowSTS);
   mChannel->SetThirdPartyFlags(thirdPartyFlags);
   mChannel->SetAllowSpdy(allowSpdy);
   mChannel->SetAllowAltSvc(allowAltSvc);
   mChannel->SetBeConservative(beConservative);
   mChannel->SetInitialRwin(aInitialRwin);
   mChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
 
+  mChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
+  mChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
+  mChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
+  mChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
+  mChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
+  mChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
+
   nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
     do_QueryObject(mChannel);
   nsCOMPtr<nsIApplicationCacheService> appCacheService =
     do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
 
   bool setChooseApplicationCache = chooseApplicationCache;
   if (appCacheChan && appCacheService) {
     // We might potentially want to drop this flag (that is TRUE by default)
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -144,17 +144,23 @@ protected:
               const uint32_t&            aInitialRwin,
               const bool&                aBlockAuthPrompt,
               const bool&                aSuspendAfterSynthesizeResponse,
               const bool&                aAllowStaleCacheContent,
               const nsCString&           aContentTypeHint,
               const uint64_t&            aChannelId,
               const uint64_t&            aContentWindowId,
               const nsCString&           aPreferredAlternativeType,
-              const uint64_t&            aTopLevelOuterContentWindowId);
+              const uint64_t&            aTopLevelOuterContentWindowId,
+              const TimeStamp&           aLaunchServiceWorkerStart,
+              const TimeStamp&           aLaunchServiceWorkerEnd,
+              const TimeStamp&           aDispatchFetchEventStart,
+              const TimeStamp&           aDispatchFetchEventEnd,
+              const TimeStamp&           aHandleFetchEventStart,
+              const TimeStamp&           aHandleFetchEventEnd);
 
   virtual mozilla::ipc::IPCResult RecvSetPriority(const int16_t& priority) override;
   virtual mozilla::ipc::IPCResult RecvSetClassOfService(const uint32_t& cos) override;
   virtual mozilla::ipc::IPCResult RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual mozilla::ipc::IPCResult RecvSuspend() override;
   virtual mozilla::ipc::IPCResult RecvResume() override;
   virtual mozilla::ipc::IPCResult RecvCancel(const nsresult& status) override;
   virtual mozilla::ipc::IPCResult RecvRedirect2Verify(const nsresult& result,
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HttpLog.h"
 
 #include "InterceptedChannel.h"
 #include "nsInputStreamPump.h"
 #include "nsIPipe.h"
 #include "nsIStreamListener.h"
+#include "nsITimedChannel.h"
 #include "nsHttpChannel.h"
 #include "HttpChannelChild.h"
 #include "nsHttpResponseHead.h"
 #include "nsNetUtil.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/dom/ChannelInfo.h"
 #include "nsIChannelEventSink.h"
 
@@ -126,16 +127,50 @@ InterceptedChannelBase::SetReleaseHandle
   MOZ_ASSERT(!mReleaseHandle);
   MOZ_ASSERT(aHandle);
 
   // We need to keep it and mChannel alive until destructor clear it up.
   mReleaseHandle = aHandle;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+InterceptedChannelBase::SaveTimeStampsToUnderlyingChannel()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIChannel> underlyingChannel;
+  nsresult rv = GetChannel(getter_AddRefs(underlyingChannel));
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  nsCOMPtr<nsITimedChannel> timedChannel =
+    do_QueryInterface(underlyingChannel);
+  MOZ_ASSERT(timedChannel);
+
+  rv = timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  rv = timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  rv = timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  rv = timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  rv = timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  rv = timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  return rv;
+}
+
 /* static */
 already_AddRefed<nsIURI>
 InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
--- a/netwerk/protocol/http/InterceptedChannel.h
+++ b/netwerk/protocol/http/InterceptedChannel.h
@@ -43,30 +43,81 @@ protected:
 
   void EnsureSynthesizedResponse();
   void DoNotifyController();
   MOZ_MUST_USE nsresult DoSynthesizeStatus(uint16_t aStatus,
                                            const nsACString& aReason);
   MOZ_MUST_USE nsresult DoSynthesizeHeader(const nsACString& aName,
                                            const nsACString& aValue);
 
+  TimeStamp mLaunchServiceWorkerStart;
+  TimeStamp mLaunchServiceWorkerEnd;
+  TimeStamp mDispatchFetchEventStart;
+  TimeStamp mDispatchFetchEventEnd;
+  TimeStamp mHandleFetchEventStart;
+  TimeStamp mHandleFetchEventEnd;
+
   virtual ~InterceptedChannelBase();
 public:
   explicit InterceptedChannelBase(nsINetworkInterceptController* aController);
 
   // Notify the interception controller that the channel has been intercepted
   // and prepare the response body output stream.
   virtual void NotifyController() = 0;
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetResponseBody(nsIOutputStream** aOutput) override;
   NS_IMETHOD GetConsoleReportCollector(nsIConsoleReportCollector** aCollectorOut) override;
   NS_IMETHOD SetReleaseHandle(nsISupports* aHandle) override;
 
+  NS_IMETHODIMP
+  SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) override
+  {
+    mLaunchServiceWorkerStart = aTimeStamp;
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) override
+  {
+    mLaunchServiceWorkerEnd = aTimeStamp;
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  SetDispatchFetchEventStart(TimeStamp aTimeStamp) override
+  {
+    mDispatchFetchEventStart = aTimeStamp;
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  SetDispatchFetchEventEnd(TimeStamp aTimeStamp) override
+  {
+    mDispatchFetchEventEnd = aTimeStamp;
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  SetHandleFetchEventStart(TimeStamp aTimeStamp) override
+  {
+    mHandleFetchEventStart = aTimeStamp;
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP
+  SetHandleFetchEventEnd(TimeStamp aTimeStamp) override
+  {
+    mHandleFetchEventEnd = aTimeStamp;
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP SaveTimeStampsToUnderlyingChannel() override;
+
   static already_AddRefed<nsIURI>
   SecureUpgradeChannelURI(nsIChannel* aChannel);
 };
 
 class InterceptedChannelChrome : public InterceptedChannelBase
 {
   // The actual channel being intercepted.
   RefPtr<nsHttpChannel> mChannel;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -578,16 +578,100 @@ NullHttpChannel::GetChannelCreation(mozi
 NS_IMETHODIMP
 NullHttpChannel::GetAsyncOpen(mozilla::TimeStamp *aAsyncOpen)
 {
   *aAsyncOpen = mAsyncOpenTime;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::GetLaunchServiceWorkerStart(mozilla::TimeStamp *_retval)
+{
+  MOZ_ASSERT(_retval);
+  *_retval = mAsyncOpenTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetLaunchServiceWorkerStart(mozilla::TimeStamp aTimeStamp)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetLaunchServiceWorkerEnd(mozilla::TimeStamp *_retval)
+{
+  MOZ_ASSERT(_retval);
+  *_retval = mAsyncOpenTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetLaunchServiceWorkerEnd(mozilla::TimeStamp aTimeStamp)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetDispatchFetchEventStart(mozilla::TimeStamp *_retval)
+{
+  MOZ_ASSERT(_retval);
+  *_retval = mAsyncOpenTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetDispatchFetchEventStart(mozilla::TimeStamp aTimeStamp)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetDispatchFetchEventEnd(mozilla::TimeStamp *_retval)
+{
+  MOZ_ASSERT(_retval);
+  *_retval = mAsyncOpenTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetDispatchFetchEventEnd(mozilla::TimeStamp aTimeStamp)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetHandleFetchEventStart(mozilla::TimeStamp *_retval)
+{
+  MOZ_ASSERT(_retval);
+  *_retval = mAsyncOpenTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetHandleFetchEventStart(mozilla::TimeStamp aTimeStamp)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::GetHandleFetchEventEnd(mozilla::TimeStamp *_retval)
+{
+  MOZ_ASSERT(_retval);
+  *_retval = mAsyncOpenTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NullHttpChannel::SetHandleFetchEventEnd(mozilla::TimeStamp aTimeStamp)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::GetDomainLookupStart(mozilla::TimeStamp *aDomainLookupStart)
 {
   *aDomainLookupStart = mAsyncOpenTime;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetDomainLookupEnd(mozilla::TimeStamp *aDomainLookupEnd)
@@ -767,16 +851,22 @@ NullHttpChannel::Get##name##Time(PRTime*
     }                                                          \
     *_retval = mChannelCreationTime +                          \
         (PRTime) ((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \
     return NS_OK;                                              \
 }
 
 IMPL_TIMING_ATTR(ChannelCreation)
 IMPL_TIMING_ATTR(AsyncOpen)
+IMPL_TIMING_ATTR(LaunchServiceWorkerStart)
+IMPL_TIMING_ATTR(LaunchServiceWorkerEnd)
+IMPL_TIMING_ATTR(DispatchFetchEventStart)
+IMPL_TIMING_ATTR(DispatchFetchEventEnd)
+IMPL_TIMING_ATTR(HandleFetchEventStart)
+IMPL_TIMING_ATTR(HandleFetchEventEnd)
 IMPL_TIMING_ATTR(DomainLookupStart)
 IMPL_TIMING_ATTR(DomainLookupEnd)
 IMPL_TIMING_ATTR(ConnectStart)
 IMPL_TIMING_ATTR(ConnectEnd)
 IMPL_TIMING_ATTR(RequestStart)
 IMPL_TIMING_ATTR(ResponseStart)
 IMPL_TIMING_ATTR(ResponseEnd)
 IMPL_TIMING_ATTR(CacheReadStart)
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5362,16 +5362,28 @@ nsHttpChannel::SetupReplacementChannel(n
             rv = newChannel->GetLoadFlags(&loadFlags);
             NS_ENSURE_SUCCESS(rv, rv);
             loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
             rv = newChannel->SetLoadFlags(loadFlags);
             NS_ENSURE_SUCCESS(rv, rv);
         }
     }
 
+    if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
+      nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
+      if (timedChannel) {
+        timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
+        timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
+        timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
+        timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
+        timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
+        timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
+      }
+    }
+
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType)
 {
     LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",
         this, redirectType));
--- a/parser/xml/moz.build
+++ b/parser/xml/moz.build
@@ -25,15 +25,15 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'saxparser'
 
 EXPORTS += [
     'nsSAXAttributes.h',
     'nsSAXLocator.h',
     'nsSAXXMLReader.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'nsSAXAttributes.cpp',
     'nsSAXLocator.cpp',
     'nsSAXXMLReader.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -141,17 +141,17 @@ android-api-15-gradle/opt:
         max-run-time: 7200
         env:
             # Bug 1292762 - Set GRADLE_USER_HOME to avoid sdk-manager-plugin intermittent
             GRADLE_USER_HOME: /home/worker/workspace/build/src/dotgradle
         artifacts:
           - name: public/android/maven
             path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
             type: directory
-          - name: public/android/geckoview_example.apk
+          - name: public/build/geckoview_example.apk
             path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/geckoview_example-withGeckoBinaries.apk
             type: file
           - name: public/build
             path: /home/worker/artifacts/
             type: directory
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -236,12 +236,13 @@ android-debug-tests:
     - marionette
 
 android-opt-tests:
     - robocop
 
 android-gradle-tests:
     - mochitest-chrome
     - robocop
+    - geckoview
 
 android-x86-tests:
     - mochitest-chrome
     - xpcshell
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -601,16 +601,17 @@ mochitest-chrome:
                 extra-options:
                     - --mochitest-suite=chrome
 
 mochitest-clipboard:
     description: "Mochitest clipboard run"
     suite: mochitest/clipboard
     treeherder-symbol: tc-M(cl)
     loopback-video: true
+    docker-image: {"in-tree": "desktop1604-test"}
     instance-size: xlarge
     e10s:
       by-test-platform:
         macosx64/debug: true
         default: both
     mozharness:
         by-test-platform:
             android.*:
@@ -909,16 +910,17 @@ mochitest-webgl:
                 extra-options:
                     - --mochitest-suite=mochitest-gl
 
 mochitest-style:
     description: "Mochitest plain run for style system"
     suite: mochitest/plain-style
     treeherder-symbol: tc-M(s)
     loopback-video: true
+    docker-image: {"in-tree": "desktop1604-test"}
     e10s: both
     run-on-projects:
         by-test-platform:
             linux64-stylo/.*: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
             default: ['all']
     mozharness:
         mochitest-flavor: plain
         script: desktop_unittest.py
@@ -931,16 +933,17 @@ mochitest-style:
         extra-options:
             - --mochitest-suite=plain-style
 
 mochitest-chrome-style:
     description: "Mochitest chrome run for style system"
     suite: mochitest/chrome-style
     treeherder-symbol: tc-M(cs)
     loopback-video: true
+    docker-image: {"in-tree": "desktop1604-test"}
     run-on-projects:
         by-test-platform:
             linux64-stylo/.*: [ 'stylo', 'autoland', 'mozilla-inbound', 'mozilla-central', 'try' ]
             default: ['all']
     e10s: false
     mozharness:
         mochitest-flavor: chrome
         script: desktop_unittest.py
@@ -1091,16 +1094,31 @@ robocop:
     mozharness:
         script: android_emulator_unittest.py
         no-read-buildbot-config: true
         config:
             - android/androidarm_4_3.py
         extra-options:
             - --test-suite=robocop
 
+geckoview:
+    description: "Geckoview run"
+    suite: geckoview
+    treeherder-symbol: tc(gv)
+    instance-size: xlarge
+    loopback-video: true
+    e10s: false
+    mozharness:
+        script: android_emulator_unittest.py
+        no-read-buildbot-config: true
+        config:
+            - android/androidarm_4_3.py
+        extra-options:
+            - --test-suite=geckoview
+
 talos-chrome:
     description: "Talos chrome"
     suite: talos
     talos-try-name: chromez
     treeherder-symbol: tc-T(c)
     run-on-projects:
         by-test-platform:
             linux64-stylo/.*: ['mozilla-central', 'try']
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -336,17 +336,20 @@ def set_defaults(config, tests):
 
 @transforms.add
 def set_target(config, tests):
     for test in tests:
         build_platform = test['build-platform']
         if build_platform.startswith('macosx'):
             target = 'target.dmg'
         elif build_platform.startswith('android'):
-            target = 'target.apk'
+            if 'geckoview' in test['test-name']:
+                target = 'geckoview_example.apk'
+            else:
+                target = 'target.apk'
         elif build_platform.startswith('win'):
             target = 'firefox-{}.en-US.{}.zip'.format(
                 get_firefox_version(),
                 build_platform.split('/')[0]
             )
         else:
             target = 'target.tar.bz2'
         test['mozharness']['build-artifact-name'] = 'public/build/' + target
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -2,16 +2,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from abc import ABCMeta, abstractmethod, abstractproperty
 from argparse import ArgumentParser, SUPPRESS
 from distutils.util import strtobool
 from itertools import chain
 from urlparse import urlparse
+import logging
 import json
 import os
 import tempfile
 
 from mozdevice import DroidADB
 from mozprofile import DEFAULT_PORTS
 import mozinfo
 import mozlog
@@ -952,16 +953,19 @@ class AndroidArguments(ArgumentContainer
 
         device_args = {'deviceRoot': options.remoteTestRoot}
         device_args['adbPath'] = options.adbPath
         if options.deviceIP:
             device_args['host'] = options.deviceIP
             device_args['port'] = options.devicePort
         elif options.deviceSerial:
             device_args['deviceSerial'] = options.deviceSerial
+
+        if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
+            device_args['logLevel'] = logging.DEBUG
         options.dm = DroidADB(**device_args)
 
         if not options.remoteTestRoot:
             options.remoteTestRoot = options.dm.deviceRoot
 
         if options.remoteWebServer is None:
             if os.name != "nt":
                 options.remoteWebServer = moznetwork.get_ip()
--- a/testing/mochitest/moz.build
+++ b/testing/mochitest/moz.build
@@ -56,16 +56,17 @@ TEST_HARNESS_FILES.testing.mochitest += 
     'leaks.py',
     'mach_test_package_commands.py',
     'manifest.webapp',
     'manifestLibrary.js',
     'mochitest_options.py',
     'nested_setup.js',
     'pywebsocket_wrapper.py',
     'redirect.html',
+    'rungeckoview.py',
     'runrobocop.py',
     'runtests.py',
     'runtestsremote.py',
     'server.js',
     'start_desktop.js',
 ]
 
 TEST_HARNESS_FILES.testing.mochitest.embed += [
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/rungeckoview.py
@@ -0,0 +1,255 @@
+# 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/.
+
+
+import posixpath
+import shutil
+import sys
+import tempfile
+import time
+import traceback
+from optparse import OptionParser
+
+import mozcrash
+import mozdevice
+import mozlog
+from mozprofile import Profile
+
+
+class GeckoviewOptions(OptionParser):
+    def __init__(self):
+        OptionParser.__init__(self)
+        self.add_option("--utility-path",
+                        action="store", type="string", dest="utility_path",
+                        default=None,
+                        help="absolute path to directory containing utility programs")
+        self.add_option("--symbols-path",
+                        action="store", type="string", dest="symbols_path",
+                        default=None,
+                        help="absolute path to directory containing breakpad symbols, \
+                              or the URL of a zip file containing symbols")
+        self.add_option("--appname",
+                        action="store", type="string", dest="app",
+                        default="org.mozilla.geckoview_example",
+                        help="geckoview_example package name")
+        self.add_option("--deviceIP",
+                        action="store", type="string", dest="deviceIP",
+                        default=None,
+                        help="ip address of remote device to test")
+        self.add_option("--deviceSerial",
+                        action="store", type="string", dest="deviceSerial",
+                        default=None,
+                        help="serial ID of remote device to test")
+        self.add_option("--adbpath",
+                        action="store", type="string", dest="adbPath",
+                        default="adb",
+                        help="Path to adb binary.")
+        self.add_option("--remoteTestRoot",
+                        action="store", type="string", dest="remoteTestRoot",
+                        default=None,
+                        help="remote directory to use as test root \
+                              (eg. /mnt/sdcard/tests or /data/local/tests)")
+
+
+class GeckoviewTestRunner:
+    """
+       A quick-and-dirty test harness to verify the geckoview_example
+       app starts without crashing.
+    """
+
+    def __init__(self, log, dm, options):
+        self.log = log
+        self.dm = dm
+        self.options = options
+        self.appname = self.options.app.split('/')[-1]
+        self.logcat = None
+        self.build_profile()
+        self.log.debug("options=%s" % vars(options))
+
+    def build_profile(self):
+        test_root = self.dm.deviceRoot
+        self.remote_profile = posixpath.join(test_root, 'gv-profile')
+        self.dm.mkDirs(posixpath.join(self.remote_profile, "x"))
+        profile = Profile()
+        self.dm.pushDir(profile.profile, self.remote_profile)
+        self.log.debug("profile %s -> %s" %
+                       (str(profile.profile), str(self.remote_profile)))
+
+    def installed(self):
+        """
+        geckoview_example installed
+        """
+        installed = self.dm.shellCheckOutput(['pm', 'list', 'packages', self.appname])
+        if self.appname not in installed:
+            return (False, "%s not installed" % self.appname)
+        return (True, "%s installed" % self.appname)
+
+    def start(self):
+        """
+        geckoview_example starts
+        """
+        try:
+            self.dm.stopApplication(self.appname)
+            self.dm.recordLogcat()
+            cmd = ['am', 'start', '-a', 'android.intent.action.MAIN', '-n',
+                   'org.mozilla.geckoview_example/org.mozilla.geckoview_example.GeckoViewActivity',
+                   '--es', 'args', '-profile %s' % self.remote_profile]
+            env = {}
+            env["MOZ_CRASHREPORTER"] = 1
+            env["MOZ_CRASHREPORTER_NO_REPORT"] = 1
+            env["XPCOM_DEBUG_BREAK"] = "stack"
+            env["DISABLE_UNSAFE_CPOW_WARNINGS"] = 1
+            env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = 1
+            env["MOZ_IN_AUTOMATION"] = 1
+            env["R_LOG_VERBOSE"] = 1
+            env["R_LOG_LEVEL"] = 6
+            env["R_LOG_DESTINATION"] = "stderr"
+            i = 0
+            for key, value in env.iteritems():
+                cmd.append("--es")
+                cmd.append("env%d" % i)
+                cmd.append("%s=%s" % (key, str(value)))
+                i = i + 1
+            self.dm.shellCheckOutput(cmd)
+        except mozdevice.DMError:
+            return (False, "Exception during %s startup" % self.appname)
+        return (True, "%s started" % self.appname)
+
+    def started(self):
+        """
+        startup logcat messages
+        """
+        expected = [
+            "zerdatime",
+            "Displayed %s/.GeckoViewActivity" % self.appname
+        ]
+        # wait up to 60 seconds for startup
+        for wait_time in xrange(60):
+            time.sleep(1)
+            self.logcat = self.dm.getLogcat()
+            for line in self.logcat:
+                for e in expected:
+                    if e in line:
+                        self.log.debug(line.strip())
+                        expected.remove(e)
+            if len(expected) == 0:
+                return (True, "All expected logcat messages found")
+        for e in expected:
+            self.log.error("missing from logcat: '%s'" % e)
+        return (False, "'%s' not found in logcat" % expected[0])
+
+    def run_tests(self):
+        """
+           Run simple tests to verify that the geckoview_example app starts.
+        """
+        all_tests = [self.installed, self.start, self.started]
+        self.log.suite_start(all_tests)
+        pass_count = 0
+        fail_count = 0
+        for test in all_tests:
+            self.test_name = test.__doc__.strip()
+            self.log.test_start(self.test_name)
+
+            expected = 'PASS'
+            (passed, message) = test()
+            if passed:
+                pass_count = pass_count + 1
+            else:
+                fail_count = fail_count + 1
+            status = 'PASS' if passed else 'FAIL'
+
+            self.log.test_end(self.test_name, status, expected, message)
+
+        self.log.info("Passed: %d" % pass_count)
+        self.log.info("Failed: %d" % fail_count)
+        self.log.suite_end()
+
+        return 0 if passed else 1
+
+    def check_for_crashes(self):
+        if self.logcat:
+            if mozcrash.check_for_java_exception(self.logcat, self.test_name):
+                return True
+        symbols_path = self.options.symbols_path
+        try:
+            dump_dir = tempfile.mkdtemp()
+            remote_dir = posixpath.join(self.remote_profile, 'minidumps')
+            if not self.dm.dirExists(remote_dir):
+                # If crash reporting is enabled (MOZ_CRASHREPORTER=1), the
+                # minidumps directory is automatically created when the app
+                # (first) starts, so its lack of presence is a hint that
+                # something went wrong.
+                print "Automation Error: No crash directory (%s) found on remote device" % \
+                    remote_dir
+                # Whilst no crash was found, the run should still display as a failure
+                return True
+            self.dm.getDirectory(remote_dir, dump_dir)
+            crashed = mozcrash.log_crashes(self.log, dump_dir, symbols_path, test=self.test_name)
+        finally:
+            try:
+                shutil.rmtree(dump_dir)
+            except:
+                self.log.warn("unable to remove directory: %s" % dump_dir)
+        return crashed
+
+    def cleanup(self):
+        """
+           Cleanup at end of job run.
+        """
+        self.log.debug("Cleaning up...")
+        self.dm.stopApplication(self.appname)
+        crashed = self.check_for_crashes()
+        self.dm.removeDir(self.remote_profile)
+        self.log.debug("Cleanup complete.")
+        return crashed
+
+
+def run_test_harness(log, parser, options):
+    device_args = {'deviceRoot': options.remoteTestRoot}
+    device_args['adbPath'] = options.adbPath
+    if options.deviceIP:
+        device_args['host'] = options.deviceIP
+        device_args['port'] = options.devicePort
+    elif options.deviceSerial:
+        device_args['deviceSerial'] = options.deviceSerial
+    device_args['packageName'] = options.app
+    dm = mozdevice.DroidADB(**device_args)
+
+    runner = GeckoviewTestRunner(log, dm, options)
+    result = -1
+    try:
+        result = runner.run_tests()
+    except KeyboardInterrupt:
+        log.info("rungeckoview.py | Received keyboard interrupt")
+        result = -1
+    except:
+        traceback.print_exc()
+        log.error(
+            "rungeckoview.py | Received unexpected exception while running tests")
+        result = 1
+    finally:
+        try:
+            crashed = runner.cleanup()
+            if not result:
+                result = crashed
+        except mozdevice.DMError:
+            # ignore device error while cleaning up
+            pass
+    return result
+
+
+def main(args=sys.argv[1:]):
+    parser = GeckoviewOptions()
+    mozlog.commandline.add_logging_group(parser)
+    options, args = parser.parse_args()
+    if args:
+        print >>sys.stderr, """Usage: %s""" % sys.argv[0]
+        sys.exit(1)
+    log = mozlog.commandline.setup_logging("rungeckoview", options,
+                                           {"tbpl": sys.stdout})
+    return run_test_harness(log, parser, options)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
--- a/testing/mozharness/configs/android/androidarm_4_3.py
+++ b/testing/mozharness/configs/android/androidarm_4_3.py
@@ -351,16 +351,24 @@ config = {
                 "--disable-e10s",
                 "--gecko-log=%(gecko_log)s",
                 "--log-raw=%(raw_log_file)s",
                 "--log-errorsummary=%(error_summary_file)s",
                 "--symbols-path=%(symbols_path)s",
                 "--startup-timeout=300",
             ],
         },
+        "geckoview": {
+            "run_filename": "rungeckoview.py",
+            "testsdir": "mochitest",
+            "options": [
+                "--utility-path=%(utility_path)s",
+                "--symbols-path=%(symbols_path)s",
+            ],
+        },
 
     },  # end suite_definitions
     "download_minidump_stackwalk": True,
     "default_blob_upload_servers": [
         "https://blobupload.elasticbeanstalk.com",
     ],
     "blob_uploader_auth_file": os.path.join(os.getcwd(), "oauth.txt"),
 }
--- a/testing/mozharness/mozharness/mozilla/testing/errors.py
+++ b/testing/mozharness/mozharness/mozilla/testing/errors.py
@@ -89,16 +89,22 @@ TinderBoxPrintRe = {
         'known_fail_group': None,
     },
     "mozmill_summary": {
         'regex': re.compile(r'''INFO (Passed|Failed|Skipped): (\d+)'''),
         'pass_group': "Passed",
         'fail_group': "Failed",
         'known_fail_group': "Skipped",
     },
+    "geckoview_summary": {
+        'regex': re.compile(r'''(Passed|Failed): (\d+)'''),
+        'pass_group': "Passed",
+        'fail_group': "Failed",
+        'known_fail_group': None,
+    },
 
     "harness_error": {
         'full_regex': re.compile(r"(?:TEST-UNEXPECTED-FAIL|PROCESS-CRASH) \| .* \| (application crashed|missing output line for total leaks!|negative leaks caught!|\d+ bytes leaked)"),
         'minimum_regex': re.compile(r'''(TEST-UNEXPECTED|PROCESS-CRASH)'''),
         'retry_regex': re.compile(r'''(FAIL-SHOULD-RETRY|No space left on device|DMError|Connection to the other side was lost in a non-clean fashion|program finished with exit code 80|INFRA-ERROR|twisted.spread.pb.PBConnectionLost|_dl_open: Assertion|Timeout exceeded for _runCmd call)''')
     },
 }
 
--- a/testing/mozharness/mozharness/mozilla/testing/testbase.py
+++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py
@@ -434,16 +434,17 @@ 2. running via buildbot and running the 
         # to how these tests are run, so we pave over these differences here.
         aliases = {
             'robocop': 'mochitest',
             'mochitest-chrome': 'mochitest',
             'mochitest-media': 'mochitest',
             'mochitest-plain-clipboard': 'mochitest',
             'mochitest-plain-gpu': 'mochitest',
             'mochitest-gl': 'mochitest',
+            'geckoview': 'mochitest',
             'jsreftest': 'reftest',
             'crashtest': 'reftest',
             'reftest-debug': 'reftest',
             'jsreftest-debug': 'reftest',
             'crashtest-debug': 'reftest',
         }
         suite_categories = [aliases.get(name, name) for name in suite_categories]
 
--- a/testing/remotecppunittests.py
+++ b/testing/remotecppunittests.py
@@ -212,20 +212,24 @@ def run_test_harness(options, args):
         runner = B2GEmulatorRunner(arch=options.emulator, b2g_home=options.with_b2g_emulator)
         runner.start()
         # because we just started the emulator, we need more than the
         # default number of retries here.
         retryLimit = 50
     else:
         retryLimit = 5
     try:
+        dm_args = {'deviceRoot': options.remote_test_root}
+        dm_args['retryLimit'] = retryLimit
         if options.device_ip:
-            dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.device_port, packageName=None, deviceRoot=options.remote_test_root, retryLimit=retryLimit)
-        else:
-            dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remote_test_root, retryLimit=retryLimit)
+            dm_args['host'] = options.device_ip
+            dm_args['port'] = options.device_port
+        if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
+            dm_args['logLevel'] = logging.DEBUG
+        dm = devicemanagerADB.DeviceManagerADB(**dm_args)
     except:
         if options.with_b2g_emulator:
             runner.cleanup()
             runner.wait()
         raise
 
     options.xre_path = os.path.abspath(options.xre_path)
     cppunittests.update_mozinfo()
--- a/testing/web-platform/meta/payment-request/allowpaymentrequest/allowpaymentrequest-attribute-same-origin-bc-containers.https.html.ini
+++ b/testing/web-platform/meta/payment-request/allowpaymentrequest/allowpaymentrequest-attribute-same-origin-bc-containers.https.html.ini
@@ -1,14 +1,4 @@
 [allowpaymentrequest-attribute-same-origin-bc-containers.https.html]
   type: testharness
-  [iframe]
-    expected: FAIL
-
-  [frame]
-    expected: FAIL
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1336760
 
-  [object]
-    expected: FAIL
-
-  [embed]
-    expected: FAIL
-
--- a/testing/web-platform/meta/payment-request/allowpaymentrequest/no-attribute-same-origin-bc-containers.https.html.ini
+++ b/testing/web-platform/meta/payment-request/allowpaymentrequest/no-attribute-same-origin-bc-containers.https.html.ini
@@ -1,14 +1,4 @@
 [no-attribute-same-origin-bc-containers.https.html]
   type: testharness
-  [iframe]
-    expected: FAIL
-
-  [frame]
-    expected: FAIL
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1328351
 
-  [object]
-    expected: FAIL
-
-  [embed]
-    expected: FAIL
-
--- a/testing/web-platform/meta/websockets/constructor/014.html.ini
+++ b/testing/web-platform/meta/websockets/constructor/014.html.ini
@@ -1,10 +1,11 @@
 [014.html]
   type: testharness
   disabled:
     if (os == "win") and (version == "5.1.2600"): https://bugzilla.mozilla.org/show_bug.cgi?id=1090198
+    if e10s and debug: https://bugzilla.mozilla.org/show_bug.cgi?id=1090198
 
 [014.html?wss]
   type: testharness
   [WebSockets: serialize establish a connection]
     expected: FAIL
 
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -1,14 +1,15 @@
 #!/usr/bin/env 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/.
 
+import logging
 import posixpath
 import sys, os
 import subprocess
 import runxpcshelltests as xpcshell
 import tempfile
 import time
 from zipfile import ZipFile
 from mozlog import commandline
@@ -573,20 +574,23 @@ def main():
             print >>sys.stderr, "Error: please specify an APK"
             sys.exit(1)
 
     options = verifyRemoteOptions(parser, options)
     log = commandline.setup_logging("Remote XPCShell",
                                     options,
                                     {"tbpl": sys.stdout})
 
+    dm_args = {'deviceRoot': options.remoteTestRoot}
     if options.deviceIP:
-        dm = mozdevice.DroidADB(options.deviceIP, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot)
-    else:
-        dm = mozdevice.DroidADB(packageName=None, deviceRoot=options.remoteTestRoot)
+        dm_args['host'] = options.deviceIP
+        dm_args['port'] = options.devicePort
+    if options.log_tbpl_level == 'debug' or options.log_mach_level == 'debug':
+        dm_args['logLevel'] = logging.DEBUG
+    dm = mozdevice.DroidADB(**dm_args)
 
     if options.interactive and not options.testPath:
         print >>sys.stderr, "Error: You must specify a test filename in interactive mode!"
         sys.exit(1)
 
     if options.xpcshell is None:
         options.xpcshell = "xpcshell"
 
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -916,16 +916,18 @@ public:
   struct StmtStats {
     struct Stat mainThread;
     struct Stat otherThreads;
   };
   typedef nsBaseHashtableET<nsCStringHashKey, StmtStats> SlowSQLEntryType;
 
   static void RecordIceCandidates(const uint32_t iceCandidateBitmask,
                                   const bool success);
+  static bool CanRecordBase();
+  static bool CanRecordExtended();
 private:
   TelemetryImpl();
   ~TelemetryImpl();
 
   static nsCString SanitizeSQL(const nsACString& sql);
 
   enum SanitizedState { Sanitized, Unsanitized };
 
@@ -947,16 +949,18 @@ private:
   void ReadLateWritesStacks(nsIFile* aProfileDir);
 
   static TelemetryImpl *sTelemetry;
   AutoHashtable<SlowSQLEntryType> mPrivateSQL;
   AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
   Mutex mHashMutex;
   HangReports mHangReports;
   Mutex mHangReportsMutex;
+  Atomic<bool> mCanRecordBase;
+  Atomic<bool> mCanRecordExtended;
 
 #if defined(ENABLE_STACK_CAPTURE)
   // Stores data about stacks captured on demand.
   KeyedStackCapturer mStackCapturer;
 #endif
 
   // mThreadHangStats stores recorded, inactive thread hang stats
   Vector<Telemetry::ThreadHangStats> mThreadHangStats;
@@ -1230,16 +1234,18 @@ TelemetryImpl::AsyncFetchTelemetryData(n
 
   targetThread->Dispatch(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 TelemetryImpl::TelemetryImpl()
   : mHashMutex("Telemetry::mHashMutex")
   , mHangReportsMutex("Telemetry::mHangReportsMutex")
+  , mCanRecordBase(false)
+  , mCanRecordExtended(false)
   , mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex")
   , mCachedTelemetryData(false)
   , mLastShutdownTime(0)
   , mFailedLockCount(0)
 {
   // We expect TelemetryHistogram::InitializeGlobalState() to have been
   // called before we get to this point.
   MOZ_ASSERT(TelemetryHistogram::GlobalStateHasBeenInitialized());
@@ -2262,46 +2268,52 @@ TelemetryImpl::GetKeyedHistogramById(con
  * FHR data reporting service or self-support are enabled.
  *
  * In the unlikely event that adding a new base probe is needed, please check the data
  * collection wiki at https://wiki.mozilla.org/Firefox/Data_Collection and talk to the
  * Telemetry team.
  */
 NS_IMETHODIMP
 TelemetryImpl::GetCanRecordBase(bool *ret) {
-  *ret = TelemetryHistogram::CanRecordBase();
+  *ret = mCanRecordBase;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelemetryImpl::SetCanRecordBase(bool canRecord) {
-  TelemetryHistogram::SetCanRecordBase(canRecord);
-  TelemetryScalar::SetCanRecordBase(canRecord);
-  TelemetryEvent::SetCanRecordBase(canRecord);
+  if (canRecord != mCanRecordBase) {
+    TelemetryHistogram::SetCanRecordBase(canRecord);
+    TelemetryScalar::SetCanRecordBase(canRecord);
+    TelemetryEvent::SetCanRecordBase(canRecord);
+    mCanRecordBase = canRecord;
+  }
   return NS_OK;
 }
 
 /**
  * Indicates if Telemetry is allowed to record extended data. Returns false if the user
  * hasn't opted into "extended Telemetry" on the Release channel, when the user has
  * explicitly opted out of Telemetry on Nightly/Aurora/Beta or if manually set to false
  * during tests.
  * If the returned value is false, gathering of extended telemetry statistics is disabled.
  */
 NS_IMETHODIMP
 TelemetryImpl::GetCanRecordExtended(bool *ret) {
-  *ret = TelemetryHistogram::CanRecordExtended();
+  *ret = mCanRecordExtended;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelemetryImpl::SetCanRecordExtended(bool canRecord) {
-  TelemetryHistogram::SetCanRecordExtended(canRecord);
-  TelemetryScalar::SetCanRecordExtended(canRecord);
-  TelemetryEvent::SetCanRecordExtended(canRecord);
+  if (canRecord != mCanRecordExtended) {
+    TelemetryHistogram::SetCanRecordExtended(canRecord);
+    TelemetryScalar::SetCanRecordExtended(canRecord);
+    TelemetryEvent::SetCanRecordExtended(canRecord);
+    mCanRecordExtended = canRecord;
+  }
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 TelemetryImpl::GetIsOfficialTelemetry(bool *ret) {
 #if defined(MOZILLA_OFFICIAL) && defined(MOZ_TELEMETRY_REPORTING) && !defined(DEBUG)
   *ret = true;
@@ -2334,16 +2346,19 @@ TelemetryImpl::CreateTelemetryInstance()
   // Now, create and initialize the Telemetry global state.
   sTelemetry = new TelemetryImpl();
 
   // AddRef for the local reference
   NS_ADDREF(sTelemetry);
   // AddRef for the caller
   nsCOMPtr<nsITelemetry> ret = sTelemetry;
 
+  sTelemetry->mCanRecordBase = useTelemetry;
+  sTelemetry->mCanRecordExtended = useTelemetry;
+
   sTelemetry->InitMemoryReporter();
   InitHistogramRecordingEnabled(); // requires sTelemetry to exist
 
   return ret.forget();
 }
 
 void
 TelemetryImpl::ShutdownTelemetry()
@@ -2684,16 +2699,38 @@ TelemetryImpl::RecordThreadHangStats(Tel
     return;
 
   MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
 
   // Ignore OOM.
   mozilla::Unused << sTelemetry->mThreadHangStats.append(Move(aStats));
 }
 
+bool
+TelemetryImpl::CanRecordBase()
+{
+  if (!sTelemetry) {
+    return false;
+  }
+  bool canRecordBase;
+  nsresult rv = sTelemetry->GetCanRecordBase(&canRecordBase);
+  return NS_SUCCEEDED(rv) && canRecordBase;
+}
+
+bool
+TelemetryImpl::CanRecordExtended()
+{
+  if (!sTelemetry) {
+    return false;
+  }
+  bool canRecordExtended;
+  nsresult rv = sTelemetry->GetCanRecordExtended(&canRecordExtended);
+  return NS_SUCCEEDED(rv) && canRecordExtended;
+}
+
 NS_IMPL_ISUPPORTS(TelemetryImpl, nsITelemetry, nsIMemoryReporter)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelemetry, TelemetryImpl::CreateTelemetryInstance)
 
 #define NS_TELEMETRY_CID \
   {0xaea477f2, 0xb3a2, 0x469c, {0xaa, 0x29, 0x0a, 0x82, 0xd1, 0x32, 0xb8, 0x29}}
 NS_DEFINE_NAMED_CID(NS_TELEMETRY_CID);
 
 const Module::CIDEntry kTelemetryCIDs[] = {
@@ -3350,23 +3387,23 @@ const char*
 GetHistogramName(HistogramID id)
 {
   return TelemetryHistogram::GetHistogramName(id);
 }
 
 bool
 CanRecordBase()
 {
-  return TelemetryHistogram::CanRecordBase();
+  return TelemetryImpl::CanRecordBase();
 }
 
 bool
 CanRecordExtended()
 {
-  return TelemetryHistogram::CanRecordExtended();
+  return TelemetryImpl::CanRecordExtended();
 }
 
 void
 RecordSlowSQLStatement(const nsACString &statement,
                        const nsACString &dbName,
                        uint32_t delay)
 {
   TelemetryImpl::RecordSlowStatement(statement, dbName, delay);
--- a/toolkit/components/telemetry/parse_events.py
+++ b/toolkit/components/telemetry/parse_events.py
@@ -15,17 +15,17 @@ MAX_OBJECT_NAME_LENGTH = 20
 MAX_EXTRA_KEYS_COUNT = 10
 MAX_EXTRA_KEY_NAME_LENGTH = 15
 
 IDENTIFIER_PATTERN = r'^[a-zA-Z][a-zA-Z0-9_.]*[a-zA-Z0-9]$'
 DATE_PATTERN = r'^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
 
 
 def nice_type_name(t):
-    if isinstance(t, basestring):
+    if issubclass(t, basestring):
         return "string"
     return t.__name__
 
 
 def convert_to_cpp_identifier(s, sep):
     return string.capwords(s, sep).replace(sep, "")
 
 
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -765,16 +765,22 @@ XRE_InitParentProcess(int aArgc,
                       char* aArgv[],
                       MainFunction aMainFunction,
                       void* aMainFunctionData)
 {
   NS_ENSURE_ARG_MIN(aArgc, 1);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
 
+  // Set main thread before we initialize the profiler
+  NS_SetMainThread();
+
+  char aLocal;
+  GeckoProfilerInitRAII profiler(&aLocal);
+
   ScopedXREEmbed embed;
 
   gArgc = aArgc;
   gArgv = aArgv;
   nsresult rv = XRE_InitCommandLine(gArgc, gArgv);
   if (NS_FAILED(rv))
       return NS_ERROR_FAILURE;
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -1976,17 +1976,16 @@ locked_profiler_start(PS::LockRef aLock,
                       const char** aFeatures, uint32_t aFeatureCount,
                       const char** aThreadNameFilters, uint32_t aFilterCount);
 
 void
 profiler_init(void* aStackTop)
 {
   LOG("profiler_init");
 
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!gPS);
 
   const char* features[] = { "js"
 #if defined(PROFILE_JAVA)
                            , "java"
 #endif
                            , "leaf"
 #if defined(HAVE_NATIVE_UNWIND)
--- a/tools/profiler/core/shared-libraries-linux.cc
+++ b/tools/profiler/core/shared-libraries-linux.cc
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "shared-libraries.h"
 
 #define PATH_MAX_TOSTRING(x) #x
 #define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
@@ -10,22 +12,66 @@
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
 #include <unistd.h>
 #include <fstream>
 #include "platform.h"
 #include "shared-libraries.h"
 #include "mozilla/Unused.h"
+#include "nsDebug.h"
 #include "nsNativeCharsetUtils.h"
 
 #include "common/linux/file_id.h"
 #include <algorithm>
 
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+// There are three different configuration cases:
+//
+// (1) GP_OS_linux
+//       Use dl_iterate_phdr for everything.
+//
+// (2) GP_OS_android non-GONK
+//       If dl_iterate_phdr doesn't exist, give up immediately.  Otherwise use
+//       dl_iterate_phdr for almost all info and /proc/self/maps to get the
+//       mapping for /dev/ashmem/dalvik-jit-code-cache.
+//
+// (3) GP_OS_android GONK
+//       Use /proc/self/maps for everything.
+
+#undef CONFIG_CASE_1
+#undef CONFIG_CASE_2
+#undef CONFIG_CASE_3
+
+#if defined(GP_OS_linux)
+# define CONFIG_CASE_1 1
+# include <link.h> // dl_phdr_info
+# include <features.h>
+# include <dlfcn.h>
+# include <sys/types.h>
+
+#elif defined(GP_OS_android) && !defined(MOZ_WIDGET_GONK)
+# define CONFIG_CASE_2 1
+# include "ElfLoader.h" // dl_phdr_info
+# include <features.h>
+# include <dlfcn.h>
+# include <sys/types.h>
+extern "C" MOZ_EXPORT __attribute__((weak))
+int dl_iterate_phdr(
+          int (*callback)(struct dl_phdr_info *info, size_t size, void *data),
+          void *data);
+
+#elif defined(GP_OS_android) && defined(MOZ_WIDGET_GONK)
+# define CONFIG_CASE_3 1
+  // No config-specific includes.
+
+#else
+# error "Unexpected configuration"
+#endif
+
 
 // Get the breakpad Id for the binary file pointed by bin_name
 static std::string getId(const char *bin_name)
 {
   using namespace google_breakpad;
   using namespace std;
 
   PageAllocator allocator;
@@ -34,36 +80,19 @@ static std::string getId(const char *bin
   FileID file_id(bin_name);
   if (file_id.ElfFileIdentifier(identifier)) {
     return FileID::ConvertIdentifierToUUIDString(identifier) + "0";
   }
 
   return "";
 }
 
-#if !defined(MOZ_WIDGET_GONK)
-// TODO fix me with proper include
-#include "nsDebug.h"
-#if defined(GP_OS_android)
-#include "ElfLoader.h" // dl_phdr_info
-#else
-#include <link.h> // dl_phdr_info
-#endif
-#include <features.h>
-#include <dlfcn.h>
-#include <sys/types.h>
 
-#if defined(GP_OS_android)
-extern "C" MOZ_EXPORT __attribute__((weak))
-int dl_iterate_phdr(
-          int (*callback) (struct dl_phdr_info *info,
-                           size_t size, void *data),
-          void *data);
-#endif
-
+// Config cases (1) and (2) use dl_iterate_phdr.
+#if defined(CONFIG_CASE_1) || defined(CONFIG_CASE_2)
 static int
 dl_iterate_callback(struct dl_phdr_info *dl_info, size_t size, void *data)
 {
   SharedLibraryInfo& info = *reinterpret_cast<SharedLibraryInfo*>(data);
 
   if (dl_info->dlpi_phnum <= 0)
     return 0;
 
@@ -79,108 +108,121 @@ dl_iterate_callback(struct dl_phdr_info 
     if (start < libStart)
       libStart = start;
     if (end > libEnd)
       libEnd = end;
   }
   const char *path = dl_info->dlpi_name;
 
   nsAutoString pathStr;
-  mozilla::Unused << NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(path), pathStr)));
+  mozilla::Unused <<
+    NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(path),
+                                                pathStr)));
 
   nsAutoString nameStr = pathStr;
   int32_t pos = nameStr.RFindChar('/');
   if (pos != kNotFound) {
     nameStr.Cut(0, pos + 1);
   }
 
   SharedLibrary shlib(libStart, libEnd, 0, getId(path),
                       nameStr, pathStr, nameStr, pathStr,
                       "", "");
   info.AddSharedLibrary(shlib);
 
   return 0;
 }
+#endif // config cases (1) and (2)
 
-#endif // !MOZ_WIDGET_GONK
 
 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
 {
   SharedLibraryInfo info;
 
-#if !defined(MOZ_WIDGET_GONK)
-#if defined(GP_OS_android)
+#if defined(CONFIG_CASE_2)
+  // If dl_iterate_phdr doesn't exist, we give up immediately.
   if (!dl_iterate_phdr) {
     // On ARM Android, dl_iterate_phdr is provided by the custom linker.
     // So if libxul was loaded by the system linker (e.g. as part of
     // xpcshell when running tests), it won't be available and we should
     // not call it.
     return info;
   }
-#endif // defined(GP_OS_android)
+#endif
 
-  dl_iterate_phdr(dl_iterate_callback, &info);
-#endif // !defined(MOZ_WIDGET_GONK)
-
-#if defined(GP_OS_android) || defined(MOZ_WIDGET_GONK)
+#if defined(CONFIG_CASE_2) || defined(CONFIG_CASE_3)
+  // Read info from /proc/self/maps.  We do this for config cases (2) and (3),
+  // but only in case (3) are we building the module list from that information.
+  // For case (2) we're collecting information only for one specific mapping.
   pid_t pid = getpid();
   char path[PATH_MAX];
   snprintf(path, PATH_MAX, "/proc/%d/maps", pid);
   std::ifstream maps(path);
   std::string line;
   int count = 0;
   while (std::getline(maps, line)) {
     int ret;
-    //XXX: needs input sanitizing
     unsigned long start;
     unsigned long end;
     char perm[6] = "";
     unsigned long offset;
     char modulePath[PATH_MAX] = "";
     ret = sscanf(line.c_str(),
                  "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
                  &start, &end, perm, &offset, modulePath);
     if (!strchr(perm, 'x')) {
       // Ignore non executable entries
       continue;
     }
     if (ret != 5 && ret != 4) {
-      LOG("Get maps line failed");
+      LOG("SharedLibraryInfo::GetInfoForSelf(): "
+          "reading /proc/self/maps failed");
       continue;
     }
-#if defined(PROFILE_JAVA)
-    // Use proc/pid/maps to get the dalvik-jit section since it has
-    // no associated phdrs
-    if (strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache") != 0) {
+
+#if defined(CONFIG_CASE_2)
+    // Use /proc/pid/maps to get the dalvik-jit section since it has no
+    // associated phdrs.
+    if (0 != strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache")) {
       continue;
     }
-#else
+    // Otherwise proceed to the tail of the loop, so as to record the entry.
+#elif defined(CONFIG_CASE_3)
     if (strcmp(perm, "r-xp") != 0) {
       // Ignore entries that are writable and/or shared.
       // At least one graphics driver uses short-lived "rwxs" mappings
       // (see bug 926734 comment 5), so just checking for 'x' isn't enough.
       continue;
     }
+    // Record all other entries.
 #endif
 
     nsAutoString pathStr;
-    mozilla::Unused << NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(modulePath), pathStr)));
+    mozilla::Unused <<
+      NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(
+                             nsDependentCString(modulePath), pathStr)));
 
     nsAutoString nameStr = pathStr;
     int32_t pos = nameStr.RFindChar('/');
     if (pos != kNotFound) {
       nameStr.Cut(0, pos + 1);
     }
 
     SharedLibrary shlib(start, end, offset, getId(path),
-                        nameStr, pathStr, nameStr, pathStr,
-                        "", "");
+                        nameStr, pathStr, nameStr, pathStr, "", "");
     info.AddSharedLibrary(shlib);
     if (count > 10000) {
-      LOG("Get maps failed");
+      LOG("SharedLibraryInfo::GetInfoForSelf(): "
+          "implausibly large number of mappings acquired");
       break;
     }
     count++;
   }
-#endif // defined(GP_OS_android) || defined(MOZ_WIDGET_GONK)
+#endif // config cases 2 and 3
+
+#if defined(CONFIG_CASE_1) || defined(CONFIG_CASE_2)
+  // For config cases (1) and (2), we collect the bulk of the library info using
+  // dl_iterate_phdr.
+  dl_iterate_phdr(dl_iterate_callback, &info);
+#endif
 
   return info;
 }