diff --git a/browser/base/content/test/static/browser_parsable_css.js b/browser/base/content/test/static/browser_parsable_css.js index 21178109e9c0..0b14a16ef028 100644 --- a/browser/base/content/test/static/browser_parsable_css.js +++ b/browser/base/content/test/static/browser_parsable_css.js @@ -240,6 +240,10 @@ function chromeFileExists(aURI) { } 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 diff --git a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js index e4b6c01b026f..79a64a545d61 100644 --- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js +++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_aboutPages.js @@ -9,7 +9,7 @@ add_task(function* setup() { /** * 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 }); @@ -17,15 +17,17 @@ add_task(function* test_remote_window_open_aboutBlank() { 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(); @@ -75,15 +77,17 @@ add_task(function* test_remote_window_open_data_uri() { 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(); @@ -103,8 +107,7 @@ add_task(function* test_remote_window_open_data_uri2() { 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"); @@ -112,12 +115,15 @@ add_task(function* test_remote_window_open_data_uri2() { 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"); }); diff --git a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_js_uri.js b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_js_uri.js index 5fd1d20991b0..5be3ce422ad6 100644 --- a/browser/components/originattributes/test/browser/browser_firstPartyIsolation_js_uri.js +++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation_js_uri.js @@ -13,15 +13,17 @@ add_task(function* test_remote_window_open_js_uri() { 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(); @@ -46,18 +48,22 @@ add_task(function* test_remote_window_open_js_uri2() { 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(); diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 index e44df1b7dd14..23106fa03245 100644 --- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -82,7 +82,7 @@ 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_ for each comma-separated diff --git a/caps/NullPrincipal.cpp b/caps/NullPrincipal.cpp index 084262feb61b..7aae26e2e9cd 100644 --- a/caps/NullPrincipal.cpp +++ b/caps/NullPrincipal.cpp @@ -49,10 +49,9 @@ NullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom) NullPrincipal::CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty) { OriginAttributes attrs = nsDocShell::Cast(aDocShell)->GetOriginAttributes(); - attrs.SetFirstPartyDomain(aIsFirstParty, NS_LITERAL_CSTRING(NULL_PRINCIPAL_FIRST_PARTY_DOMAIN)); RefPtr nullPrin = new NullPrincipal(); - nsresult rv = nullPrin->Init(attrs); + nsresult rv = nullPrin->Init(attrs, aIsFirstParty); MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); return nullPrin.forget(); } @@ -93,6 +92,33 @@ NullPrincipal::Init(const OriginAttributes& aOriginAttributes, nsIURI* aURI) 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 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) { diff --git a/caps/NullPrincipal.h b/caps/NullPrincipal.h index db6f3a77ac18..ec076adca99e 100644 --- a/caps/NullPrincipal.h +++ b/caps/NullPrincipal.h @@ -57,7 +57,7 @@ public: // 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 CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty = false); @@ -81,6 +81,12 @@ public: bool MayLoadInternal(nsIURI* aURI) override; nsCOMPtr 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__ diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 80741082aa02..d671578afaaf 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -3271,7 +3271,7 @@ nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() mIsNested = false; ::InvalidateAllFrames(mCommonAncestor); nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor(); - if (commonAncestor != mCommonAncestor) { + if (commonAncestor && commonAncestor != mCommonAncestor) { ::InvalidateAllFrames(commonAncestor); } } diff --git a/dom/html/crashtests/1350972.html b/dom/html/crashtests/1350972.html new file mode 100644 index 000000000000..7af7f9e174fa --- /dev/null +++ b/dom/html/crashtests/1350972.html @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/dom/html/crashtests/crashtests.list b/dom/html/crashtests/crashtests.list index 793a6c3cf572..45bb29836f58 100644 --- a/dom/html/crashtests/crashtests.list +++ b/dom/html/crashtests/crashtests.list @@ -81,3 +81,4 @@ load 1290904.html load 1343886-1.html load 1343886-2.xml load 1343886-3.xml +asserts(0-3) load 1350972.html diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index 793e8e92eddd..07b0ad63717f 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -12,6 +12,7 @@ #include "nsINetworkInterceptController.h" #include "nsIOutputStream.h" #include "nsIScriptError.h" +#include "nsITimedChannel.h" #include "nsIUnicodeDecoder.h" #include "nsIUnicodeEncoder.h" #include "nsContentPolicyUtils.h" @@ -108,6 +109,12 @@ 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; @@ -230,6 +237,9 @@ public: return NS_OK; } + mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); + mChannel->SaveTimeStampsToUnderlyingChannel(); + nsCOMPtr obsService = services::GetObserverService(); if (obsService) { obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr); diff --git a/dom/workers/ServiceWorkerPrivate.cpp b/dom/workers/ServiceWorkerPrivate.cpp index 7a6827ac7660..aff45dca961a 100644 --- a/dom/workers/ServiceWorkerPrivate.cpp +++ b/dom/workers/ServiceWorkerPrivate.cpp @@ -14,6 +14,7 @@ #include "nsINetworkInterceptController.h" #include "nsIPushErrorReporter.h" #include "nsISupportsImpl.h" +#include "nsITimedChannel.h" #include "nsIUploadChannel2.h" #include "nsNetUtil.h" #include "nsProxyRelease.h" @@ -1297,6 +1298,7 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mMethod; nsString mClientId; bool mIsReload; + bool mMarkLaunchServiceWorkerEnd; RequestCache mCacheMode; RequestMode mRequestMode; RequestRedirect mRequestRedirect; @@ -1315,13 +1317,15 @@ public: const nsACString& aScriptSpec, nsMainThreadPtrHandle& 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) @@ -1474,6 +1478,12 @@ public: WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { MOZ_ASSERT(aWorkerPrivate); + + if (mMarkLaunchServiceWorkerEnd) { + mInterceptedChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now()); + } + + mInterceptedChannel->SetDispatchFetchEventEnd(TimeStamp::Now()); return DispatchFetchEvent(aCx, aWorkerPrivate); } @@ -1502,6 +1512,10 @@ private: 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"); @@ -1577,6 +1591,8 @@ private: event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec); event->SetTrusted(true); + mInterceptedChannel->SetHandleFetchEventStart(TimeStamp::Now()); + nsresult rv2 = DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event, nullptr); @@ -1647,9 +1663,20 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsCOMPtr 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 handle( new nsMainThreadPtrHolder(aChannel, false)); @@ -1658,10 +1685,11 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, RefPtr token = CreateEventKeepAliveToken(); + RefPtr 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; @@ -1684,6 +1712,7 @@ ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel, nsresult ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, + bool* aNewWorkerCreated, nsILoadGroup* aLoadGroup) { AssertIsOnMainThread(); @@ -1694,6 +1723,12 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, // 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); @@ -1800,6 +1835,10 @@ ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy, RenewKeepAliveToken(aWhy); + if (aNewWorkerCreated) { + *aNewWorkerCreated = true; + } + return NS_OK; } diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h index 55c6d76bfd51..fcd32c6c8344 100644 --- a/dom/workers/ServiceWorkerPrivate.h +++ b/dom/workers/ServiceWorkerPrivate.h @@ -196,6 +196,7 @@ private: nsresult SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, + bool* aNewWorkerCreated = nullptr, nsILoadGroup* aLoadGroup = nullptr); ~ServiceWorkerPrivate(); diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini index e064e7fd0ae2..6d7dbebd084a 100644 --- a/dom/workers/test/serviceworkers/chrome.ini +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -3,6 +3,8 @@ skip-if = os == 'android' support-files = chrome_helpers.js empty.js + fetch.js + hello.html serviceworker.html serviceworkerinfo_iframe.html serviceworkermanager_iframe.html @@ -10,6 +12,7 @@ support-files = worker.js worker2.js +[test_devtools_serviceworker_interception.html] [test_privateBrowsing.html] [test_serviceworkerinfo.xul] [test_serviceworkermanager.xul] diff --git a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html new file mode 100644 index 000000000000..d49ebb2c9363 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html @@ -0,0 +1,168 @@ + + + + + Bug 1168875 - test devtools serviceworker interception. + + + + +

+ +

+
+
+
+
+
diff --git a/gfx/2d/DrawEventRecorder.cpp b/gfx/2d/DrawEventRecorder.cpp
index 0e20b8b5ac5d..430f8113a49c 100644
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -78,40 +78,5 @@ DrawEventRecorderFile::Close()
   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
diff --git a/gfx/2d/DrawEventRecorder.h b/gfx/2d/DrawEventRecorder.h
index 843a71cfe647..68bf772feb4c 100644
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -103,53 +103,6 @@ private:
   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
 
diff --git a/image/imgICache.idl b/image/imgICache.idl
index 2a12e2bb199a..e67691399065 100644
--- a/image/imgICache.idl
+++ b/image/imgICache.idl
@@ -30,6 +30,16 @@ interface imgICache : nsISupports
    */
   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'
diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp
index ccab35de7499..93a181c70999 100644
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -1369,6 +1369,29 @@ imgLoader::ClearCache(bool chrome)
 
 }
 
+NS_IMETHODIMP
+imgLoader::RemoveEntry(nsIURI* aURI,
+                       nsIDOMDocument* aDOMDoc)
+{
+  nsCOMPtr doc = do_QueryInterface(aDOMDoc);
+  if (aURI) {
+    OriginAttributes attrs;
+    if (doc) {
+      nsCOMPtr 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,
@@ -2169,6 +2192,18 @@ imgLoader::LoadImage(nsIURI* aURI,
         request->SetCacheEntry(entry);
 
         if (mCacheTracker) {
+          if (MOZ_UNLIKELY(!entry->GetExpirationState()->IsTracked())) {
+            bool inCache = false;
+            RefPtr 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);
         }
       }
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 7f2ff041bf5e..81aa2d158169 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -2414,16 +2414,16 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
     // 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 promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
     if (!promise)
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index c5600febe45e..178d332d4155 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -244,9 +244,9 @@ class LoopControl : public BreakableControl
         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;
@@ -269,6 +269,20 @@ class LoopControl : public BreakableControl
         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))
@@ -2080,11 +2094,11 @@ class ForOfLoopControl : public LoopControl
     }
 
     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
@@ -2100,11 +2114,9 @@ class ForOfLoopControl : public LoopControl
         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;
@@ -2780,7 +2792,7 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta
                 if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
                     return false;
             } else {
-                npops += 3;
+                npops += 2;
             }
             break;
 
@@ -2805,7 +2817,7 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta
 
     if (target && emitIteratorCloseAtTarget && target->is()) {
         ForOfLoopControl& loopinfo = target->as();
-        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF
+        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF
             return false;
     }
 
@@ -7026,11 +7038,9 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
 
     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);
@@ -7041,11 +7051,11 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
         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
@@ -7062,7 +7072,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
         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;
         }
 
@@ -7078,21 +7088,49 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
         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,
@@ -7100,14 +7138,14 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
                    "operation");
 
         // Remove VALUE from the stack to release it.
-        if (!emit1(JSOP_POP))                             // ITER RESULT
+        if (!emit1(JSOP_POP))                             // ITER
             return false;
-        if (!emit1(JSOP_UNDEFINED))                       // ITER RESULT UNDEF
+        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,
@@ -7119,29 +7157,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
         // Set offset for continues.
         loopInfo.continueTarget = { offset() };
 
-        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER RESULT UNDEF
+        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER UNDEF
             return false;
 
-        if (!emit1(JSOP_SWAP))                            // ITER UNDEF RESULT
+        if (!emit1(JSOP_FALSE))                           // ITER UNDEF FALSE
             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 (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
-            return false;                                 // ITER RESULT UNDEF
+            return false;                                 // ITER UNDEF
 
         MOZ_ASSERT(this->stackDepth == loopDepth);
     }
@@ -7156,7 +7178,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
     if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
         return false;
 
-    return emitPopN(3);                                   //
+    return emitPopN(2);                                   //
 }
 
 bool
@@ -7566,9 +7588,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
         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.
@@ -7606,31 +7626,56 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
     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
+    if (!emit1(JSOP_POP))                                 // ITER
         return false;
-    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT UNDEF
+    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.
@@ -7642,25 +7687,13 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
     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);
 
@@ -7681,7 +7714,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
     }
 
     // Pop the result and the iter.
-    return emitPopN(3);                                   //
+    return emitPopN(2);                                   //
 }
 
 bool
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index ef9c68774d60..9e26cb770ff8 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5973,11 +5973,6 @@ Parser::forHeadStart(YieldHandling yieldHandling,
     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
@@ -6125,15 +6120,19 @@ Parser::forStatement(YieldHandling yieldHandling)
 
     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);
@@ -6189,11 +6188,6 @@ Parser::forStatement(YieldHandling yieldHandling)
             stmt.refineForKind(StatementKind::ForInLoop);
             iflags |= JSITER_ENUMERATE;
         } else {
-            if (isForEach) {
-                errorAt(begin, JSMSG_BAD_FOR_EACH_LOOP);
-                return null();
-            }
-
             stmt.refineForKind(StatementKind::ForOfLoop);
         }
 
diff --git a/js/src/jit-test/tests/ion/bug1348777.js b/js/src/jit-test/tests/ion/bug1348777.js
new file mode 100644
index 000000000000..b170510d2264
--- /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();
diff --git a/js/src/jit-test/tests/wasm/builtin.js b/js/src/jit-test/tests/wasm/builtin.js
new file mode 100644
index 000000000000..7eb056e74107
--- /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');
diff --git a/js/src/jit-test/tests/wasm/profiling.js b/js/src/jit-test/tests/wasm/profiling.js
index 6c958a8aa381..22e6574ac625 100644
--- a/js/src/jit-test/tests/wasm/profiling.js
+++ b/js/src/jit-test/tests/wasm/profiling.js
@@ -13,10 +13,10 @@ 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(',');
@@ -113,6 +113,16 @@ test(
 {"":{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();
diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp
index 96008682088f..c1e368543e18 100644
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -516,10 +516,9 @@ HasLiveStackValueAtDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
             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;
 
diff --git a/js/src/jit/IonControlFlow.cpp b/js/src/jit/IonControlFlow.cpp
index d18e9143b257..c2750e9f35fd 100644
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -937,7 +937,7 @@ ControlFlowGenerator::processWhileOrForInLoop(jssrcnote* sn)
 
     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
diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h
index 81b0304495a3..295f5cbb9da6 100644
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -769,11 +769,11 @@ PropertyNameToExtraName(PropertyName* name)
 
 #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,
@@ -828,6 +828,9 @@ enum ABIFunctionType
     // 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)) |
@@ -856,7 +859,6 @@ enum ABIFunctionType
         (ArgType_General << (ArgType_Shift * 2)) |
         (ArgType_Double  << (ArgType_Shift * 3)) |
         (ArgType_General << (ArgType_Shift * 4))
-
 };
 
 enum class BarrierKind : uint32_t {
diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp
index 70becbdd88fe..457f1dee207d 100644
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5424,6 +5424,11 @@ InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choic
         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())
+			continue;
+
         JSFunction* target = &targets[i]->as();
 
         // Eliminate all entries containing the vetoed function from the map.
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
index 4ccc9bbd103b..3e9fc7886bb7 100644
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -2291,6 +2291,19 @@ 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 ABIArgIter
 {
diff --git a/js/src/jit/arm/Simulator-arm.cpp b/js/src/jit/arm/Simulator-arm.cpp
index 5d0eb07e67a7..9fa1759e68bd 100644
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -2446,6 +2446,7 @@ typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t
 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);
@@ -2634,6 +2635,21 @@ Simulator::softwareInterrupt(SimInstruction* instr)
             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(arg0);
+                fval1 = mozilla::BitwiseCast(arg1);
+            }
+            Prototype_Float32_Float32Float32 target = reinterpret_cast(external);
+            float fresult = target(fval0, fval1);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResultFloat(fresult);
+            break;
+          }
           case Args_Float32_IntInt: {
             Prototype_Float32_IntInt target = reinterpret_cast(external);
             float fresult = target(arg0, arg1);
diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h
index 0c8a1cf3d39a..459f9b0ba926 100644
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -161,6 +161,14 @@ 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(value))
     {
         // To make code serialization-safe, wasm compilation should only
@@ -202,7 +210,6 @@ struct ImmPtr
     {
         MOZ_ASSERT(!IsCompilingWasm());
     }
-
 };
 
 // The same as ImmPtr except that the intention is to patch this
diff --git a/js/src/js.msg b/js/src/js.msg
index 2aa826a2c1d7..88ff693ba964 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -594,7 +594,7 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE,     0, JSEXN_TYPEERR, "property 'return' of i
 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")
diff --git a/js/src/moz.build b/js/src/moz.build
index bec41565c7a7..51013970e5d1 100644
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -376,6 +376,7 @@ UNIFIED_SOURCES += [
     'wasm/WasmIonCompile.cpp',
     'wasm/WasmJS.cpp',
     'wasm/WasmModule.cpp',
+    'wasm/WasmRuntime.cpp',
     'wasm/WasmSignalHandlers.cpp',
     'wasm/WasmStubs.cpp',
     'wasm/WasmTable.cpp',
diff --git a/js/src/tests/ecma_2018/AsyncGenerators/browser.js b/js/src/tests/ecma_2018/AsyncGenerators/browser.js
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/js/src/tests/ecma_2018/AsyncGenerators/for-await-bad-syntax.js b/js/src/tests/ecma_2018/AsyncGenerators/for-await-bad-syntax.js
new file mode 100644
index 000000000000..814599c32c64
--- /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);
diff --git a/js/src/tests/ecma_2018/AsyncGenerators/shell.js b/js/src/tests/ecma_2018/AsyncGenerators/shell.js
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list
index 6ed8cf806d5b..ab16fb8af9fa 100644
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -870,12 +870,11 @@ 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
-
-# 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
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
index c32bf5854696..7a65ce89a9d6 100644
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -267,6 +267,9 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
     if (!caches().init())
         return false;
 
+    if (!wasm().init())
+        return false;
+
     return true;
 }
 
@@ -279,6 +282,8 @@ JSRuntime::destroyRuntime()
 
     sharedIntlData.ref().destroyInstance();
 
+    wasm().destroy();
+
     if (gcInitialized) {
         /*
          * Finish any in-progress GCs first. This ensures the parseWaitingOnGC
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
index 52226e94d220..787db3f73030 100644
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -54,6 +54,7 @@
 #include "vm/Stack.h"
 #include "vm/Stopwatch.h"
 #include "vm/Symbol.h"
+#include "wasm/WasmRuntime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
@@ -534,6 +535,13 @@ struct JSRuntime : public js::MallocProvider
     /* AsmJSCache callbacks are runtime-wide. */
     js::UnprotectedData asmJSCacheOps;
 
+  private:
+    // All runtime data needed for wasm and defined in wasm/WasmRuntime.h.
+    js::ActiveThreadData wasmRuntime_;
+
+  public:
+    js::wasm::Runtime& wasm() { return wasmRuntime_.ref(); }
+
   private:
     js::UnprotectedData trustedPrincipals_;
   public:
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
index 70150b556b52..db6b0f60bdde 100644
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1837,6 +1837,7 @@ JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
     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;
     }
diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp
index 5551d1ec5247..94ad05108124 100644
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -111,7 +111,8 @@ StaticallyLink(CodeSegment& cs, const LinkData& linkData, JSContext* cx)
         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));
@@ -295,62 +296,6 @@ 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)
 {
diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h
index 75e8700eea25..1693b37372a4 100644
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -218,119 +218,6 @@ class FuncImport
 
 typedef Vector 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).
 
diff --git a/js/src/wasm/WasmFrameIterator.cpp b/js/src/wasm/WasmFrameIterator.cpp
index f74626cde858..621c0dc13222 100644
--- a/js/src/wasm/WasmFrameIterator.cpp
+++ b/js/src/wasm/WasmFrameIterator.cpp
@@ -553,6 +553,7 @@ ProfilingFrameIterator::initFromExitFP()
         break;
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
+      case CodeRange::ImportNativeExit:
       case CodeRange::TrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::Inline:
@@ -591,9 +592,23 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
     // 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
@@ -606,10 +621,9 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
     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
@@ -618,12 +632,12 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
     // 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()) {
@@ -631,6 +645,7 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
       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()) {
@@ -658,11 +673,11 @@ ProfilingFrameIterator::ProfilingFrameIterator(const WasmActivation& activation,
             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];
@@ -740,6 +755,7 @@ ProfilingFrameIterator::operator++()
       case CodeRange::Function:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
+      case CodeRange::ImportNativeExit:
       case CodeRange::TrapExit:
       case CodeRange::DebugTrap:
       case CodeRange::Inline:
@@ -769,6 +785,7 @@ ProfilingFrameIterator::label() const
     //     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)";
 
@@ -779,6 +796,8 @@ ProfilingFrameIterator::label() const
         return importJitDescription;
       case ExitReason::ImportInterp:
         return importInterpDescription;
+      case ExitReason::ImportNative:
+        return importNativeDescription;
       case ExitReason::Trap:
         return trapDescription;
       case ExitReason::DebugTrap:
@@ -789,6 +808,7 @@ ProfilingFrameIterator::label() const
       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;
diff --git a/js/src/wasm/WasmFrameIterator.h b/js/src/wasm/WasmFrameIterator.h
index 14050424010e..dda59691ee9d 100644
--- a/js/src/wasm/WasmFrameIterator.h
+++ b/js/src/wasm/WasmFrameIterator.h
@@ -89,6 +89,7 @@ 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
 };
diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp
index 5d5574fe9d9f..676f7f6670b0 100644
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -414,6 +414,7 @@ ModuleGenerator::patchCallSites()
         }
     }
 
+    masm_.flushBuffer();
     return true;
 }
 
diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp
index 019bd1239648..f450703881bd 100644
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -19,7 +19,9 @@
 #include "wasm/WasmInstance.h"
 
 #include "jit/BaselineJIT.h"
+#include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
+
 #include "wasm/WasmModule.h"
 
 #include "jsobjinlines.h"
@@ -315,6 +317,128 @@ Instance::currentMemory_i32(Instance* instance)
     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 object,
                    UniqueCode code,
@@ -355,6 +479,11 @@ Instance::Instance(JSContext* cx,
             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();
diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h
index 46b8a622154f..27b59bde11de 100644
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -77,21 +77,13 @@ class Instance
     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,
@@ -165,6 +157,15 @@ class Instance
                        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 UniqueInstance;
diff --git a/js/src/wasm/WasmRuntime.cpp b/js/src/wasm/WasmRuntime.cpp
new file mode 100644
index 000000000000..db68c9ef31f9
--- /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 
+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(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(sin, *abiType);
+      case SymbolicAddress::CosD:
+        *abiType = Args_Double_Double;
+        return FuncCast(cos, *abiType);
+      case SymbolicAddress::TanD:
+        *abiType = Args_Double_Double;
+        return FuncCast(tan, *abiType);
+      case SymbolicAddress::ASinD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::asin, *abiType);
+      case SymbolicAddress::ACosD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::acos, *abiType);
+      case SymbolicAddress::ATanD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::atan, *abiType);
+      case SymbolicAddress::CeilD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::ceil, *abiType);
+      case SymbolicAddress::CeilF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast(fdlibm::ceilf, *abiType);
+      case SymbolicAddress::FloorD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::floor, *abiType);
+      case SymbolicAddress::FloorF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast(fdlibm::floorf, *abiType);
+      case SymbolicAddress::TruncD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::trunc, *abiType);
+      case SymbolicAddress::TruncF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast(fdlibm::truncf, *abiType);
+      case SymbolicAddress::NearbyIntD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::nearbyint, *abiType);
+      case SymbolicAddress::NearbyIntF:
+        *abiType = Args_Float32_Float32;
+        return FuncCast(fdlibm::nearbyintf, *abiType);
+      case SymbolicAddress::ExpD:
+        *abiType = Args_Double_Double;
+        return FuncCast(fdlibm::exp, *abiType);
+      case SymbolicAddress::LogD:
+        *abiType = Args_Double_Double;
+        return FuncCast(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(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);
+}
diff --git a/js/src/wasm/WasmRuntime.h b/js/src/wasm/WasmRuntime.h
new file mode 100644
index 000000000000..a33c8833893b
--- /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 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 UniqueBuiltinThunk;
+typedef Vector 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
diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp
index 9b67bcd83079..668b9e8dbfd3 100644
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -108,7 +108,7 @@ class AutoSetHandlingSegFault
 # 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])
@@ -1530,5 +1530,6 @@ js::wasm::IsPCInWasmCode(void *pc)
     if (!activation)
         return false;
 
-    return !!activation->compartment()->wasm.lookupCode(pc);
+    return !!activation->compartment()->wasm.lookupCode(pc) ||
+           !!activation->cx()->runtime()->wasm().lookupBuiltin(pc);
 }
diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp
index e0720d8f1642..1582086249ff 100644
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -31,6 +31,15 @@ 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)
 {
@@ -331,7 +340,7 @@ wasm::GenerateEntry(MacroAssembler& masm, const FuncExport& fe)
     masm.move32(Imm32(true), ReturnReg);
     masm.ret();
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -498,7 +507,7 @@ wasm::GenerateImportFunction(jit::MacroAssembler& masm, const FuncImport& fi, Si
 
     masm.wasmEmitTrapOutOfLineCode();
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -625,7 +634,7 @@ wasm::GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, uint3
 
     GenerateExitEpilogue(masm, framePushed, ExitReason::ImportInterp, &offsets);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -858,6 +867,94 @@ wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* t
 
     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 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;
 }
@@ -896,7 +993,7 @@ wasm::GenerateTrapExit(MacroAssembler& masm, Trap trap, Label* throwLabel)
 
     GenerateExitEpilogue(masm, framePushed, ExitReason::Trap, &offsets);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -926,7 +1023,7 @@ GenerateGenericMemoryAccessTrap(MacroAssembler& masm, SymbolicAddress reporter,
     masm.call(reporter);
     masm.jump(throwLabel);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -1109,7 +1206,7 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
 # error "Unknown architecture!"
 #endif
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -1147,7 +1244,7 @@ wasm::GenerateThrowStub(MacroAssembler& masm, Label* throwLabel)
     masm.mov(ImmWord(0), ReturnReg);
     masm.ret();
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
 
@@ -1198,6 +1295,6 @@ wasm::GenerateDebugTrapStub(MacroAssembler& masm, Label* throwLabel)
 
     GenerateExitEpilogue(masm, 0, ExitReason::DebugTrap, &offsets);
 
-    offsets.end = masm.currentOffset();
+    FinishOffsets(masm, &offsets);
     return offsets;
 }
diff --git a/js/src/wasm/WasmStubs.h b/js/src/wasm/WasmStubs.h
index dcd90499510d..0026f2b7fc21 100644
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -23,7 +23,11 @@
 
 namespace js {
 
-namespace jit { class MacroAssembler; class Label; }
+namespace jit {
+    class MacroAssembler;
+    class Label;
+    enum ABIFunctionType;
+}
 
 namespace wasm {
 
@@ -43,6 +47,9 @@ GenerateImportInterpExit(jit::MacroAssembler& masm, const FuncImport& fi, uint32
 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);
 
diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp
index b117f65a530c..d5f27dc9758d 100644
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -18,29 +18,16 @@
 
 #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
@@ -67,346 +54,6 @@ Val::writePayload(uint8_t* dst) const
     }
 }
 
-#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 
-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)
 {
@@ -432,125 +79,6 @@ wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode)
     }
 }
 
-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(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(sin, Args_Double_Double);
-      case SymbolicAddress::CosD:
-        return FuncCast(cos, Args_Double_Double);
-      case SymbolicAddress::TanD:
-        return FuncCast(tan, Args_Double_Double);
-      case SymbolicAddress::ASinD:
-        return FuncCast(fdlibm::asin, Args_Double_Double);
-      case SymbolicAddress::ACosD:
-        return FuncCast(fdlibm::acos, Args_Double_Double);
-      case SymbolicAddress::ATanD:
-        return FuncCast(fdlibm::atan, Args_Double_Double);
-      case SymbolicAddress::CeilD:
-        return FuncCast(fdlibm::ceil, Args_Double_Double);
-      case SymbolicAddress::CeilF:
-        return FuncCast(fdlibm::ceilf, Args_Float32_Float32);
-      case SymbolicAddress::FloorD:
-        return FuncCast(fdlibm::floor, Args_Double_Double);
-      case SymbolicAddress::FloorF:
-        return FuncCast(fdlibm::floorf, Args_Float32_Float32);
-      case SymbolicAddress::TruncD:
-        return FuncCast(fdlibm::trunc, Args_Double_Double);
-      case SymbolicAddress::TruncF:
-        return FuncCast(fdlibm::truncf, Args_Float32_Float32);
-      case SymbolicAddress::NearbyIntD:
-        return FuncCast(fdlibm::nearbyint, Args_Double_Double);
-      case SymbolicAddress::NearbyIntF:
-        return FuncCast(fdlibm::nearbyintf, Args_Float32_Float32);
-      case SymbolicAddress::ExpD:
-        return FuncCast(fdlibm::exp, Args_Double_Double);
-      case SymbolicAddress::LogD:
-        return FuncCast(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(Instance::growMemory_i32, Args_General2);
-      case SymbolicAddress::CurrentMemory:
-        return FuncCast(Instance::currentMemory_i32, Args_General1);
-      case SymbolicAddress::Limit:
-        break;
-    }
-
-    MOZ_CRASH("Bad SymbolicAddress");
-}
-
 static uint32_t
 GetCPUID()
 {
@@ -1139,3 +667,76 @@ DebugFrame::leave(JSContext* cx)
        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);
+}
diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h
index aed3dd6aa461..779422e10033 100644
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -97,7 +97,6 @@ typedef int32_t I32x4[4];
 typedef float F32x4[4];
 
 class Code;
-class CodeRange;
 class GlobalSegment;
 class Memory;
 class Module;
@@ -820,6 +819,120 @@ struct FuncOffsets : CallableOffsets
     }
 };
 
+// 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
@@ -1019,9 +1132,6 @@ enum class SymbolicAddress
 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.
diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py
index 0613ad0dc395..66ca6b4d0a35 100644
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -3,6 +3,7 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import sys
+import logging
 import os
 import time
 import tempfile
@@ -340,6 +341,8 @@ def run_test_harness(parser, options):
     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)
diff --git a/mobile/android/app/moz.build b/mobile/android/app/moz.build
index cfa46fe09660..77f28cb58a59 100644
--- a/mobile/android/app/moz.build
+++ b/mobile/android/app/moz.build
@@ -4,6 +4,33 @@
 # 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]
 
diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build
index e742990463f3..3a331764a7fd 100644
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -4,6 +4,117 @@
 # 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 += [
diff --git a/mobile/android/chrome/moz.build b/mobile/android/chrome/moz.build
index 0c6d737bf833..e881d7e0c166 100644
--- a/mobile/android/chrome/moz.build
+++ b/mobile/android/chrome/moz.build
@@ -4,6 +4,13 @@
 # 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']
diff --git a/mobile/android/components/moz.build b/mobile/android/components/moz.build
index dbe898c59996..89dd676797d6 100644
--- a/mobile/android/components/moz.build
+++ b/mobile/android/components/moz.build
@@ -4,6 +4,12 @@
 # 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',
 ]
diff --git a/mobile/android/extensions/moz.build b/mobile/android/extensions/moz.build
index 24b82646b976..48ca8570779b 100644
--- a/mobile/android/extensions/moz.build
+++ b/mobile/android/extensions/moz.build
@@ -4,6 +4,9 @@
 # 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 += [
diff --git a/mobile/android/fonts/moz.build b/mobile/android/fonts/moz.build
index 55c114f79ed8..b7d3875f0408 100644
--- a/mobile/android/fonts/moz.build
+++ b/mobile/android/fonts/moz.build
@@ -4,6 +4,9 @@
 # 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',
diff --git 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
index f7fd52f144bb..80762840104b 100644
--- 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
@@ -39,7 +39,7 @@ public class GeckoViewActivity extends Activity {
         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();
diff --git a/mobile/android/installer/moz.build b/mobile/android/installer/moz.build
index 28919c271d33..3d00e1d0b143 100644
--- a/mobile/android/installer/moz.build
+++ b/mobile/android/installer/moz.build
@@ -4,3 +4,5 @@
 # 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')
diff --git a/mobile/android/javaaddons/moz.build b/mobile/android/javaaddons/moz.build
index c2f004cc19aa..d225023659da 100644
--- a/mobile/android/javaaddons/moz.build
+++ b/mobile/android/javaaddons/moz.build
@@ -4,6 +4,9 @@
 # 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',
diff --git a/mobile/android/locales/moz.build b/mobile/android/locales/moz.build
index eb4454d28f88..41e4a8b00c66 100644
--- a/mobile/android/locales/moz.build
+++ b/mobile/android/locales/moz.build
@@ -4,4 +4,7 @@
 # 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
diff --git a/mobile/android/modules/moz.build b/mobile/android/modules/moz.build
index 785c2ed3a843..88e8412fd69d 100644
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -4,6 +4,19 @@
 # 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 += [
diff --git a/mobile/android/moz.build b/mobile/android/moz.build
index b3f25275a33d..d1902bce606f 100644
--- a/mobile/android/moz.build
+++ b/mobile/android/moz.build
@@ -4,6 +4,51 @@
 # 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 += [
diff --git a/mobile/android/stumbler/moz.build b/mobile/android/stumbler/moz.build
index 651cd18dd8a1..d75665644003 100644
--- a/mobile/android/stumbler/moz.build
+++ b/mobile/android/stumbler/moz.build
@@ -4,6 +4,9 @@
 # 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')
diff --git a/mobile/android/tests/background/moz.build b/mobile/android/tests/background/moz.build
index 891477f32189..18abcd86ea41 100644
--- a/mobile/android/tests/background/moz.build
+++ b/mobile/android/tests/background/moz.build
@@ -4,6 +4,9 @@
 # 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',
 ]
diff --git a/mobile/android/tests/browser/moz.build b/mobile/android/tests/browser/moz.build
index ca1214e445d3..4672b775997e 100644
--- a/mobile/android/tests/browser/moz.build
+++ b/mobile/android/tests/browser/moz.build
@@ -4,6 +4,19 @@
 # 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']:
diff --git a/mobile/android/tests/javaaddons/moz.build b/mobile/android/tests/javaaddons/moz.build
index 2fabebc568c1..bd95c77ced3f 100644
--- a/mobile/android/tests/javaaddons/moz.build
+++ b/mobile/android/tests/javaaddons/moz.build
@@ -4,6 +4,9 @@
 # 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'
 
diff --git a/mobile/android/tests/moz.build b/mobile/android/tests/moz.build
index 2ec31396e0b7..b7f9ac2ca47a 100644
--- a/mobile/android/tests/moz.build
+++ b/mobile/android/tests/moz.build
@@ -4,6 +4,10 @@
 # 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',
diff --git a/mobile/locales/moz.build b/mobile/locales/moz.build
index eb4454d28f88..adb2ef014615 100644
--- a/mobile/locales/moz.build
+++ b/mobile/locales/moz.build
@@ -4,4 +4,7 @@
 # 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
diff --git 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
index b8f238c743da..63ce79b7585c 100644
--- 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,7 +1,7 @@
 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.
@@ -10,8 +10,10 @@ diff --git a/modules/fdlibm/src/math_private.h b/modules/fdlibm/src/math_private
   * 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
 +
diff --git a/modules/fdlibm/src/math_private.h b/modules/fdlibm/src/math_private.h
index 6947cecc0970..86597e75f74a 100644
--- a/modules/fdlibm/src/math_private.h
+++ b/modules/fdlibm/src/math_private.h
@@ -38,8 +38,10 @@
  * 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
 
diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp
index 670f2c3d4c4a..606dac8b2018 100644
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -10,6 +10,7 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #include "nsXULAppAPI.h"
@@ -478,10 +479,11 @@ 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 prefService =
       do_GetService(NS_PREFSERVICE_CONTRACTID);
   }
diff --git a/netwerk/base/nsINetworkInterceptController.idl b/netwerk/base/nsINetworkInterceptController.idl
index 17d27de42aec..721b7a334d76 100644
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -14,12 +14,16 @@ interface nsIURI;
 %{C++
 #include "nsIConsoleReportCollector.h"
 namespace mozilla {
+class TimeStamp;
+
 namespace dom {
 class ChannelInfo;
 }
 }
 %}
 
+native TimeStamp(mozilla::TimeStamp);
+
 [ptr] native ChannelInfo(mozilla::dom::ChannelInfo);
 
 /**
@@ -97,6 +101,30 @@ interface nsIInterceptedChannel : nsISupports
     [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
     GetConsoleReportCollector()
diff --git a/netwerk/base/nsITimedChannel.idl b/netwerk/base/nsITimedChannel.idl
index 6ec2d1ff80a3..9b646e046ac7 100644
--- a/netwerk/base/nsITimedChannel.idl
+++ b/netwerk/base/nsITimedChannel.idl
@@ -26,6 +26,15 @@ interface nsITimedChannel : nsISupports {
   [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;
@@ -66,6 +75,12 @@ interface nsITimedChannel : nsISupports {
   // 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;
diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h
index 5dc43ca07422..be99db82f174 100644
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -656,10 +656,6 @@ bool NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport = false);
 #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.
  */
diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh
index fdea367b6fff..92b9000c0b18 100644
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -19,6 +19,7 @@ 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 {
@@ -132,6 +133,12 @@ struct HttpChannelOpenArgs
   uint64_t                    contentWindowId;
   nsCString                   preferredAlternativeType;
   uint64_t                    topLevelOuterContentWindowId;
+  TimeStamp                   launchServiceWorkerStart;
+  TimeStamp                   launchServiceWorkerEnd;
+  TimeStamp                   dispatchFetchEventStart;
+  TimeStamp                   dispatchFetchEventEnd;
+  TimeStamp                   handleFetchEventStart;
+  TimeStamp                   handleFetchEventEnd;
 };
 
 struct HttpChannelConnectArgs
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
index 6eadc0aaba4e..a5e53dd2ba47 100644
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3617,6 +3617,84 @@ HttpBaseChannel::TimingAllowCheck(nsIPrincipal *aOrigin, bool *_retval)
   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;
@@ -3701,6 +3779,12 @@ HttpBaseChannel::Get##name##Time(PRTime* _retval) {            \
 
 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)
diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h
index f35d19bdfd28..591c08496ea8 100644
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -560,6 +560,12 @@ protected:
   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;
diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp
index e53374ae0694..e7dbf4b74838 100644
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2423,6 +2423,13 @@ HttpChannelChild::ContinueAsyncOpen()
     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.
diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp
index d990ccb3ebdc..dbacb46f2e15 100644
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -132,7 +132,13 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
                        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:
   {
@@ -331,7 +337,13 @@ HttpChannelParent::DoAsyncOpen(  const URIParams&           aURI,
                                  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 uri = DeserializeURI(aURI);
   if (!uri) {
@@ -540,6 +552,13 @@ HttpChannelParent::DoAsyncOpen(  const URIParams&           aURI,
   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 appCacheChan =
     do_QueryObject(mChannel);
   nsCOMPtr appCacheService =
diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h
index b4c161482cf6..f552dcc086dc 100644
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -149,7 +149,13 @@ protected:
               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;
diff --git a/netwerk/protocol/http/InterceptedChannel.cpp b/netwerk/protocol/http/InterceptedChannel.cpp
index 0e4185910e4f..0da8f95aaf10 100644
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -10,6 +10,7 @@
 #include "nsInputStreamPump.h"
 #include "nsIPipe.h"
 #include "nsIStreamListener.h"
+#include "nsITimedChannel.h"
 #include "nsHttpChannel.h"
 #include "HttpChannelChild.h"
 #include "nsHttpResponseHead.h"
@@ -131,6 +132,40 @@ InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle)
   return NS_OK;
 }
 
+NS_IMETHODIMP
+InterceptedChannelBase::SaveTimeStampsToUnderlyingChannel()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr underlyingChannel;
+  nsresult rv = GetChannel(getter_AddRefs(underlyingChannel));
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+  nsCOMPtr 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
 InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel)
diff --git a/netwerk/protocol/http/InterceptedChannel.h b/netwerk/protocol/http/InterceptedChannel.h
index 9973b40bf411..6df8a24a7610 100644
--- a/netwerk/protocol/http/InterceptedChannel.h
+++ b/netwerk/protocol/http/InterceptedChannel.h
@@ -48,6 +48,13 @@ protected:
   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);
@@ -62,6 +69,50 @@ public:
   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
   SecureUpgradeChannelURI(nsIChannel* aChannel);
 };
diff --git a/netwerk/protocol/http/NullHttpChannel.cpp b/netwerk/protocol/http/NullHttpChannel.cpp
index b14e5578f166..12fdb7b29347 100644
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -582,6 +582,90 @@ NullHttpChannel::GetAsyncOpen(mozilla::TimeStamp *aAsyncOpen)
   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)
 {
@@ -772,6 +856,12 @@ NullHttpChannel::Get##name##Time(PRTime* _retval) {            \
 
 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)
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index 246f85498b5f..0140d01aae93 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5367,6 +5367,18 @@ nsHttpChannel::SetupReplacementChannel(nsIURI       *newURI,
         }
     }
 
+    if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
+      nsCOMPtr 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;
 }
 
diff --git a/parser/xml/moz.build b/parser/xml/moz.build
index b659c0904313..7d88095ab2be 100644
--- a/parser/xml/moz.build
+++ b/parser/xml/moz.build
@@ -30,7 +30,7 @@ EXPORTS += [
     'nsSAXXMLReader.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'nsSAXAttributes.cpp',
     'nsSAXLocator.cpp',
     'nsSAXXMLReader.cpp',
diff --git a/taskcluster/ci/build/android.yml b/taskcluster/ci/build/android.yml
index 4d3a6635cbfb..e4b2d3662fd9 100644
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -146,7 +146,7 @@ android-api-15-gradle/opt:
           - 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
diff --git a/taskcluster/ci/test/test-sets.yml b/taskcluster/ci/test/test-sets.yml
index 9534f2624dc9..dc66ecaeb6f2 100644
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -241,6 +241,7 @@ android-opt-tests:
 android-gradle-tests:
     - mochitest-chrome
     - robocop
+    - geckoview
 
 android-x86-tests:
     - mochitest-chrome
diff --git a/taskcluster/ci/test/tests.yml b/taskcluster/ci/test/tests.yml
index 603ebad58e85..47e2a7f4eee4 100644
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -606,6 +606,7 @@ mochitest-clipboard:
     suite: mochitest/clipboard
     treeherder-symbol: tc-M(cl)
     loopback-video: true
+    docker-image: {"in-tree": "desktop1604-test"}
     instance-size: xlarge
     e10s:
       by-test-platform:
@@ -914,6 +915,7 @@ mochitest-style:
     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:
@@ -936,6 +938,7 @@ mochitest-chrome-style:
     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' ]
@@ -1096,6 +1099,21 @@ robocop:
         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
diff --git a/taskcluster/taskgraph/transforms/tests.py b/taskcluster/taskgraph/transforms/tests.py
index c7ac684ea62c..f0e3e03f787c 100644
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -341,7 +341,10 @@ def set_target(config, tests):
         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(),
diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py
index c453037d68f7..3886b24680c7 100644
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -7,6 +7,7 @@ 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
@@ -957,6 +958,9 @@ class AndroidArguments(ArgumentContainer):
             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:
diff --git a/testing/mochitest/moz.build b/testing/mochitest/moz.build
index ce6bb6fd72ae..adf7c7f96568 100644
--- a/testing/mochitest/moz.build
+++ b/testing/mochitest/moz.build
@@ -61,6 +61,7 @@ TEST_HARNESS_FILES.testing.mochitest += [
     'nested_setup.js',
     'pywebsocket_wrapper.py',
     'redirect.html',
+    'rungeckoview.py',
     'runrobocop.py',
     'runtests.py',
     'runtestsremote.py',
diff --git a/testing/mochitest/rungeckoview.py b/testing/mochitest/rungeckoview.py
new file mode 100644
index 000000000000..1cf71b482238
--- /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())
diff --git a/testing/mozharness/configs/android/androidarm_4_3.py b/testing/mozharness/configs/android/androidarm_4_3.py
index 5905c8384a3a..394845c8ae7e 100644
--- a/testing/mozharness/configs/android/androidarm_4_3.py
+++ b/testing/mozharness/configs/android/androidarm_4_3.py
@@ -356,6 +356,14 @@ config = {
                 "--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,
diff --git a/testing/mozharness/mozharness/mozilla/testing/errors.py b/testing/mozharness/mozharness/mozilla/testing/errors.py
index 3937b28c43da..8169b5b99fc0 100644
--- a/testing/mozharness/mozharness/mozilla/testing/errors.py
+++ b/testing/mozharness/mozharness/mozilla/testing/errors.py
@@ -94,6 +94,12 @@ TinderBoxPrintRe = {
         '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)"),
diff --git a/testing/mozharness/mozharness/mozilla/testing/testbase.py b/testing/mozharness/mozharness/mozilla/testing/testbase.py
index 23660fd03bcb..ca98cb33d30e 100755
--- a/testing/mozharness/mozharness/mozilla/testing/testbase.py
+++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py
@@ -439,6 +439,7 @@ You can set this by:
             'mochitest-plain-clipboard': 'mochitest',
             'mochitest-plain-gpu': 'mochitest',
             'mochitest-gl': 'mochitest',
+            'geckoview': 'mochitest',
             'jsreftest': 'reftest',
             'crashtest': 'reftest',
             'reftest-debug': 'reftest',
diff --git a/testing/remotecppunittests.py b/testing/remotecppunittests.py
index 1be694fca2b6..0c3fcfb1d2a6 100644
--- a/testing/remotecppunittests.py
+++ b/testing/remotecppunittests.py
@@ -217,10 +217,14 @@ def run_test_harness(options, args):
     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()
diff --git 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
index 3c56ba59b12d..b71489db6125 100644
--- 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
-
-  [object]
-    expected: FAIL
-
-  [embed]
-    expected: FAIL
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1336760
 
diff --git 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
index b3788ce1c54c..73195f727b29 100644
--- 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
-
-  [object]
-    expected: FAIL
-
-  [embed]
-    expected: FAIL
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1328351
 
diff --git a/testing/web-platform/meta/websockets/constructor/014.html.ini b/testing/web-platform/meta/websockets/constructor/014.html.ini
index e8561ce6ccca..dc9b4a76b5f5 100644
--- a/testing/web-platform/meta/websockets/constructor/014.html.ini
+++ b/testing/web-platform/meta/websockets/constructor/014.html.ini
@@ -2,6 +2,7 @@
   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
diff --git a/testing/xpcshell/remotexpcshelltests.py b/testing/xpcshell/remotexpcshelltests.py
index 568f51656c88..cdb242c2b613 100644
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -4,6 +4,7 @@
 # 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
@@ -578,10 +579,13 @@ def main():
                                     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!"
diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp
index cd3c031491f7..874d0265ad00 100644
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -921,6 +921,8 @@ public:
 
   static void RecordIceCandidates(const uint32_t iceCandidateBitmask,
                                   const bool success);
+  static bool CanRecordBase();
+  static bool CanRecordExtended();
 private:
   TelemetryImpl();
   ~TelemetryImpl();
@@ -952,6 +954,8 @@ private:
   Mutex mHashMutex;
   HangReports mHangReports;
   Mutex mHangReportsMutex;
+  Atomic mCanRecordBase;
+  Atomic mCanRecordExtended;
 
 #if defined(ENABLE_STACK_CAPTURE)
   // Stores data about stacks captured on demand.
@@ -1235,6 +1239,8 @@ TelemetryImpl::AsyncFetchTelemetryData(nsIFetchTelemetryDataCallback *aCallback)
 TelemetryImpl::TelemetryImpl()
   : mHashMutex("Telemetry::mHashMutex")
   , mHangReportsMutex("Telemetry::mHangReportsMutex")
+  , mCanRecordBase(false)
+  , mCanRecordExtended(false)
   , mThreadHangStatsMutex("Telemetry::mThreadHangStatsMutex")
   , mCachedTelemetryData(false)
   , mLastShutdownTime(0)
@@ -2267,15 +2273,18 @@ TelemetryImpl::GetKeyedHistogramById(const nsACString &name, JSContext *cx,
  */
 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;
 }
 
@@ -2288,15 +2297,18 @@ TelemetryImpl::SetCanRecordBase(bool canRecord) {
  */
 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;
 }
 
@@ -2339,6 +2351,9 @@ TelemetryImpl::CreateTelemetryInstance()
   // AddRef for the caller
   nsCOMPtr ret = sTelemetry;
 
+  sTelemetry->mCanRecordBase = useTelemetry;
+  sTelemetry->mCanRecordExtended = useTelemetry;
+
   sTelemetry->InitMemoryReporter();
   InitHistogramRecordingEnabled(); // requires sTelemetry to exist
 
@@ -2689,6 +2704,28 @@ TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
   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)
 
@@ -3355,13 +3392,13 @@ GetHistogramName(HistogramID id)
 bool
 CanRecordBase()
 {
-  return TelemetryHistogram::CanRecordBase();
+  return TelemetryImpl::CanRecordBase();
 }
 
 bool
 CanRecordExtended()
 {
-  return TelemetryHistogram::CanRecordExtended();
+  return TelemetryImpl::CanRecordExtended();
 }
 
 void
diff --git a/toolkit/components/telemetry/parse_events.py b/toolkit/components/telemetry/parse_events.py
index 038f86d6eb89..2503a9919b1a 100644
--- a/toolkit/components/telemetry/parse_events.py
+++ b/toolkit/components/telemetry/parse_events.py
@@ -20,7 +20,7 @@ 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__
 
diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp
index 9eba9ee1bf4d..3d40a1b58936 100644
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -770,6 +770,12 @@ XRE_InitParentProcess(int aArgc,
   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;
diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp
index 8b96370f9f9d..5ac906326744 100644
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -1981,7 +1981,6 @@ profiler_init(void* aStackTop)
 {
   LOG("profiler_init");
 
-  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_RELEASE_ASSERT(!gPS);
 
   const char* features[] = { "js"
diff --git a/tools/profiler/core/shared-libraries-linux.cc b/tools/profiler/core/shared-libraries-linux.cc
index 4f50e73410d7..4eea4db8bd2c 100644
--- a/tools/profiler/core/shared-libraries-linux.cc
+++ b/tools/profiler/core/shared-libraries-linux.cc
@@ -1,3 +1,5 @@
+/* -*- 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/. */
@@ -15,12 +17,56 @@
 #include "platform.h"
 #include "shared-libraries.h"
 #include "mozilla/Unused.h"
+#include "nsDebug.h"
 #include "nsNativeCharsetUtils.h"
 
 #include "common/linux/file_id.h"
 #include 
 
-#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  // dl_phdr_info
+# include 
+# include 
+# include 
+
+#elif defined(GP_OS_android) && !defined(MOZ_WIDGET_GONK)
+# define CONFIG_CASE_2 1
+# include "ElfLoader.h" // dl_phdr_info
+# include 
+# include 
+# include 
+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)
@@ -39,26 +85,9 @@ static std::string getId(const char *bin_name)
   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  // dl_phdr_info
-#endif
-#include 
-#include 
-#include 
-
-#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)
 {
@@ -84,7 +113,9 @@ dl_iterate_callback(struct dl_phdr_info *dl_info, size_t size, void *data)
   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('/');
@@ -99,15 +130,15 @@ dl_iterate_callback(struct dl_phdr_info *dl_info, size_t size, void *data)
 
   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
@@ -115,12 +146,12 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
     // 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);
@@ -129,7 +160,6 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
   int count = 0;
   while (std::getline(maps, line)) {
     int ret;
-    //XXX: needs input sanitizing
     unsigned long start;
     unsigned long end;
     char perm[6] = "";
@@ -143,26 +173,32 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
       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('/');
@@ -171,16 +207,22 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
     }
 
     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;
 }