Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 09 Mar 2018 02:17:42 +0200
changeset 407233 31a33fc61956
parent 407211 144f1e24df60 (current diff)
parent 407232 b93a9c630a0c (diff)
child 407255 5b67155a1e61
child 407312 16cdea986fe1
push id33596
push userncsoregi@mozilla.com
push date2018-03-09 00:18 +0000
treeherdermozilla-central@31a33fc61956 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
31a33fc61956 / 60.0a1 / 20180309003239 / files
nightly linux64
31a33fc61956 / 60.0a1 / 20180309003239 / files
nightly mac
31a33fc61956 / 60.0a1 / 20180309003239 / files
nightly win32
31a33fc61956 / 60.0a1 / 20180309003239 / files
nightly win64
31a33fc61956 / 60.0a1 / 20180309003239 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
js/src/builtin/SymbolObject.cpp
js/src/builtin/SymbolObject.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/vm/String-inl.h
js/src/vm/String.cpp
js/src/vm/String.h
js/src/vm/StringBuffer.cpp
js/src/vm/StringBuffer.h
js/src/vm/Symbol.cpp
js/src/vm/Symbol.h
js/src/vm/Unicode.cpp
js/src/vm/Unicode.h
js/src/vm/UnicodeData.txt
js/src/vm/UnicodeNonBMP.h
js/src/vm/make_unicode.py
--- a/build/sparse-profiles/taskgraph
+++ b/build/sparse-profiles/taskgraph
@@ -1,14 +1,15 @@
 %include build/sparse-profiles/mach
 
 [include]
 # These files are read as part of generating the taskgraph.
 path:browser/locales/l10n-changesets.json
 path:mobile/locales/l10n-changesets.json
+path:browser/locales/shipped-locales
 path:browser/config/version_display.txt
 path:browser/config/version.txt
 
 # Lots of random files in here are read. Just pull in the whole thing.
 path:build/
 
 # TODO remove once bug 1402010 is resolved and test manifests aren't
 # processed in Files() reading mode in moz.build files.
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1258,18 +1258,16 @@ nsGlobalWindowInner::CleanUp()
   }
   mAudioContexts.Clear();
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
-  mServiceWorkerRegistrationTable.Clear();
-
   mIntlUtils = nullptr;
 }
 
 void
 nsGlobalWindowInner::FreeInnerObjects()
 {
   // Make sure that this is called before we null out the document and
   // other members that the window destroyed observers could
@@ -1463,18 +1461,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   } else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
 
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable)
-
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopInnerWindow)
 
@@ -1548,17 +1544,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
   tmp->CleanupCachedXBLHandlers();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable)
 
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
 #endif
 
   if (tmp->mOuterWindow) {
     nsGlobalWindowOuter::Cast(tmp->mOuterWindow)->
       MaybeClearInnerWindow(tmp);
@@ -5200,36 +5195,16 @@ nsGlobalWindowInner::GetCaches(ErrorResu
                                                      storageBlocked,
                                                      forceTrustedOrigin, aRv);
   }
 
   RefPtr<CacheStorage> ref = mCacheStorage;
   return ref.forget();
 }
 
-already_AddRefed<ServiceWorkerRegistration>
-nsPIDOMWindowInner::GetServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor)
-{
-  NS_ConvertUTF8toUTF16 scope(aDescriptor.Scope());
-  RefPtr<ServiceWorkerRegistration> registration;
-  if (!mServiceWorkerRegistrationTable.Get(scope,
-                                           getter_AddRefs(registration))) {
-    registration =
-      ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
-    mServiceWorkerRegistrationTable.Put(scope, registration);
-  }
-  return registration.forget();
-}
-
-void
-nsPIDOMWindowInner::InvalidateServiceWorkerRegistration(const nsAString& aScope)
-{
-  mServiceWorkerRegistrationTable.Remove(aScope);
-}
-
 void
 nsGlobalWindowInner::FireOfflineStatusEventIfChanged()
 {
   if (!IsCurrentInnerWindow())
     return;
 
   // Don't fire an event if the status hasn't changed
   if (mWasOffline == NS_IsOffline()) {
@@ -6430,16 +6405,38 @@ nsGlobalWindowInner::GetOrCreateServiceW
 
   if (!ref) {
     ref = ServiceWorker::Create(this, aDescriptor);
   }
 
   return ref.forget();
 }
 
+RefPtr<ServiceWorkerRegistration>
+nsGlobalWindowInner::GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  RefPtr<ServiceWorkerRegistration> ref;
+  ForEachEventTargetObject([&] (DOMEventTargetHelper* aTarget, bool* aDoneOut) {
+    RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
+    if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
+      return;
+    }
+
+    ref = swr.forget();
+    *aDoneOut = true;
+  });
+
+  if (!ref) {
+    ref = ServiceWorkerRegistration::CreateForMainThread(this, aDescriptor);
+  }
+
+  return ref.forget();
+}
+
 nsresult
 nsGlobalWindowInner::FireDelayedDOMEvents()
 {
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   // Fires an offline status event if the offline status has changed
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -351,16 +351,19 @@ public:
 
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const override;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController() const override;
 
   virtual RefPtr<mozilla::dom::ServiceWorker>
   GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor) override;
 
+  RefPtr<mozilla::dom::ServiceWorkerRegistration>
+  GetOrCreateServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor) override;
+
   void NoteCalledRegisterForServiceWorkerScope(const nsACString& aScope);
 
   virtual nsresult FireDelayedDOMEvents() override;
 
   virtual nsresult SetNewDocument(nsIDocument *aDocument,
                                   nsISupports *aState,
                                   bool aForceReuseInnerWindow) override;
 
--- a/dom/base/nsIGlobalObject.cpp
+++ b/dom/base/nsIGlobalObject.cpp
@@ -2,26 +2,29 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIGlobalObject.h"
 
 #include "mozilla/dom/ServiceWorker.h"
+#include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 
 using mozilla::MallocSizeOf;
 using mozilla::Maybe;
 using mozilla::DOMEventTargetHelper;
 using mozilla::dom::ClientInfo;
 using mozilla::dom::ServiceWorker;
 using mozilla::dom::ServiceWorkerDescriptor;
+using mozilla::dom::ServiceWorkerRegistration;
+using mozilla::dom::ServiceWorkerRegistrationDescriptor;
 
 nsIGlobalObject::~nsIGlobalObject()
 {
   UnlinkHostObjectURIs();
   DisconnectEventTargetObjects();
   MOZ_DIAGNOSTIC_ASSERT(mEventTargetObjects.IsEmpty());
 }
 
@@ -191,15 +194,22 @@ nsIGlobalObject::GetController() const
 
 RefPtr<ServiceWorker>
 nsIGlobalObject::GetOrCreateServiceWorker(const ServiceWorkerDescriptor& aDescriptor)
 {
   MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service workers");
   return nullptr;
 }
 
+RefPtr<ServiceWorkerRegistration>
+nsIGlobalObject::GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  MOZ_DIAGNOSTIC_ASSERT(false, "this global should not have any service worker registrations");
+  return nullptr;
+}
+
 size_t
 nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const
 {
   size_t rtn = mHostObjectURIs.ShallowSizeOfExcludingThis(aSizeOf);
   rtn += mEventTargetObjects.ShallowSizeOfExcludingThis(aSizeOf);
   return rtn;
 }
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -25,16 +25,18 @@
 
 class nsCycleCollectionTraversalCallback;
 class nsIPrincipal;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class ServiceWorker;
+class ServiceWorkerRegistration;
+class ServiceWorkerRegistrationDescriptor;
 } // namespace dom
 } // namespace mozilla
 
 class nsIGlobalObject : public nsISupports,
                         public mozilla::dom::DispatcherTrait
 {
   nsTArray<nsCString> mHostObjectURIs;
 
@@ -114,16 +116,21 @@ public:
   virtual mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor>
   GetController() const;
 
   // Get the DOM object for the given descriptor or attempt to create one.
   // Creation can still fail and return nullptr during shutdown, etc.
   virtual RefPtr<mozilla::dom::ServiceWorker>
   GetOrCreateServiceWorker(const mozilla::dom::ServiceWorkerDescriptor& aDescriptor);
 
+  // Get the DOM object for the given descriptor or attempt to create one.
+  // Creation can still fail and return nullptr during shutdown, etc.
+  virtual RefPtr<mozilla::dom::ServiceWorkerRegistration>
+  GetOrCreateServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor);
+
 protected:
   virtual ~nsIGlobalObject();
 
   void
   StartDying()
   {
     mIsDying = true;
   }
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -50,18 +50,16 @@ class ClientInfo;
 class ClientState;
 class DocGroup;
 class TabGroup;
 class Element;
 class Navigator;
 class Performance;
 class ServiceWorker;
 class ServiceWorkerDescriptor;
-class ServiceWorkerRegistration;
-class ServiceWorkerRegistrationDescriptor;
 class Timeout;
 class TimeoutManager;
 class CustomElementRegistry;
 enum class CallerType : uint32_t;
 } // namespace dom
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
@@ -185,20 +183,16 @@ public:
   bool AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void MuteAudioContexts();
   void UnmuteAudioContexts();
 
   bool GetAudioCaptured() const;
   nsresult SetAudioCapture(bool aCapture);
 
-  already_AddRefed<mozilla::dom::ServiceWorkerRegistration>
-    GetServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor);
-  void InvalidateServiceWorkerRegistration(const nsAString& aScope);
-
   mozilla::dom::Performance* GetPerformance();
 
   bool HasMutationListeners(uint32_t aMutationEventType) const
   {
     if (!mOuterWindow) {
       NS_ERROR("HasMutationListeners() called on orphan inner window!");
 
       return false;
@@ -650,21 +644,16 @@ protected:
 
   nsCOMPtr<mozilla::dom::EventTarget> mParentTarget; // strong
 
   RefPtr<mozilla::dom::Performance> mPerformance;
   mozilla::UniquePtr<mozilla::dom::TimeoutManager> mTimeoutManager;
 
   RefPtr<mozilla::dom::Navigator> mNavigator;
 
-  typedef nsRefPtrHashtable<nsStringHashKey,
-                            mozilla::dom::ServiceWorkerRegistration>
-          ServiceWorkerRegistrationTable;
-  ServiceWorkerRegistrationTable mServiceWorkerRegistrationTable;
-
   // These variables are only used on inner windows.
   uint32_t mMutationBits;
 
   uint32_t mActivePeerConnections;
 
   bool mIsDocumentLoaded;
   bool mIsHandlingResizeEvent;
   bool mMayHavePaintEventListener;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -448,16 +448,48 @@ ConsoleListener::Observe(nsIConsoleMessa
     NS_ENSURE_SUCCESS(rv, rv);
     rv = scriptError->GetLineNumber(&lineNum);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = scriptError->GetColumnNumber(&colNum);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = scriptError->GetFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    {
+      AutoJSAPI jsapi;
+      jsapi.Init();
+      JSContext* cx = jsapi.cx();
+
+      JS::RootedValue stack(cx);
+      rv = scriptError->GetStack(&stack);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (stack.isObject()) {
+        JSAutoCompartment ac(cx, &stack.toObject());
+
+        StructuredCloneData data;
+        ErrorResult err;
+        data.Write(cx, stack, err);
+        if (err.Failed()) {
+          return err.StealNSResult();
+        }
+
+        ClonedMessageData cloned;
+        if (!data.BuildClonedMessageDataForChild(mChild, cloned)) {
+          return NS_ERROR_FAILURE;
+        }
+
+        mChild->SendScriptErrorWithStack(msg, sourceName, sourceLine,
+                                         lineNum, colNum, flags, category,
+                                         cloned);
+        return NS_OK;
+      }
+    }
+
+
     mChild->SendScriptError(msg, sourceName, sourceLine,
                             lineNum, colNum, flags, category);
     return NS_OK;
   }
 
   nsString msg;
   nsresult rv = aMessage->GetMessageMoz(getter_Copies(msg));
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -154,16 +154,17 @@
 #include "nsIWindowWatcher.h"
 #include "nsPIWindowWatcher.h"
 #include "nsThread.h"
 #include "nsWindowWatcher.h"
 #include "nsIXULRuntime.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
 #include "nsMemoryInfoDumper.h"
 #include "nsMemoryReporterManager.h"
+#include "nsScriptError.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStyleSheetService.h"
 #include "nsThreadUtils.h"
 #include "nsToolkitCompsCID.h"
 #include "nsWidgetsCID.h"
 #include "PreallocatedProcessManager.h"
 #include "ProcessPriorityManager.h"
 #include "SandboxHal.h"
@@ -3888,24 +3889,80 @@ mozilla::ipc::IPCResult
 ContentParent::RecvScriptError(const nsString& aMessage,
                                const nsString& aSourceName,
                                const nsString& aSourceLine,
                                const uint32_t& aLineNumber,
                                const uint32_t& aColNumber,
                                const uint32_t& aFlags,
                                const nsCString& aCategory)
 {
+  return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
+                                 aLineNumber, aColNumber, aFlags,
+                                 aCategory);
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvScriptErrorWithStack(const nsString& aMessage,
+                                        const nsString& aSourceName,
+                                        const nsString& aSourceLine,
+                                        const uint32_t& aLineNumber,
+                                        const uint32_t& aColNumber,
+                                        const uint32_t& aFlags,
+                                        const nsCString& aCategory,
+                                        const ClonedMessageData& aFrame)
+{
+  return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
+                                 aLineNumber, aColNumber, aFlags,
+                                 aCategory, &aFrame);
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvScriptErrorInternal(const nsString& aMessage,
+                                       const nsString& aSourceName,
+                                       const nsString& aSourceLine,
+                                       const uint32_t& aLineNumber,
+                                       const uint32_t& aColNumber,
+                                       const uint32_t& aFlags,
+                                       const nsCString& aCategory,
+                                       const ClonedMessageData* aStack)
+{
   RefPtr<nsConsoleService> consoleService = GetConsoleService();
   if (!consoleService) {
     return IPC_OK();
   }
 
-  nsCOMPtr<nsIScriptError> msg(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
-  nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine,
-                          aLineNumber, aColNumber, aFlags, aCategory.get());
+  nsCOMPtr<nsIScriptError> msg;
+
+  if (aStack) {
+    StructuredCloneData data;
+    UnpackClonedMessageDataForParent(*aStack, data);
+
+    AutoJSAPI jsapi;
+    if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+      MOZ_CRASH();
+    }
+    JSContext* cx = jsapi.cx();
+
+    JS::RootedValue stack(cx);
+    ErrorResult rv;
+    data.Read(cx, &stack, rv);
+    if (rv.Failed() || !stack.isObject()) {
+      rv.SuppressException();
+      return IPC_OK();
+    }
+
+    JS::RootedObject stackObj(cx, &stack.toObject());
+    msg = new nsScriptErrorWithStack(stackObj);
+  } else {
+    msg = new nsScriptError();
+  }
+
+  nsresult rv = msg->InitWithWindowID(aMessage, aSourceName, aSourceLine,
+                                      aLineNumber, aColNumber, aFlags,
+                                      aCategory, 0);
   if (NS_FAILED(rv))
     return IPC_OK();
 
   consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1053,16 +1053,36 @@ private:
   virtual mozilla::ipc::IPCResult RecvScriptError(const nsString& aMessage,
                                                   const nsString& aSourceName,
                                                   const nsString& aSourceLine,
                                                   const uint32_t& aLineNumber,
                                                   const uint32_t& aColNumber,
                                                   const uint32_t& aFlags,
                                                   const nsCString& aCategory) override;
 
+  virtual mozilla::ipc::IPCResult RecvScriptErrorWithStack(const nsString& aMessage,
+                                                           const nsString& aSourceName,
+                                                           const nsString& aSourceLine,
+                                                           const uint32_t& aLineNumber,
+                                                           const uint32_t& aColNumber,
+                                                           const uint32_t& aFlags,
+                                                           const nsCString& aCategory,
+                                                           const ClonedMessageData& aStack) override;
+
+private:
+  mozilla::ipc::IPCResult RecvScriptErrorInternal(const nsString& aMessage,
+                                                  const nsString& aSourceName,
+                                                  const nsString& aSourceLine,
+                                                  const uint32_t& aLineNumber,
+                                                  const uint32_t& aColNumber,
+                                                  const uint32_t& aFlags,
+                                                  const nsCString& aCategory,
+                                                  const ClonedMessageData* aStack = nullptr);
+
+public:
   virtual mozilla::ipc::IPCResult RecvPrivateDocShellsExist(const bool& aExist) override;
 
   virtual mozilla::ipc::IPCResult RecvFirstIdle() override;
 
   virtual mozilla::ipc::IPCResult RecvDeviceReset() override;
 
   virtual mozilla::ipc::IPCResult RecvKeywordToURI(const nsCString& aKeyword,
                                                    nsString* aProviderName,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -824,16 +824,19 @@ parent:
     async AddGeolocationListener(Principal principal, bool highAccuracy);
     async RemoveGeolocationListener();
     async SetGeolocationHigherAccuracy(bool enable);
 
     async ConsoleMessage(nsString message);
     async ScriptError(nsString message, nsString sourceName, nsString sourceLine,
                       uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
                       nsCString category);
+    async ScriptErrorWithStack(nsString message, nsString sourceName, nsString sourceLine,
+                               uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
+                               nsCString category, ClonedMessageData stack);
 
     // Places the items within dataTransfer on the clipboard.
     async SetClipboard(IPCDataTransfer aDataTransfer,
                        bool aIsPrivateData,
                        Principal aRequestingPrincipal,
                        int32_t aWhichClipboard);
 
     // Given a list of supported types, returns the clipboard data for the
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -119,16 +119,17 @@ if CONFIG['MOZ_CONTENT_SANDBOX'] and CON
         'mozsandbox',
     ]
 
 LOCAL_INCLUDES += [
     '/caps',
     '/chrome',
     '/docshell/base',
     '/dom/base',
+    '/dom/bindings',
     '/dom/events',
     '/dom/filesystem',
     '/dom/geolocation',
     '/dom/media/webspeech/synth/ipc',
     '/dom/security',
     '/extensions/cookie',
     '/extensions/spellcheck/src',
     '/gfx/2d',
--- a/dom/serviceworkers/IPCServiceWorkerRegistrationDescriptor.ipdlh
+++ b/dom/serviceworkers/IPCServiceWorkerRegistrationDescriptor.ipdlh
@@ -10,16 +10,18 @@ using ServiceWorkerUpdateViaCache from "
 namespace mozilla {
 namespace dom {
 
 // IPC type with enough information to create a ServiceWorker DOM object
 // in a child process.  Note that the state may be slightly out-of-sync
 // with the parent and should be updated dynamically if necessary.
 struct IPCServiceWorkerRegistrationDescriptor
 {
+  uint64_t id;
+
   // These values should match the principal and scope in each
   // associated worker.  It may be possible to optimize in the future,
   // but for now we duplicate the information here to ensure correctness.
   // Its possible we may need to reference a registration before the
   // worker is installed yet, etc.
   PrincipalInfo principalInfo;
   nsCString scope;
 
--- a/dom/serviceworkers/ServiceWorker.cpp
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -78,17 +78,19 @@ ServiceWorker::ServiceWorker(nsIGlobalOb
 
   // This will update our state too.
   mInner->AddServiceWorker(this);
 }
 
 ServiceWorker::~ServiceWorker()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mInner->RemoveServiceWorker(this);
+  if (mInner) {
+    mInner->RemoveServiceWorker(this);
+  }
 }
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorker)
   NS_INTERFACE_MAP_ENTRY(ServiceWorker)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
@@ -123,17 +125,17 @@ ServiceWorker::GetScriptURL(nsString& aU
   CopyUTF8toUTF16(mDescriptor.ScriptURL(), aURL);
 }
 
 void
 ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                            const Sequence<JSObject*>& aTransferable,
                            ErrorResult& aRv)
 {
-  if (State() == ServiceWorkerState::Redundant) {
+  if (State() == ServiceWorkerState::Redundant || !mInner) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   mInner->PostMessage(GetParentObject(), aCx, aMessage, aTransferable, aRv);
 }
 
 
@@ -141,13 +143,17 @@ const ServiceWorkerDescriptor&
 ServiceWorker::Descriptor() const
 {
   return mDescriptor;
 }
 
 void
 ServiceWorker::DisconnectFromOwner()
 {
+  if (mInner) {
+    mInner->RemoveServiceWorker(this);
+    mInner = nullptr;
+  }
   DOMEventTargetHelper::DisconnectFromOwner();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorker.h
+++ b/dom/serviceworkers/ServiceWorker.h
@@ -97,17 +97,22 @@ private:
   ServiceWorker(nsIGlobalObject* aWindow,
                 const ServiceWorkerDescriptor& aDescriptor,
                 Inner* aInner);
 
   // This class is reference-counted and will be destroyed from Release().
   ~ServiceWorker();
 
   ServiceWorkerDescriptor mDescriptor;
-  const RefPtr<Inner> mInner;
+
+  // Hold a strong reference to the inner service worker object.  This will
+  // create a ref-cycle.  The cycle is broken when either DisconnectFromOwner()
+  // is called due to the global tearing down or when the underlying service
+  // worker transitions to the redundant state.
+  RefPtr<Inner> mInner;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorker, NS_DOM_SERVICEWORKER_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_serviceworker_h__
--- a/dom/serviceworkers/ServiceWorkerInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerInfo.cpp
@@ -113,17 +113,17 @@ ServiceWorkerInfo::DetachDebugger()
   return mServiceWorkerPrivate->DetachDebugger();
 }
 
 namespace {
 
 class ChangeStateUpdater final : public Runnable
 {
 public:
-  ChangeStateUpdater(const nsTArray<ServiceWorker*>& aInstances,
+  ChangeStateUpdater(const nsTArray<RefPtr<ServiceWorker>>& aInstances,
                      ServiceWorkerState aState)
     : Runnable("dom::ChangeStateUpdater")
     , mState(aState)
   {
     for (size_t i = 0; i < aInstances.Length(); ++i) {
       mInstances.AppendElement(aInstances[i]);
     }
   }
@@ -171,16 +171,21 @@ ServiceWorkerInfo::UpdateState(ServiceWo
   if (State() != aState) {
     mServiceWorkerPrivate->UpdateState(aState);
   }
   mDescriptor.SetState(aState);
   nsCOMPtr<nsIRunnable> r = new ChangeStateUpdater(mInstances, State());
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r.forget()));
   if (State() == ServiceWorkerState::Redundant) {
     serviceWorkerScriptCache::PurgeCache(mPrincipal, mCacheName);
+
+    // Break the ref-cycle with the binding objects.  We won't need to
+    // fire any more events and they should be able to GC once content
+    // script no longer references them.
+    mInstances.Clear();
   }
 }
 
 ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
                                      const nsACString& aScope,
                                      const nsACString& aScriptSpec,
                                      const nsAString& aCacheName,
                                      nsLoadFlags aImportsLoadFlags)
@@ -245,18 +250,21 @@ ServiceWorkerInfo::RemoveServiceWorker(S
 {
   MOZ_DIAGNOSTIC_ASSERT(aWorker);
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   nsAutoString workerURL;
   aWorker->GetScriptURL(workerURL);
   MOZ_DIAGNOSTIC_ASSERT(
     workerURL.Equals(NS_ConvertUTF8toUTF16(mDescriptor.ScriptURL())));
 #endif
-  MOZ_ASSERT(mInstances.Contains(aWorker));
 
+  // If the binding layer initiates this call by disconnecting the global,
+  // then we will find an entry in mInstances here.  If the worker transitions
+  // to redundant and we clear mInstances, then we will not find an entry
+  // here.
   mInstances.RemoveElement(aWorker);
 }
 
 void
 ServiceWorkerInfo::PostMessage(nsIGlobalObject* aGlobal,
                                JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const Sequence<JSObject*>& aTransferable,
                                ErrorResult& aRv)
--- a/dom/serviceworkers/ServiceWorkerInfo.h
+++ b/dom/serviceworkers/ServiceWorkerInfo.h
@@ -49,21 +49,26 @@ private:
   TimeStamp mCreationTimeStamp;
 
   // The time of states are 0, if SW has not reached that state yet. Besides, we
   // update each of them after UpdateState() is called in SWRegistrationInfo.
   PRTime mInstalledTime;
   PRTime mActivatedTime;
   PRTime mRedundantTime;
 
-  // We hold rawptrs since the ServiceWorker constructor and destructor ensure
-  // addition and removal.
+  // Track the list of known binding objects so we can fire events on them
+  // when appropriate.  These are held using strong references so that they
+  // are not GC'd while an event handler is registered and could observe an
+  // event.  This reference will create a cycle with the binding object.  The
+  // cycle is broken when either the global is detached or the service worker
+  // transitions to the redundant state.
+  //
   // There is a high chance of there being at least one ServiceWorker
   // associated with this all the time.
-  AutoTArray<ServiceWorker*, 1> mInstances;
+  AutoTArray<RefPtr<ServiceWorker>, 1> mInstances;
 
   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
   bool mSkipWaitingFlag;
 
   enum {
     Unknown,
     Enabled,
     Disabled
--- a/dom/serviceworkers/ServiceWorkerManager.cpp
+++ b/dom/serviceworkers/ServiceWorkerManager.cpp
@@ -462,17 +462,17 @@ class ServiceWorkerResolveWindowPromiseO
     }
 
     MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
     RefPtr<ServiceWorkerRegisterJob> registerJob =
       static_cast<ServiceWorkerRegisterJob*>(aJob);
     RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
 
     RefPtr<ServiceWorkerRegistration> swr =
-      window->GetServiceWorkerRegistration(reg->Descriptor());
+      window->AsGlobal()->GetOrCreateServiceWorkerRegistration(reg->Descriptor());
 
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
       "ServiceWorkerResolveWindowPromiseOnRegisterCallback::JobFinished",
       [promise = Move(promise), swr = Move(swr)] () {
         promise->MaybeResolve(swr);
       });
     MOZ_ALWAYS_SUCCEEDS(
       window->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget()));
@@ -1026,17 +1026,17 @@ public:
 
       rv = principal->CheckMayLoad(scopeURI, true /* report */,
                                    false /* allowIfInheritsPrincipal */);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         continue;
       }
 
       RefPtr<ServiceWorkerRegistration> swr =
-        mWindow->GetServiceWorkerRegistration(info->Descriptor());
+        mWindow->AsGlobal()->GetOrCreateServiceWorkerRegistration(info->Descriptor());
 
       array.AppendElement(swr);
     }
 
     mPromise->MaybeResolve(array);
     return NS_OK;
   }
 };
@@ -1149,17 +1149,17 @@ public:
       swm->GetServiceWorkerRegistrationInfo(principal, uri);
 
     if (!registration) {
       mPromise->MaybeResolveWithUndefined();
       return NS_OK;
     }
 
     RefPtr<ServiceWorkerRegistration> swr =
-      mWindow->GetServiceWorkerRegistration(registration->Descriptor());
+      mWindow->AsGlobal()->GetOrCreateServiceWorkerRegistration(registration->Descriptor());
     mPromise->MaybeResolve(swr);
 
     return NS_OK;
   }
 };
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
@@ -1463,17 +1463,17 @@ ServiceWorkerManager::CheckReadyPromise(
   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
   MOZ_ASSERT(principal);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(principal, aURI);
 
   if (registration && registration->GetActive()) {
     RefPtr<ServiceWorkerRegistration> swr =
-      aWindow->GetServiceWorkerRegistration(registration->Descriptor());
+      aWindow->AsGlobal()->GetOrCreateServiceWorkerRegistration(registration->Descriptor());
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
 ServiceWorkerInfo*
--- a/dom/serviceworkers/ServiceWorkerRegistration.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistration.cpp
@@ -25,16 +25,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Servi
                                    mWaitingWorker,
                                    mActiveWorker,
                                    mPushManager);
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistration)
+  NS_INTERFACE_MAP_ENTRY(ServiceWorkerRegistration)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 ServiceWorkerRegistration::ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
                                                      const ServiceWorkerRegistrationDescriptor& aDescriptor,
                                                      ServiceWorkerRegistration::Inner* aInner)
   : DOMEventTargetHelper(aGlobal)
   , mDescriptor(aDescriptor)
   , mInner(aInner)
@@ -70,27 +71,28 @@ ServiceWorkerRegistration::CreateForMain
   RefPtr<ServiceWorkerRegistration> registration =
     new ServiceWorkerRegistration(aWindow->AsGlobal(), aDescriptor, inner);
 
   return registration.forget();
 }
 
 /* static */ already_AddRefed<ServiceWorkerRegistration>
 ServiceWorkerRegistration::CreateForWorker(WorkerPrivate* aWorkerPrivate,
+                                           nsIGlobalObject* aGlobal,
                                            const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
-  MOZ_ASSERT(aWorkerPrivate);
+  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
+  MOZ_DIAGNOSTIC_ASSERT(aGlobal);
   aWorkerPrivate->AssertIsOnWorkerThread();
 
   RefPtr<Inner> inner =
     new ServiceWorkerRegistrationWorkerThread(aWorkerPrivate, aDescriptor);
 
   RefPtr<ServiceWorkerRegistration> registration =
-    new ServiceWorkerRegistration(aWorkerPrivate->GlobalScope(), aDescriptor,
-                                  inner);
+    new ServiceWorkerRegistration(aGlobal, aDescriptor, inner);
 
   return registration.forget();
 }
 
 void
 ServiceWorkerRegistration::DisconnectFromOwner()
 {
   mInner->ClearServiceWorkerRegistration(this);
@@ -122,17 +124,22 @@ ServiceWorkerRegistration::GetActive() c
 void
 ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   MOZ_DIAGNOSTIC_ASSERT(MatchesDescriptor(aDescriptor));
 
   mDescriptor = aDescriptor;
 
   nsCOMPtr<nsIGlobalObject> global = GetParentObject();
-  if (!global) {
+
+  // Clear all workers if the registration has been detached from the global.
+  // Also, we cannot expose ServiceWorker objects on worker threads yet, so
+  // do the same on when off-main-thread.  This main thread check should be
+  // removed as part of bug 1113522.
+  if (!global || !NS_IsMainThread()) {
     mInstallingWorker = nullptr;
     mWaitingWorker = nullptr;
     mActiveWorker = nullptr;
     return;
   }
 
   Maybe<ServiceWorkerDescriptor> active = aDescriptor.GetActive();
   if (active.isSome()) {
@@ -154,17 +161,18 @@ ServiceWorkerRegistration::UpdateState(c
   } else {
     mInstallingWorker = nullptr;
   }
 }
 
 bool
 ServiceWorkerRegistration::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) const
 {
-  return aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
+  return aDescriptor.Id() == mDescriptor.Id() &&
+         aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
          aDescriptor.Scope() == mDescriptor.Scope();
 }
 
 void
 ServiceWorkerRegistration::GetScope(nsAString& aScope) const
 {
   CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
 }
--- a/dom/serviceworkers/ServiceWorkerRegistration.h
+++ b/dom/serviceworkers/ServiceWorkerRegistration.h
@@ -21,16 +21,19 @@ class nsIGlobalObject;
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class PushManager;
 class WorkerPrivate;
 class ServiceWorker;
 
+#define NS_DOM_SERVICEWORKERREGISTRATION_IID \
+  {0x4578a90e, 0xa427, 0x4237, {0x98, 0x4a, 0xbd, 0x98, 0xe4, 0xcd, 0x5f, 0x3a}}
+
 class ServiceWorkerRegistration final : public DOMEventTargetHelper
 {
 public:
   class Inner
   {
   public:
     NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
@@ -55,27 +58,29 @@ public:
     virtual already_AddRefed<Promise>
     GetNotifications(const GetNotificationOptions& aOptions,
                      ErrorResult& aRv) = 0;
 
     virtual already_AddRefed<PushManager>
     GetPushManager(JSContext* aCx, ErrorResult& aRv) = 0;
   };
 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_SERVICEWORKERREGISTRATION_IID)
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(updatefound)
 
   static already_AddRefed<ServiceWorkerRegistration>
   CreateForMainThread(nsPIDOMWindowInner* aWindow,
                       const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
   static already_AddRefed<ServiceWorkerRegistration>
   CreateForWorker(WorkerPrivate* aWorkerPrivate,
+                  nsIGlobalObject* aGlobal,
                   const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void DisconnectFromOwner() override;
 
   already_AddRefed<ServiceWorker>
@@ -121,20 +126,26 @@ public:
 private:
   ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
                             const ServiceWorkerRegistrationDescriptor& aDescriptor,
                             Inner* aInner);
 
   ~ServiceWorkerRegistration();
 
   ServiceWorkerRegistrationDescriptor mDescriptor;
+
+  // This forms a ref-cycle with the inner implementation object.  Its broken
+  // when either the global is torn down or the registration is removed from
+  // the ServiceWorkerManager.
   RefPtr<Inner> mInner;
+
   RefPtr<ServiceWorker> mInstallingWorker;
   RefPtr<ServiceWorker> mWaitingWorker;
   RefPtr<ServiceWorker> mActiveWorker;
   RefPtr<PushManager> mPushManager;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerRegistration, NS_DOM_SERVICEWORKERREGISTRATION_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_ServiceWorkerRegistration_h */
--- a/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.cpp
@@ -23,36 +23,40 @@ ServiceWorkerRegistrationDescriptor::New
     result.emplace(mData->waiting().get_IPCServiceWorkerDescriptor());
   } else if (mData->active().type() != OptionalIPCServiceWorkerDescriptor::Tvoid_t) {
     result.emplace(mData->active().get_IPCServiceWorkerDescriptor());
   }
   return Move(result);
 }
 
 ServiceWorkerRegistrationDescriptor::ServiceWorkerRegistrationDescriptor(
+                                    uint64_t aId,
                                     nsIPrincipal* aPrincipal,
                                     const nsACString& aScope,
                                     ServiceWorkerUpdateViaCache aUpdateViaCache)
   : mData(MakeUnique<IPCServiceWorkerRegistrationDescriptor>())
 {
   MOZ_ALWAYS_SUCCEEDS(
     PrincipalToPrincipalInfo(aPrincipal, &mData->principalInfo()));
 
+  mData->id() = aId;
   mData->scope() = aScope;
   mData->updateViaCache() = aUpdateViaCache;
   mData->installing() = void_t();
   mData->waiting() = void_t();
   mData->active() = void_t();
 }
 
 ServiceWorkerRegistrationDescriptor::ServiceWorkerRegistrationDescriptor(
+                                    uint64_t aId,
                                     const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                                     const nsACString& aScope,
                                     ServiceWorkerUpdateViaCache aUpdateViaCache)
-  : mData(MakeUnique<IPCServiceWorkerRegistrationDescriptor>(aPrincipalInfo,
+  : mData(MakeUnique<IPCServiceWorkerRegistrationDescriptor>(aId,
+                                                             aPrincipalInfo,
                                                              nsCString(aScope),
                                                              aUpdateViaCache,
                                                              void_t(),
                                                              void_t(),
                                                              void_t()))
 {
 }
 
@@ -103,16 +107,22 @@ ServiceWorkerRegistrationDescriptor::~Se
 }
 
 bool
 ServiceWorkerRegistrationDescriptor::operator==(const ServiceWorkerRegistrationDescriptor& aRight) const
 {
   return *mData == *aRight.mData;
 }
 
+uint64_t
+ServiceWorkerRegistrationDescriptor::Id() const
+{
+  return mData->id();
+}
+
 ServiceWorkerUpdateViaCache
 ServiceWorkerRegistrationDescriptor::UpdateViaCache() const
 {
   return mData->updateViaCache();
 }
 
 const mozilla::ipc::PrincipalInfo&
 ServiceWorkerRegistrationDescriptor::PrincipalInfo() const
--- a/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationDescriptor.h
@@ -32,21 +32,23 @@ class ServiceWorkerRegistrationDescripto
   // need the wrapper class since IPDL generated code includes windows.h
   // which is in turn incompatible with bindings code.
   UniquePtr<IPCServiceWorkerRegistrationDescriptor> mData;
 
   Maybe<IPCServiceWorkerDescriptor>
   NewestInternal() const;
 
 public:
-  ServiceWorkerRegistrationDescriptor(nsIPrincipal* aPrincipal,
+  ServiceWorkerRegistrationDescriptor(uint64_t aId,
+                                      nsIPrincipal* aPrincipal,
                                       const nsACString& aScope,
                                       ServiceWorkerUpdateViaCache aUpdateViaCache);
 
-  ServiceWorkerRegistrationDescriptor(const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+  ServiceWorkerRegistrationDescriptor(uint64_t aId,
+                                      const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                                       const nsACString& aScope,
                                       ServiceWorkerUpdateViaCache aUpdateViaCache);
 
   explicit ServiceWorkerRegistrationDescriptor(const IPCServiceWorkerRegistrationDescriptor& aDescriptor);
 
   ServiceWorkerRegistrationDescriptor(const ServiceWorkerRegistrationDescriptor& aRight);
 
   ServiceWorkerRegistrationDescriptor&
@@ -57,16 +59,19 @@ public:
   ServiceWorkerRegistrationDescriptor&
   operator=(ServiceWorkerRegistrationDescriptor&& aRight);
 
   ~ServiceWorkerRegistrationDescriptor();
 
   bool
   operator==(const ServiceWorkerRegistrationDescriptor& aRight) const;
 
+  uint64_t
+  Id() const;
+
   ServiceWorkerUpdateViaCache
   UpdateViaCache() const;
 
   const mozilla::ipc::PrincipalInfo&
   PrincipalInfo() const;
 
   const nsCString&
   Scope() const;
--- a/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
@@ -79,36 +79,51 @@ ServiceWorkerRegistrationMainThread::Sto
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (swm) {
     swm->RemoveRegistrationEventListener(mScope, this);
   }
   mListeningForEvents = false;
 }
 
 void
+ServiceWorkerRegistrationMainThread::RegistrationRemovedInternal()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  StopListeningForEvents();
+
+  // Since the registration is effectively dead in the SWM we can break
+  // the ref-cycle and let the binding object clean up.
+  mOuter = nullptr;
+}
+
+void
 ServiceWorkerRegistrationMainThread::UpdateFound()
 {
   mOuter->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
 }
 
 void
 ServiceWorkerRegistrationMainThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   mOuter->UpdateState(aDescriptor);
 }
 
 void
 ServiceWorkerRegistrationMainThread::RegistrationRemoved()
 {
-  // If the registration is being removed completely, remove it from the
-  // window registration hash table so that a new registration would get a new
-  // wrapper JS object.
-  if (nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner()) {
-    window->InvalidateServiceWorkerRegistration(mScope);
-  }
+  // Queue a runnable to clean up the registration.  This is necessary
+  // because there may be runnables in the event queue already to
+  // update the registration state.  We want to let those run
+  // if possible before clearing our mOuter reference.
+  nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
+    "ServiceWorkerRegistrationMainThread::RegistrationRemoved",
+    this,
+    &ServiceWorkerRegistrationMainThread::RegistrationRemovedInternal);
+  MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
 }
 
 bool
 ServiceWorkerRegistrationMainThread::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   return mOuter->MatchesDescriptor(aDescriptor);
 }
 
@@ -119,18 +134,17 @@ ServiceWorkerRegistrationMainThread::Set
   MOZ_DIAGNOSTIC_ASSERT(!mOuter);
   mOuter = aReg;
   StartListeningForEvents();
 }
 
 void
 ServiceWorkerRegistrationMainThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
 {
-  MOZ_DIAGNOSTIC_ASSERT(mOuter);
-  MOZ_DIAGNOSTIC_ASSERT(mOuter == aReg);
+  MOZ_ASSERT_IF(mOuter, mOuter == aReg);
   StopListeningForEvents();
   mOuter = nullptr;
 }
 
 namespace {
 
 void
 UpdateInternal(nsIPrincipal* aPrincipal,
@@ -598,16 +612,21 @@ public:
   }
 };
 } // namespace
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  if (!mOuter) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsIGlobalObject> go = mOuter->GetParentObject();
   if (!go) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<Promise> promise = Promise::Create(go, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
@@ -623,16 +642,21 @@ ServiceWorkerRegistrationMainThread::Upd
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  if (!mOuter) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsIGlobalObject> go = mOuter->GetParentObject();
   if (!go) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   // Although the spec says that the same-origin checks should also be done
   // asynchronously, we do them in sync because the Promise created by the
@@ -690,16 +714,21 @@ ServiceWorkerRegistrationMainThread::Unr
 // Notification API extension.
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
                                                       const nsAString& aTitle,
                                                       const NotificationOptions& aOptions,
                                                       ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  if (!mOuter) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner();
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
@@ -722,30 +751,39 @@ ServiceWorkerRegistrationMainThread::Sho
 
   return p.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  if (!mOuter) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
   nsCOMPtr<nsPIDOMWindowInner> window = mOuter->GetOwner();
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
   return Notification::Get(window, aOptions, mScope, aRv);
 }
 
 already_AddRefed<PushManager>
 ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx,
                                                     ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (!mOuter) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsIGlobalObject> globalObject = mOuter->GetParentObject();
 
   if (!globalObject) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   GlobalObject global(aCx, globalObject->GetGlobalJSObject());
@@ -800,17 +838,19 @@ public:
     }
   }
 
   void
   StopListeningForEvents()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    MOZ_ASSERT(mListeningForEvents);
+    if (!mListeningForEvents) {
+      return;
+    }
 
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
     // We aren't going to need this anymore and we shouldn't hold on since the
     // worker will go away soon.
     mWorkerPrivate = nullptr;
 
     if (swm) {
@@ -827,20 +867,17 @@ public:
   void
   UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     // TODO: Not implemented
   }
 
   void
-  RegistrationRemoved() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
+  RegistrationRemoved() override;
 
   void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
   bool
@@ -886,29 +923,34 @@ ServiceWorkerRegistrationWorkerThread::S
 
 ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread()
 {
   MOZ_DIAGNOSTIC_ASSERT(!mListener);
   MOZ_DIAGNOSTIC_ASSERT(!mOuter);
 }
 
 void
+ServiceWorkerRegistrationWorkerThread::RegistrationRemoved()
+{
+  mOuter = nullptr;
+}
+
+void
 ServiceWorkerRegistrationWorkerThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
 {
   MOZ_DIAGNOSTIC_ASSERT(aReg);
   MOZ_DIAGNOSTIC_ASSERT(!mOuter);
   mOuter = aReg;
   InitListener();
 }
 
 void
 ServiceWorkerRegistrationWorkerThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
 {
-  MOZ_DIAGNOSTIC_ASSERT(mOuter);
-  MOZ_DIAGNOSTIC_ASSERT(mOuter == aReg);
+  MOZ_ASSERT_IF(mOuter, mOuter == aReg);
   ReleaseListener();
   mOuter = nullptr;
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
@@ -1020,16 +1062,24 @@ ServiceWorkerRegistrationWorkerThread::R
   mListener = nullptr;
   mWorkerPrivate = nullptr;
 }
 
 bool
 ServiceWorkerRegistrationWorkerThread::Notify(WorkerStatus aStatus)
 {
   ReleaseListener();
+
+  // Break the ref-cycle immediately when the worker thread starts to
+  // teardown.  We must make sure its GC'd before the worker RuntimeService
+  // is destroyed.  The WorkerListener may not be able to post a runnable
+  // clearing this value after shutdown begins and thus delaying cleanup
+  // too late.
+  mOuter = nullptr;
+
   return true;
 }
 
 class FireUpdateFoundRunnable final : public WorkerRunnable
 {
   RefPtr<WorkerListener> mListener;
 public:
   FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate,
@@ -1063,16 +1113,60 @@ WorkerListener::UpdateFound()
   MOZ_ASSERT(NS_IsMainThread());
   if (mWorkerPrivate) {
     RefPtr<FireUpdateFoundRunnable> r =
       new FireUpdateFoundRunnable(mWorkerPrivate, this);
     Unused << NS_WARN_IF(!r->Dispatch());
   }
 }
 
+class RegistrationRemovedWorkerRunnable final : public WorkerRunnable
+{
+  RefPtr<WorkerListener> mListener;
+public:
+  RegistrationRemovedWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+                                    WorkerListener* aListener)
+    : WorkerRunnable(aWorkerPrivate)
+    , mListener(aListener)
+  {
+    // Need this assertion for now since runnables which modify busy count can
+    // only be dispatched from parent thread to worker thread and we don't deal
+    // with nested workers. SW threads can't be nested.
+    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration();
+    if (reg) {
+      reg->RegistrationRemoved();
+    }
+    return true;
+  }
+};
+
+void
+WorkerListener::RegistrationRemoved()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mWorkerPrivate) {
+    return;
+  }
+
+  RefPtr<WorkerRunnable> r =
+    new RegistrationRemovedWorkerRunnable(mWorkerPrivate, this);
+  Unused << r->Dispatch();
+
+  StopListeningForEvents();
+}
+
 // Notification API extension.
 already_AddRefed<Promise>
 ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
                                                         const nsAString& aTitle,
                                                         const NotificationOptions& aOptions,
                                                         ErrorResult& aRv)
 {
   if (!mWorkerPrivate) {
--- a/dom/serviceworkers/ServiceWorkerRegistrationImpl.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.h
@@ -80,17 +80,20 @@ private:
   ~ServiceWorkerRegistrationMainThread();
 
   void
   StartListeningForEvents();
 
   void
   StopListeningForEvents();
 
-  ServiceWorkerRegistration* mOuter;
+  void
+  RegistrationRemovedInternal();
+
+  RefPtr<ServiceWorkerRegistration> mOuter;
   const nsString mScope;
   bool mListeningForEvents;
 };
 
 ////////////////////////////////////////////////////
 // Worker Thread implementation
 
 class WorkerListener;
@@ -99,16 +102,19 @@ class ServiceWorkerRegistrationWorkerThr
                                                   , public WorkerHolder
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationWorkerThread, override)
 
   ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
                                         const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
+  void
+  RegistrationRemoved();
+
   // ServiceWorkerRegistration::Inner
   void
   SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override;
 
   void
   ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg) override;
 
   already_AddRefed<Promise>
@@ -141,16 +147,21 @@ private:
   ~ServiceWorkerRegistrationWorkerThread();
 
   void
   InitListener();
 
   void
   ReleaseListener();
 
-  ServiceWorkerRegistration* mOuter;
+  // Store a strong reference to the outer binding object.  This will create
+  // a ref-cycle.  We must hold it alive in case any events need to be fired
+  // on it.  The cycle is broken when the global becomes detached or the
+  // registration is removed in the ServiceWorkerManager.
+  RefPtr<ServiceWorkerRegistration> mOuter;
+
   WorkerPrivate* mWorkerPrivate;
   const nsString mScope;
   RefPtr<WorkerListener> mListener;
 };
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
@@ -80,17 +80,17 @@ ServiceWorkerRegistrationInfo::Clear()
   NotifyChromeRegistrationListeners();
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
     const nsACString& aScope,
     nsIPrincipal* aPrincipal,
     ServiceWorkerUpdateViaCache aUpdateViaCache)
   : mPrincipal(aPrincipal)
-  , mDescriptor(aPrincipal, aScope, aUpdateViaCache)
+  , mDescriptor(GetNextId(), aPrincipal, aScope, aUpdateViaCache)
   , mControlledClientsCounter(0)
   , mDelayMultiplier(0)
   , mUpdateState(NoUpdate)
   , mCreationTime(PR_Now())
   , mCreationTimeStamp(TimeStamp::Now())
   , mLastUpdateTime(0)
   , mPendingUninstall(false)
 {}
@@ -725,10 +725,19 @@ ServiceWorkerRegistrationInfo::GetUpdate
 
   if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) {
     mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30;
   }
 
   return delay;
 }
 
+// static
+uint64_t
+ServiceWorkerRegistrationInfo::GetNextId()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  static uint64_t sNextId = 0;
+  return ++sNextId;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
@@ -224,14 +224,17 @@ private:
   UpdateRegistrationState();
 
   // Used by devtools to track changes to the properties of *nsIServiceWorkerRegistrationInfo*.
   // Note, this doesn't necessarily need to be in sync with the DOM registration objects, but
   // it does need to be called in the same task that changed |mInstallingWorker|,
   // |mWaitingWorker| or |mActiveWorker|.
   void
   NotifyChromeRegistrationListeners();
+
+  static uint64_t
+  GetNextId();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_serviceworkerregistrationinfo_h
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -554,16 +554,39 @@ WorkerGlobalScope::GetClientState() cons
 }
 
 Maybe<ServiceWorkerDescriptor>
 WorkerGlobalScope::GetController() const
 {
   return mWorkerPrivate->GetController();
 }
 
+RefPtr<ServiceWorkerRegistration>
+WorkerGlobalScope::GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  RefPtr<ServiceWorkerRegistration> ref;
+  ForEachEventTargetObject([&] (DOMEventTargetHelper* aTarget, bool* aDoneOut) {
+    RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
+    if (!swr || !swr->MatchesDescriptor(aDescriptor)) {
+      return;
+    }
+
+    ref = swr.forget();
+    *aDoneOut = true;
+  });
+
+  if (!ref) {
+    ref = ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, this,
+                                                     aDescriptor);
+  }
+
+  return ref.forget();
+}
+
 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
                                                        const nsString& aName)
   : WorkerGlobalScope(aWorkerPrivate)
   , mName(aName)
 {
 }
 
 bool
@@ -657,18 +680,17 @@ NS_IMPL_RELEASE_INHERITED(ServiceWorkerG
 ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
                                                    const ServiceWorkerRegistrationDescriptor& aRegistrationDescriptor)
   : WorkerGlobalScope(aWorkerPrivate)
   , mScope(NS_ConvertUTF8toUTF16(aRegistrationDescriptor.Scope()))
 
   // Eagerly create the registration because we will need to receive updates
   // about the state of the registration.  We can't wait until first access
   // to start receiving these.
-  , mRegistration(ServiceWorkerRegistration::CreateForWorker(aWorkerPrivate,
-                                                             aRegistrationDescriptor))
+  , mRegistration(GetOrCreateServiceWorkerRegistration(aRegistrationDescriptor))
 {
 }
 
 ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope()
 {
 }
 
 bool
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -29,17 +29,16 @@ class ClientState;
 class Console;
 class Crypto;
 class Function;
 class IDBFactory;
 enum class ImageBitmapFormat : uint8_t;
 class Performance;
 class Promise;
 class RequestOrUSVString;
-class ServiceWorkerRegistration;
 class WorkerLocation;
 class WorkerNavigator;
 class WorkerPrivate;
 enum class CallerType : uint32_t;
 
 namespace cache {
 
 class CacheStorage;
@@ -230,16 +229,19 @@ public:
   Maybe<ClientInfo>
   GetClientInfo() const override;
 
   Maybe<ClientState>
   GetClientState() const;
 
   Maybe<ServiceWorkerDescriptor>
   GetController() const override;
+
+  RefPtr<mozilla::dom::ServiceWorkerRegistration>
+  GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistrationDescriptor& aDescriptor) override;
 };
 
 class DedicatedWorkerGlobalScope final : public WorkerGlobalScope
 {
   const nsString mName;
 
   ~DedicatedWorkerGlobalScope() { }
 
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -11,18 +11,19 @@ use std::char;
 use std::ffi;
 use std::ptr;
 use std::slice;
 use std::mem;
 use std::u32;
 use std::default::Default;
 use std::marker;
 use std::ops::{Deref, DerefMut};
-use std::sync::{Once, ONCE_INIT};
+use std::sync::{Once, ONCE_INIT, Arc, Mutex};
 use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
+use std::sync::mpsc::{SyncSender, sync_channel};
 use std::thread;
 use jsapi::root::*;
 use jsval::{self, UndefinedValue};
 use glue::{CreateAutoObjectVector, CreateCallArgsFromVp, AppendToAutoObjectVector, DeleteAutoObjectVector, IsDebugBuild};
 use glue::{CreateAutoIdVector, SliceAutoIdVector, DestroyAutoIdVector};
 use glue::{NewCompileOptions, DeleteCompileOptions};
 use panic;
 
@@ -60,16 +61,17 @@ impl ToResult for bool {
 // ___________________________________________________________________________
 // friendly Rustic API to runtimes
 
 thread_local!(static CONTEXT: Cell<*mut JSContext> = Cell::new(ptr::null_mut()));
 
 lazy_static! {
     static ref OUTSTANDING_RUNTIMES: AtomicUsize = AtomicUsize::new(0);
     static ref SHUT_DOWN: AtomicBool = AtomicBool::new(false);
+    static ref SHUT_DOWN_SIGNAL: Arc<Mutex<Option<SyncSender<()>>>> = Arc::new(Mutex::new(None));
 }
 
 /// A wrapper for the `JSContext` structure in SpiderMonkey.
 pub struct Runtime {
     cx: *mut JSContext,
 }
 
 impl Runtime {
@@ -96,17 +98,16 @@ impl Runtime {
 
         unsafe {
             #[derive(Debug)]
             struct Parent(UnsafeCell<*mut JSContext>);
             unsafe impl ::std::marker::Sync for Parent {}
 
             impl Parent {
                 fn set(&self, val: *mut JSContext) {
-                    assert!(self.get().is_null());
                     self.as_atomic().store(val, Ordering::SeqCst);
                     assert_eq!(self.get(), val);
                 }
 
                 fn get(&self) -> *mut JSContext {
                     self.as_atomic().load(Ordering::SeqCst)
                 }
 
@@ -120,33 +121,40 @@ impl Runtime {
             lazy_static! {
                 static ref PARENT: Parent = Parent(UnsafeCell::new(0 as *mut _));
             }
             static ONCE: Once = ONCE_INIT;
 
             ONCE.call_once(|| {
                 // There is a 1:1 relationship between threads and JSContexts,
                 // so we must spawn a new thread for the parent context.
+                let (tx, rx) = sync_channel(0);
+                *SHUT_DOWN_SIGNAL.lock().unwrap() = Some(tx);
                 let _ = thread::spawn(move || {
                     let is_debug_mozjs = cfg!(feature = "debugmozjs");
                     let diagnostic = JS::detail::InitWithFailureDiagnostic(is_debug_mozjs);
                     if !diagnostic.is_null() {
                         panic!("JS::detail::InitWithFailureDiagnostic failed: {}",
                                ffi::CStr::from_ptr(diagnostic).to_string_lossy());
                     }
 
                     let context = JS_NewContext(
                         DEFAULT_HEAPSIZE, ChunkSize as u32, ptr::null_mut());
                     assert!(!context.is_null());
                     JS::InitSelfHostedCode(context);
                     PARENT.set(context);
 
-                    loop {
-                        thread::sleep(::std::time::Duration::new(::std::u64::MAX, 0));
-                    }
+                    // The last JSRuntime child died, resume the execution by destroying the parent.
+                    rx.recv().unwrap();
+                    let cx = PARENT.get();
+                    JS_DestroyContext(cx);
+                    JS_ShutDown();
+                    PARENT.set(0 as *mut _);
+                    // Unblock the last child thread, waiting for the JS_ShutDown.
+                    rx.recv().unwrap();
                 });
 
                 while PARENT.get().is_null() {
                     thread::yield_now();
                 }
             });
 
             assert_eq!(IsDebugBuild(), cfg!(feature = "debugmozjs"));
@@ -248,17 +256,23 @@ impl Drop for Runtime {
 
             CONTEXT.with(|context| {
                 assert_eq!(context.get(), self.cx);
                 context.set(ptr::null_mut());
             });
 
             if OUTSTANDING_RUNTIMES.fetch_sub(1, Ordering::SeqCst) == 1 {
                 SHUT_DOWN.store(true, Ordering::SeqCst);
-                JS_ShutDown();
+                let signal = &SHUT_DOWN_SIGNAL.lock().unwrap();
+                let signal = signal.as_ref().unwrap();
+                // Send a signal to shutdown the Parent runtime.
+                signal.send(()).unwrap();
+                // Wait for it to be destroyed before resuming the execution
+                // of static variable destructors.
+                signal.send(()).unwrap();
             }
         }
     }
 }
 
 // ___________________________________________________________________________
 // Rooting API for standard JS things
 
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -7,26 +7,26 @@
 #include "builtin/JSON.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Range.h"
 #include "mozilla/ScopeExit.h"
 
 #include "jsarray.h"
 #include "jsnum.h"
-#include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
+#include "builtin/String.h"
+#include "util/StringBuffer.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSONParser.h"
-#include "vm/StringBuffer.h"
 
 #include "jsarrayinlines.h"
 #include "jsboolinlines.h"
 
 #include "vm/JSAtom-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -10,17 +10,17 @@
 #include "gc/FreeOp.h"
 #include "js/Utility.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
 #include "gc/Marking-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsNaN;
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -3,27 +3,26 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/Object.h"
 
 #include "mozilla/MaybeOneOf.h"
 
-#include "jsstr.h"
-
 #include "builtin/Eval.h"
 #include "builtin/SelfHostingDefines.h"
+#include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "jit/InlinableNatives.h"
 #include "js/UniquePtr.h"
+#include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpObject.h"
-#include "vm/StringBuffer.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 #ifdef FUZZING
 #include "builtin/TestingFunctions.h"
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -7,21 +7,21 @@
 #include "builtin/RegExp.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
+#include "util/StringBuffer.h"
+#include "util/Unicode.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
-#include "vm/StringBuffer.h"
-#include "vm/Unicode.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::unicode;
 
rename from js/src/jsstr.cpp
rename to js/src/builtin/String.cpp
--- a/js/src/jsstr.cpp
+++ b/js/src/builtin/String.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Range.h"
 #include "mozilla/TypeTraits.h"
@@ -31,47 +31,45 @@
 #include "builtin/RegExp.h"
 #include "jit/InlinableNatives.h"
 #include "js/Conversions.h"
 #include "js/UniquePtr.h"
 #if ENABLE_INTL_API
 # include "unicode/uchar.h"
 # include "unicode/unorm2.h"
 #endif
+#include "util/StringBuffer.h"
+#include "util/Unicode.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Opcodes.h"
 #include "vm/Printer.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
-#include "vm/StringBuffer.h"
-#include "vm/Unicode.h"
 
 #include "vm/Interpreter-inl.h"
-#include "vm/String-inl.h"
 #include "vm/StringObject-inl.h"
+#include "vm/StringType-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using JS::Symbol;
 using JS::SymbolCode;
 
 using mozilla::CheckedInt;
 using mozilla::IsNaN;
-using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
-using mozilla::PodEqual;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
 
 static JSLinearString*
 ArgToLinearString(JSContext* cx, const CallArgs& args, unsigned argno)
 {
     if (argno >= args.length())
@@ -2411,39 +2409,16 @@ js::str_lastIndexOf(JSContext* cx, unsig
         else
             res = LastIndexOfImpl(textChars, len, searchStr->twoByteChars(nogc), searchLen, start);
     }
 
     args.rval().setInt32(res);
     return true;
 }
 
-bool
-js::HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start)
-{
-    MOZ_ASSERT(start + pat->length() <= text->length());
-
-    size_t patLen = pat->length();
-
-    AutoCheckCannotGC nogc;
-    if (text->hasLatin1Chars()) {
-        const Latin1Char* textChars = text->latin1Chars(nogc) + start;
-        if (pat->hasLatin1Chars())
-            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
-
-        return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
-    }
-
-    const char16_t* textChars = text->twoByteChars(nogc) + start;
-    if (pat->hasTwoByteChars())
-        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
-
-    return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
-}
-
 // ES2018 draft rev de77aaeffce115deaf948ed30c7dbe4c60983c0c
 // 21.1.3.20 String.prototype.startsWith ( searchString [ , position ] )
 bool
 js::str_startsWith(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1-2.
@@ -3702,429 +3677,16 @@ js::InitStringClass(JSContext* cx, Handl
         return nullptr;
 
     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_String, ctor, proto))
         return nullptr;
 
     return proto;
 }
 
-const char*
-js::ValueToPrintable(JSContext* cx, const Value& vArg, JSAutoByteString* bytes, bool asSource)
-{
-    RootedValue v(cx, vArg);
-    JSString* str;
-    if (asSource)
-        str = ValueToSource(cx, v);
-    else
-        str = ToString<CanGC>(cx, v);
-    if (!str)
-        return nullptr;
-    str = QuoteString(cx, str, 0);
-    if (!str)
-        return nullptr;
-    return bytes->encodeLatin1(cx, str);
-}
-
-template <AllowGC allowGC>
-JSString*
-js::ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
-{
-    /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
-    MOZ_ASSERT(!arg.isString());
-
-    Value v = arg;
-    if (!v.isPrimitive()) {
-        MOZ_ASSERT(!cx->helperThread());
-        if (!allowGC)
-            return nullptr;
-        RootedValue v2(cx, v);
-        if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
-            return nullptr;
-        v = v2;
-    }
-
-    JSString* str;
-    if (v.isString()) {
-        str = v.toString();
-    } else if (v.isInt32()) {
-        str = Int32ToString<allowGC>(cx, v.toInt32());
-    } else if (v.isDouble()) {
-        str = NumberToString<allowGC>(cx, v.toDouble());
-    } else if (v.isBoolean()) {
-        str = BooleanToString(cx, v.toBoolean());
-    } else if (v.isNull()) {
-        str = cx->names().null;
-    } else if (v.isSymbol()) {
-        MOZ_ASSERT(!cx->helperThread());
-        if (allowGC) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                      JSMSG_SYMBOL_TO_STRING);
-        }
-        return nullptr;
-    } else {
-        MOZ_ASSERT(v.isUndefined());
-        str = cx->names().undefined;
-    }
-    return str;
-}
-
-template JSString*
-js::ToStringSlow<CanGC>(JSContext* cx, HandleValue arg);
-
-template JSString*
-js::ToStringSlow<NoGC>(JSContext* cx, const Value& arg);
-
-JS_PUBLIC_API(JSString*)
-js::ToStringSlow(JSContext* cx, HandleValue v)
-{
-    return ToStringSlow<CanGC>(cx, v);
-}
-
-static JSString*
-SymbolToSource(JSContext* cx, Symbol* symbol)
-{
-    RootedString desc(cx, symbol->description());
-    SymbolCode code = symbol->code();
-    if (code != SymbolCode::InSymbolRegistry && code != SymbolCode::UniqueSymbol) {
-        // Well-known symbol.
-        MOZ_ASSERT(uint32_t(code) < JS::WellKnownSymbolLimit);
-        return desc;
-    }
-
-    StringBuffer buf(cx);
-    if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(") : !buf.append("Symbol("))
-        return nullptr;
-    if (desc) {
-        desc = StringToSource(cx, desc);
-        if (!desc || !buf.append(desc))
-            return nullptr;
-    }
-    if (!buf.append(')'))
-        return nullptr;
-    return buf.finishString();
-}
-
-JSString*
-js::ValueToSource(JSContext* cx, HandleValue v)
-{
-    if (!CheckRecursionLimit(cx))
-        return nullptr;
-    assertSameCompartment(cx, v);
-
-    if (v.isUndefined())
-        return cx->names().void0;
-    if (v.isString())
-        return StringToSource(cx, v.toString());
-    if (v.isSymbol())
-        return SymbolToSource(cx, v.toSymbol());
-    if (v.isPrimitive()) {
-        /* Special case to preserve negative zero, _contra_ toString. */
-        if (v.isDouble() && IsNegativeZero(v.toDouble())) {
-            static const Latin1Char negativeZero[] = {'-', '0'};
-
-            return NewStringCopyN<CanGC>(cx, negativeZero, mozilla::ArrayLength(negativeZero));
-        }
-        return ToString<CanGC>(cx, v);
-    }
-
-    RootedValue fval(cx);
-    RootedObject obj(cx, &v.toObject());
-    if (!GetProperty(cx, obj, obj, cx->names().toSource, &fval))
-        return nullptr;
-    if (IsCallable(fval)) {
-        RootedValue v(cx);
-        if (!js::Call(cx, fval, obj, &v))
-            return nullptr;
-
-        return ToString<CanGC>(cx, v);
-    }
-
-    return ObjectToSource(cx, obj);
-}
-
-JSString*
-js::StringToSource(JSContext* cx, JSString* str)
-{
-    return QuoteString(cx, str, '"');
-}
-
-bool
-js::EqualChars(JSLinearString* str1, JSLinearString* str2)
-{
-    MOZ_ASSERT(str1->length() == str2->length());
-
-    size_t len = str1->length();
-
-    AutoCheckCannotGC nogc;
-    if (str1->hasTwoByteChars()) {
-        if (str2->hasTwoByteChars())
-            return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len);
-
-        return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
-    }
-
-    if (str2->hasLatin1Chars())
-        return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
-
-    return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
-}
-
-bool
-js::EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result)
-{
-    if (str1 == str2) {
-        *result = true;
-        return true;
-    }
-
-    size_t length1 = str1->length();
-    if (length1 != str2->length()) {
-        *result = false;
-        return true;
-    }
-
-    JSLinearString* linear1 = str1->ensureLinear(cx);
-    if (!linear1)
-        return false;
-    JSLinearString* linear2 = str2->ensureLinear(cx);
-    if (!linear2)
-        return false;
-
-    *result = EqualChars(linear1, linear2);
-    return true;
-}
-
-bool
-js::EqualStrings(JSLinearString* str1, JSLinearString* str2)
-{
-    if (str1 == str2)
-        return true;
-
-    size_t length1 = str1->length();
-    if (length1 != str2->length())
-        return false;
-
-    return EqualChars(str1, str2);
-}
-
-static int32_t
-CompareStringsImpl(JSLinearString* str1, JSLinearString* str2)
-{
-    size_t len1 = str1->length();
-    size_t len2 = str2->length();
-
-    AutoCheckCannotGC nogc;
-    if (str1->hasLatin1Chars()) {
-        const Latin1Char* chars1 = str1->latin1Chars(nogc);
-        return str2->hasLatin1Chars()
-               ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
-               : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
-    }
-
-    const char16_t* chars1 = str1->twoByteChars(nogc);
-    return str2->hasLatin1Chars()
-           ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
-           : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
-}
-
-int32_t
-js::CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2)
-{
-    AutoCheckCannotGC nogc;
-    return s2->hasLatin1Chars()
-           ? CompareChars(s1, len1, s2->latin1Chars(nogc), s2->length())
-           : CompareChars(s1, len1, s2->twoByteChars(nogc), s2->length());
-}
-
-bool
-js::CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result)
-{
-    MOZ_ASSERT(str1);
-    MOZ_ASSERT(str2);
-
-    if (str1 == str2) {
-        *result = 0;
-        return true;
-    }
-
-    JSLinearString* linear1 = str1->ensureLinear(cx);
-    if (!linear1)
-        return false;
-
-    JSLinearString* linear2 = str2->ensureLinear(cx);
-    if (!linear2)
-        return false;
-
-    *result = CompareStringsImpl(linear1, linear2);
-    return true;
-}
-
-int32_t
-js::CompareAtoms(JSAtom* atom1, JSAtom* atom2)
-{
-    return CompareStringsImpl(atom1, atom2);
-}
-
-bool
-js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes)
-{
-    size_t length = strlen(asciiBytes);
-#ifdef DEBUG
-    for (size_t i = 0; i != length; ++i)
-        MOZ_ASSERT(unsigned(asciiBytes[i]) <= 127);
-#endif
-    if (length != str->length())
-        return false;
-
-    const Latin1Char* latin1 = reinterpret_cast<const Latin1Char*>(asciiBytes);
-
-    AutoCheckCannotGC nogc;
-    return str->hasLatin1Chars()
-           ? PodEqual(latin1, str->latin1Chars(nogc), length)
-           : EqualChars(latin1, str->twoByteChars(nogc), length);
-}
-
-int32_t
-js_fputs(const char16_t* s, FILE* f)
-{
-    while (*s != 0) {
-        if (fputwc(wchar_t(*s), f) == WEOF)
-            return WEOF;
-        s++;
-    }
-    return 1;
-}
-
-UniqueChars
-js::DuplicateString(JSContext* cx, const char* s)
-{
-    size_t n = strlen(s) + 1;
-    auto ret = cx->make_pod_array<char>(n);
-    if (!ret)
-        return ret;
-    PodCopy(ret.get(), s, n);
-    return ret;
-}
-
-UniqueTwoByteChars
-js::DuplicateString(JSContext* cx, const char16_t* s)
-{
-    size_t n = js_strlen(s) + 1;
-    auto ret = cx->make_pod_array<char16_t>(n);
-    if (!ret)
-        return ret;
-    PodCopy(ret.get(), s, n);
-    return ret;
-}
-
-UniqueChars
-js::DuplicateString(const char* s)
-{
-    size_t n = strlen(s) + 1;
-    UniqueChars ret(js_pod_malloc<char>(n));
-    if (!ret)
-        return ret;
-    PodCopy(ret.get(), s, n);
-    return ret;
-}
-
-UniqueChars
-js::DuplicateString(const char* s, size_t n)
-{
-    UniqueChars ret(js_pod_malloc<char>(n + 1));
-    if (!ret)
-        return nullptr;
-    PodCopy(ret.get(), s, n);
-    ret[n] = 0;
-    return ret;
-}
-
-UniqueTwoByteChars
-js::DuplicateString(const char16_t* s)
-{
-    return DuplicateString(s, js_strlen(s));
-}
-
-UniqueTwoByteChars
-js::DuplicateString(const char16_t* s, size_t n)
-{
-    UniqueTwoByteChars ret(js_pod_malloc<char16_t>(n + 1));
-    if (!ret)
-        return nullptr;
-    PodCopy(ret.get(), s, n);
-    ret[n] = 0;
-    return ret;
-}
-
-JS_PUBLIC_API(char*)
-js_strdup(const char* s)
-{
-    return DuplicateString(s).release();
-}
-
-template <typename CharT>
-const CharT*
-js_strchr_limit(const CharT* s, char16_t c, const CharT* limit)
-{
-    while (s < limit) {
-        if (*s == c)
-            return s;
-        s++;
-    }
-    return nullptr;
-}
-
-template const Latin1Char*
-js_strchr_limit(const Latin1Char* s, char16_t c, const Latin1Char* limit);
-
-template const char16_t*
-js_strchr_limit(const char16_t* s, char16_t c, const char16_t* limit);
-
-char16_t*
-js::InflateString(JSContext* cx, const char* bytes, size_t length)
-{
-    char16_t* chars = cx->pod_malloc<char16_t>(length + 1);
-    if (!chars)
-        return nullptr;
-    CopyAndInflateChars(chars, bytes, length);
-    chars[length] = 0;
-    return chars;
-}
-
-template <typename CharT>
-bool
-js::DeflateStringToBuffer(JSContext* maybecx, const CharT* src, size_t srclen,
-                          char* dst, size_t* dstlenp)
-{
-    size_t dstlen = *dstlenp;
-    if (srclen > dstlen) {
-        for (size_t i = 0; i < dstlen; i++)
-            dst[i] = char(src[i]);
-        if (maybecx) {
-            AutoSuppressGC suppress(maybecx);
-            JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
-                                      JSMSG_BUFFER_TOO_SMALL);
-        }
-        return false;
-    }
-    for (size_t i = 0; i < srclen; i++)
-        dst[i] = char(src[i]);
-    *dstlenp = srclen;
-    return true;
-}
-
-template bool
-js::DeflateStringToBuffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
-
-template bool
-js::DeflateStringToBuffer(JSContext* maybecx, const char16_t* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
-
 #define ____ false
 
 /*
  * Uri reserved chars + #:
  * - 35: #
  * - 36: $
  * - 38: &
  * - 43: +
@@ -4470,192 +4032,16 @@ js::EncodeURI(JSContext* cx, StringBuffe
         return false;
     if (result == EncodeResult::Encode_BadUri) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_URI);
         return false;
     }
     return true;
 }
 
-/*
- * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
- * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
- */
-uint32_t
-js::OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char)
-{
-    MOZ_ASSERT(ucs4Char <= unicode::NonBMPMax);
-
-    if (ucs4Char < 0x80) {
-        utf8Buffer[0] = uint8_t(ucs4Char);
-        return 1;
-    }
-
-    uint32_t a = ucs4Char >> 11;
-    uint32_t utf8Length = 2;
-    while (a) {
-        a >>= 5;
-        utf8Length++;
-    }
-
-    MOZ_ASSERT(utf8Length <= 4);
-
-    uint32_t i = utf8Length;
-    while (--i) {
-        utf8Buffer[i] = uint8_t((ucs4Char & 0x3F) | 0x80);
-        ucs4Char >>= 6;
-    }
-
-    utf8Buffer[0] = uint8_t(0x100 - (1 << (8 - utf8Length)) + ucs4Char);
-    return utf8Length;
-}
-
-size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, JSLinearString* str,
-                         uint32_t quote)
-{
-    size_t len = str->length();
-    AutoCheckCannotGC nogc;
-    return str->hasLatin1Chars()
-           ? PutEscapedStringImpl(buffer, bufferSize, out, str->latin1Chars(nogc), len, quote)
-           : PutEscapedStringImpl(buffer, bufferSize, out, str->twoByteChars(nogc), len, quote);
-}
-
-template <typename CharT>
-size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
-                         size_t length, uint32_t quote)
-{
-    enum {
-        STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
-    } state;
-
-    MOZ_ASSERT(quote == 0 || quote == '\'' || quote == '"');
-    MOZ_ASSERT_IF(!buffer, bufferSize == 0);
-    MOZ_ASSERT_IF(out, !buffer);
-
-    if (bufferSize == 0)
-        buffer = nullptr;
-    else
-        bufferSize--;
-
-    const CharT* charsEnd = chars + length;
-    size_t n = 0;
-    state = FIRST_QUOTE;
-    unsigned shift = 0;
-    unsigned hex = 0;
-    unsigned u = 0;
-    char c = 0;  /* to quell GCC warnings */
-
-    for (;;) {
-        switch (state) {
-          case STOP:
-            goto stop;
-          case FIRST_QUOTE:
-            state = CHARS;
-            goto do_quote;
-          case LAST_QUOTE:
-            state = STOP;
-          do_quote:
-            if (quote == 0)
-                continue;
-            c = (char)quote;
-            break;
-          case CHARS:
-            if (chars == charsEnd) {
-                state = LAST_QUOTE;
-                continue;
-            }
-            u = *chars++;
-            if (u < ' ') {
-                if (u != 0) {
-                    const char* escape = strchr(js_EscapeMap, (int)u);
-                    if (escape) {
-                        u = escape[1];
-                        goto do_escape;
-                    }
-                }
-                goto do_hex_escape;
-            }
-            if (u < 127) {
-                if (u == quote || u == '\\')
-                    goto do_escape;
-                c = (char)u;
-            } else if (u < 0x100) {
-                goto do_hex_escape;
-            } else {
-                shift = 16;
-                hex = u;
-                u = 'u';
-                goto do_escape;
-            }
-            break;
-          do_hex_escape:
-            shift = 8;
-            hex = u;
-            u = 'x';
-          do_escape:
-            c = '\\';
-            state = ESCAPE_START;
-            break;
-          case ESCAPE_START:
-            MOZ_ASSERT(' ' <= u && u < 127);
-            c = (char)u;
-            state = ESCAPE_MORE;
-            break;
-          case ESCAPE_MORE:
-            if (shift == 0) {
-                state = CHARS;
-                continue;
-            }
-            shift -= 4;
-            u = 0xF & (hex >> shift);
-            c = (char)(u + (u < 10 ? '0' : 'A' - 10));
-            break;
-        }
-        if (buffer) {
-            MOZ_ASSERT(n <= bufferSize);
-            if (n != bufferSize) {
-                buffer[n] = c;
-            } else {
-                buffer[n] = '\0';
-                buffer = nullptr;
-            }
-        } else if (out) {
-            if (!out->put(&c, 1))
-                return size_t(-1);
-        }
-        n++;
-    }
-  stop:
-    if (buffer)
-        buffer[n] = '\0';
-    return n;
-}
-
-template size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const Latin1Char* chars,
-                         size_t length, uint32_t quote);
-
-template size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char* chars,
-                         size_t length, uint32_t quote);
-
-template size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char16_t* chars,
-                         size_t length, uint32_t quote);
-
-template size_t
-js::PutEscapedString(char* buffer, size_t bufferSize, const Latin1Char* chars, size_t length,
-                     uint32_t quote);
-
-template size_t
-js::PutEscapedString(char* buffer, size_t bufferSize, const char16_t* chars, size_t length,
-                     uint32_t quote);
-
 static bool
 FlatStringMatchHelper(JSContext* cx, HandleString str, HandleString pattern, bool* isFlat, int32_t* match)
 {
     RootedLinearString linearPattern(cx, pattern->ensureLinear(cx));
     if (!linearPattern)
         return false;
 
     static const size_t MAX_FLAT_PAT_LEN = 256;
rename from js/src/jsstr.h
rename to js/src/builtin/String.h
--- a/js/src/jsstr.h
+++ b/js/src/builtin/String.h
@@ -1,295 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jsstr_h
-#define jsstr_h
+#ifndef builtin_String_h
+#define builtin_String_h
 
 #include "mozilla/HashFunctions.h"
 #include "mozilla/PodOperations.h"
 
 #include <stdio.h>
 #include <string.h>
 
 #include "jsutil.h"
 #include "NamespaceImports.h"
 
 #include "gc/Rooting.h"
 #include "js/RootingAPI.h"
 #include "js/UniquePtr.h"
+#include "util/Unicode.h"
 #include "vm/Printer.h"
-#include "vm/Unicode.h"
-
-class JSAutoByteString;
-class JSLinearString;
-
-namespace js {
-
-class StringBuffer;
-
-template <AllowGC allowGC>
-extern JSString*
-ConcatStrings(JSContext* cx,
-              typename MaybeRooted<JSString*, allowGC>::HandleType left,
-              typename MaybeRooted<JSString*, allowGC>::HandleType right);
-
-// Return s advanced past any Unicode white space characters.
-template <typename CharT>
-static inline const CharT*
-SkipSpace(const CharT* s, const CharT* end)
-{
-    MOZ_ASSERT(s <= end);
-
-    while (s < end && unicode::IsSpace(*s))
-        s++;
-
-    return s;
-}
-
-// Return less than, equal to, or greater than zero depending on whether
-// s1 is less than, equal to, or greater than s2.
-template <typename Char1, typename Char2>
-inline int32_t
-CompareChars(const Char1* s1, size_t len1, const Char2* s2, size_t len2)
-{
-    size_t n = Min(len1, len2);
-    for (size_t i = 0; i < n; i++) {
-        if (int32_t cmp = s1[i] - s2[i])
-            return cmp;
-    }
-
-    return int32_t(len1 - len2);
-}
-
-extern int32_t
-CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2);
-
-}  /* namespace js */
-
-/*
- * Shorthands for ASCII (7-bit) decimal and hex conversion.
- * Manually inline isdigit and isxdigit for performance; MSVC doesn't do this for us.
- */
-#define JS7_ISDEC(c)    ((((unsigned)(c)) - '0') <= 9)
-#define JS7_ISA2F(c)    ((((((unsigned)(c)) - 'a') <= 5) || (((unsigned)(c)) - 'A') <= 5))
-#define JS7_UNDEC(c)    ((c) - '0')
-#define JS7_ISOCT(c)    ((((unsigned)(c)) - '0') <= 7)
-#define JS7_UNOCT(c)    (JS7_UNDEC(c))
-#define JS7_ISHEX(c)    ((c) < 128 && (JS7_ISDEC(c) || JS7_ISA2F(c)))
-#define JS7_UNHEX(c)    (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a')
-#define JS7_ISLET(c)    ((c) < 128 && isalpha(c))
-
-static MOZ_ALWAYS_INLINE size_t
-js_strlen(const char16_t* s)
-{
-    return std::char_traits<char16_t>::length(s);
-}
-
-template <typename CharT>
-extern const CharT*
-js_strchr_limit(const CharT* s, char16_t c, const CharT* limit);
-
-extern int32_t
-js_fputs(const char16_t* s, FILE* f);
 
 namespace js {
 
 /* Initialize the String class, returning its prototype object. */
 extern JSObject*
 InitStringClass(JSContext* cx, HandleObject obj);
 
-/*
- * Convert a value to a printable C string.
- */
-extern const char*
-ValueToPrintable(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false);
-
-extern UniqueChars
-DuplicateString(JSContext* cx, const char* s);
-
-extern UniqueTwoByteChars
-DuplicateString(JSContext* cx, const char16_t* s);
-
-/*
- * These variants do not report OOMs, you must arrange for OOMs to be reported
- * yourself.
- */
-extern UniqueChars
-DuplicateString(const char* s);
-
-extern UniqueChars
-DuplicateString(const char* s, size_t n);
-
-extern UniqueTwoByteChars
-DuplicateString(const char16_t* s);
-
-extern UniqueTwoByteChars
-DuplicateString(const char16_t* s, size_t n);
-
-/*
- * Convert a non-string value to a string, returning null after reporting an
- * error, otherwise returning a new string reference.
- */
-template <AllowGC allowGC>
-extern JSString*
-ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg);
-
-/*
- * Convert the given value to a string.  This method includes an inline
- * fast-path for the case where the value is already a string; if the value is
- * known not to be a string, use ToStringSlow instead.
- */
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE JSString*
-ToString(JSContext* cx, JS::HandleValue v)
-{
-    if (v.isString())
-        return v.toString();
-    return ToStringSlow<allowGC>(cx, v);
-}
-
-/*
- * This function implements E-262-3 section 9.8, toString. Convert the given
- * value to a string of characters appended to the given buffer. On error, the
- * passed buffer may have partial results appended.
- */
-inline bool
-ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb);
-
-/*
- * Convert a value to its source expression, returning null after reporting
- * an error, otherwise returning a new string reference.
- */
-extern JSString*
-ValueToSource(JSContext* cx, HandleValue v);
-
-/*
- * Convert a JSString to its source expression; returns null after reporting an
- * error, otherwise returns a new string reference. No Handle needed since the
- * input is dead after the GC.
- */
-extern JSString*
-StringToSource(JSContext* cx, JSString* str);
-
-/*
- * Test if strings are equal. The caller can call the function even if str1
- * or str2 are not GC-allocated things.
- */
-extern bool
-EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result);
-
-/* Use the infallible method instead! */
-extern bool
-EqualStrings(JSContext* cx, JSLinearString* str1, JSLinearString* str2, bool* result) = delete;
-
-/* EqualStrings is infallible on linear strings. */
-extern bool
-EqualStrings(JSLinearString* str1, JSLinearString* str2);
-
-extern bool
-EqualChars(JSLinearString* str1, JSLinearString* str2);
-
-/*
- * Return less than, equal to, or greater than zero depending on whether
- * str1 is less than, equal to, or greater than str2.
- */
-extern bool
-CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
-
-/*
- * Same as CompareStrings but for atoms.  Don't use this to just test
- * for equality; use this when you need an ordering on atoms.
- */
-extern int32_t
-CompareAtoms(JSAtom* atom1, JSAtom* atom2);
-
-/*
- * Return true if the string matches the given sequence of ASCII bytes.
- */
-extern bool
-StringEqualsAscii(JSLinearString* str, const char* asciiBytes);
-
-extern int
-StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start);
-
-/* Return true if the string contains a pattern at |start|. */
-extern bool
-HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start);
-
-template <typename Char1, typename Char2>
-inline bool
-EqualChars(const Char1* s1, const Char2* s2, size_t len);
-
-template <typename Char1>
-inline bool
-EqualChars(const Char1* s1, const Char1* s2, size_t len)
-{
-    return mozilla::PodEqual(s1, s2, len);
-}
-
-template <typename Char1, typename Char2>
-inline bool
-EqualChars(const Char1* s1, const Char2* s2, size_t len)
-{
-    for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) {
-        if (*s1 != *s2)
-            return false;
-    }
-    return true;
-}
-
-/*
- * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt).
- * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden
- * and constitute API misuse.
- */
-JSString*
-SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengthInt);
-
-/*
- * Inflate bytes in ASCII encoding to char16_t code units. Return null on error,
- * otherwise return the char16_t buffer that was malloc'ed. A null char is
- * appended.
- */
-extern char16_t*
-InflateString(JSContext* cx, const char* bytes, size_t length);
-
-/*
- * Inflate bytes to JS chars in an existing buffer. 'dst' must be large
- * enough for 'srclen' char16_t code units. The buffer is NOT null-terminated.
- */
-inline void
-CopyAndInflateChars(char16_t* dst, const char* src, size_t srclen)
-{
-    for (size_t i = 0; i < srclen; i++)
-        dst[i] = (unsigned char) src[i];
-}
-
-inline void
-CopyAndInflateChars(char16_t* dst, const JS::Latin1Char* src, size_t srclen)
-{
-    for (size_t i = 0; i < srclen; i++)
-        dst[i] = src[i];
-}
-
-/*
- * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for
- * 'length chars. The buffer is NOT null-terminated. The destination length
- * must to be initialized with the buffer size and will contain on return the
- * number of copied bytes.
- */
-template <typename CharT>
-extern bool
-DeflateStringToBuffer(JSContext* maybecx, const CharT* chars,
-                      size_t charsLength, char* bytes, size_t* length);
-
 extern bool
 str_fromCharCode(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_fromCharCode_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval);
 
 extern bool
 str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp);
@@ -409,100 +152,16 @@ str_normalize(JSContext* cx, unsigned ar
 extern bool
 str_localeCompare(JSContext* cx, unsigned argc, Value* vp);
 
 #endif // EXPOSE_INTL_API
 
 extern bool
 str_concat(JSContext* cx, unsigned argc, Value* vp);
 
-/*
- * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
- * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
- */
-extern uint32_t
-OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char);
-
-extern size_t
-PutEscapedStringImpl(char* buffer, size_t size, GenericPrinter* out, JSLinearString* str,
-                     uint32_t quote);
-
-template <typename CharT>
-extern size_t
-PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
-                     size_t length, uint32_t quote);
-
-/*
- * Write str into buffer escaping any non-printable or non-ASCII character
- * using \escapes for JS string literals.
- * Guarantees that a NUL is at the end of the buffer unless size is 0. Returns
- * the length of the written output, NOT including the NUL. Thus, a return
- * value of size or more means that the output was truncated. If buffer
- * is null, just returns the length of the output. If quote is not 0, it must
- * be a single or double quote character that will quote the output.
-*/
-inline size_t
-PutEscapedString(char* buffer, size_t size, JSLinearString* str, uint32_t quote)
-{
-    size_t n = PutEscapedStringImpl(buffer, size, nullptr, str, quote);
-
-    /* PutEscapedStringImpl can only fail with a file. */
-    MOZ_ASSERT(n != size_t(-1));
-    return n;
-}
-
-template <typename CharT>
-inline size_t
-PutEscapedString(char* buffer, size_t bufferSize, const CharT* chars, size_t length, uint32_t quote)
-{
-    size_t n = PutEscapedStringImpl(buffer, bufferSize, nullptr, chars, length, quote);
-
-    /* PutEscapedStringImpl can only fail with a file. */
-    MOZ_ASSERT(n != size_t(-1));
-    return n;
-}
-
-inline bool
-EscapedStringPrinter(GenericPrinter& out, JSLinearString* str, uint32_t quote)
-{
-    return PutEscapedStringImpl(nullptr, 0, &out, str, quote) != size_t(-1);
-}
-
-inline bool
-EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint32_t quote)
-{
-    return PutEscapedStringImpl(nullptr, 0, &out, chars, length, quote) != size_t(-1);
-}
-
-/*
- * Write str into file escaping any non-printable or non-ASCII character.
- * If quote is not 0, it must be a single or double quote character that
- * will quote the output.
-*/
-inline bool
-FileEscapedString(FILE* fp, JSLinearString* str, uint32_t quote)
-{
-    Fprinter out(fp);
-    bool res = EscapedStringPrinter(out, str, quote);
-    out.finish();
-    return res;
-}
-
-inline bool
-FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
-{
-    Fprinter out(fp);
-    bool res = EscapedStringPrinter(out, chars, length, quote);
-    out.finish();
-    return res;
-}
-
-bool
-EncodeURI(JSContext* cx, StringBuffer& sb, const char* chars, size_t length);
-
 ArrayObject*
 str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
                  uint32_t limit);
 
 JSString *
 str_flat_replace_string(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString replacement);
 
@@ -522,9 +181,9 @@ StringConstructor(JSContext* cx, unsigne
 extern bool
 FlatStringMatch(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 FlatStringSearch(JSContext* cx, unsigned argc, Value* vp);
 
 } /* namespace js */
 
-#endif /* jsstr_h */
+#endif /* builtin_String_h */
rename from js/src/builtin/SymbolObject.cpp
rename to js/src/builtin/Symbol.cpp
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/Symbol.cpp
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "builtin/SymbolObject.h"
+#include "builtin/Symbol.h"
 
-#include "vm/StringBuffer.h"
-#include "vm/Symbol.h"
+#include "util/StringBuffer.h"
+#include "vm/SymbolType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using JS::Symbol;
 using namespace js;
 
 const Class SymbolObject::class_ = {
rename from js/src/builtin/SymbolObject.h
rename to js/src/builtin/Symbol.h
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/Symbol.h
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef builtin_SymbolObject_h
-#define builtin_SymbolObject_h
+#ifndef builtin_Symbol_h
+#define builtin_Symbol_h
 
 #include "vm/NativeObject.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
 namespace js {
 
 class SymbolObject : public NativeObject
 {
     /* Stores this Symbol object's [[PrimitiveValue]]. */
     static const unsigned PRIMITIVE_VALUE_SLOT = 0;
 
@@ -60,9 +60,9 @@ class SymbolObject : public NativeObject
 extern JSObject*
 InitSymbolClass(JSContext* cx, HandleObject obj);
 
 extern JSObject*
 InitBareSymbolCtor(JSContext* cx, HandleObject obj);
 
 } /* namespace js */
 
-#endif /* builtin_SymbolObject_h */
+#endif /* builtin_Symbol_h */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -33,28 +33,29 @@
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
-#include "vm/StringBuffer.h"
 #include "vm/TraceLogging.h"
 #include "wasm/AsmJS.h"
 #include "wasm/WasmBinaryToText.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTextToBinary.h"
 #include "wasm/WasmTypes.h"
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -9,21 +9,21 @@
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jsutil.h"
 
 #include "builtin/SIMD.h"
 #include "gc/Marking.h"
 #include "js/Vector.h"
+#include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSFunction.h"
-#include "vm/String.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 #include "vm/TypedArrayObject.h"
 
 #include "gc/Nursery-inl.h"
 #include "gc/StoreBuffer-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -16,17 +16,17 @@
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
 #include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::ReportInternalError;
--- a/js/src/builtin/intl/CommonFunctions.h
+++ b/js/src/builtin/intl/CommonFunctions.h
@@ -12,17 +12,17 @@
 
 #include <stddef.h>
 #include <stdint.h>
 #include <string.h>
 
 #include "builtin/intl/ICUStubs.h"
 #include "js/RootingAPI.h"
 #include "js/Vector.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 
 namespace intl {
 
 /**
  * Initialize a new Intl.* object using the named self-hosted function.
  */
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -20,17 +20,17 @@
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "js/Class.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::Range;
 using mozilla::RangedPtr;
 
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -12,17 +12,17 @@
 #include "mozilla/Casting.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::AssertedCast;
 
--- a/js/src/builtin/intl/SharedIntlData.cpp
+++ b/js/src/builtin/intl/SharedIntlData.cpp
@@ -8,22 +8,21 @@
 
 #include "builtin/intl/SharedIntlData.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/HashFunctions.h"
 
 #include <stdint.h>
 
-#include "jsstr.h"
-
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
+#include "builtin/String.h"
 #include "js/Utility.h"
 #include "vm/JSAtom.h"
 
 using js::HashNumber;
 using js::intl::StringsAreEqual;
 
 template<typename Char>
 static constexpr Char
--- a/js/src/builtin/intl/SharedIntlData.h
+++ b/js/src/builtin/intl/SharedIntlData.h
@@ -12,17 +12,17 @@
 #include <stddef.h>
 
 #include "js/AllocPolicy.h"
 #include "js/CharacterEncoding.h"
 #include "js/GCAPI.h"
 #include "js/GCHashTable.h"
 #include "js/RootingAPI.h"
 #include "js/Utility.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 
 namespace intl {
 
 /**
  * Stores Intl data which can be shared across compartments (but not contexts).
  *
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -12,17 +12,17 @@
 #include "ffi.h"
 #include "prlink.h"
 
 #include "ctypes/typedefs.h"
 #include "js/AllocPolicy.h"
 #include "js/GCHashTable.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
 ** Utility classes
 *******************************************************************************/
 
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -7,17 +7,17 @@
 #ifndef frontend_BytecodeCompiler_h
 #define frontend_BytecodeCompiler_h
 
 #include "mozilla/Maybe.h"
 
 #include "NamespaceImports.h"
 
 #include "vm/Scope.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 
 class JSLinearString;
 
 namespace js {
 
 class LazyScript;
 class LifoAlloc;
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/FloatingPoint.h"
 
 #include "jslibmath.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "js/Conversions.h"
+#include "vm/StringType.h"
 
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsNaN;
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -7,18 +7,18 @@
 #include "frontend/NameFunctions.h"
 
 #include "mozilla/MemoryChecking.h"
 #include "mozilla/Sprintf.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
+#include "util/StringBuffer.h"
 #include "vm/JSFunction.h"
-#include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::frontend;
 
 namespace {
 
 class NameResolver
 {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -33,16 +33,17 @@
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpObject.h"
+#include "vm/StringType.h"
 #include "wasm/AsmJS.h"
 
 #include "frontend/ParseContext-inl.h"
 #include "frontend/ParseNode-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -21,22 +21,22 @@
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/Parser.h"
 #include "frontend/ReservedWords.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
+#include "util/StringBuffer.h"
+#include "util/Unicode.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
-#include "vm/StringBuffer.h"
-#include "vm/Unicode.h"
 
 using mozilla::ArrayLength;
 using mozilla::MakeScopeExit;
 using mozilla::PodArrayZero;
 using mozilla::PodCopy;
 
 struct ReservedWordInfo
 {
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -173,21 +173,21 @@
 #include <stdio.h>
 
 #include "jspubtd.h"
 
 #include "frontend/ErrorReporter.h"
 #include "frontend/TokenKind.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
+#include "util/Unicode.h"
 #include "vm/ErrorReporting.h"
 #include "vm/JSContext.h"
 #include "vm/RegExpShared.h"
-#include "vm/String.h"
-#include "vm/Unicode.h"
+#include "vm/StringType.h"
 
 struct KeywordInfo;
 
 namespace js {
 namespace frontend {
 
 struct TokenPos {
     uint32_t    begin;  // Offset of the token's first char.
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -8,17 +8,17 @@
 
 #include "gc/GCInternals.h"
 #include "gc/GCTrace.h"
 #include "gc/Nursery.h"
 #include "jit/JitCompartment.h"
 #include "threading/CpuCount.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "gc/ArenaList-inl.h"
 #include "gc/Heap-inl.h"
 #include "gc/PrivateIterators-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace gc;
--- a/js/src/gc/AtomMarking.h
+++ b/js/src/gc/AtomMarking.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_AtomMarking_h
 #define gc_AtomMarking_h
 
 #include "NamespaceImports.h"
 #include "ds/Bitmap.h"
 #include "threading/ProtectedData.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
 namespace js {
 namespace gc {
 
 class Arena;
 
 // This class manages state used for marking atoms during GCs.
 // See AtomMarking.cpp for details.
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -10,17 +10,17 @@
 #include "gc/Policy.h"
 #include "gc/Zone.h"
 #include "js/HashTable.h"
 #include "js/Value.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSObject.h"
 #include "vm/SharedArrayObject.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 #include "wasm/WasmJS.h"
 
 namespace js {
 
 bool
 RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(shadowZone->runtimeFromActiveCooperatingThread()));
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -228,31 +228,31 @@
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Printer.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
-#include "vm/String.h"
-#include "vm/Symbol.h"
+#include "vm/StringType.h"
+#include "vm/SymbolType.h"
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 #include "vm/WrapperObject.h"
 
 #include "gc/Heap-inl.h"
 #include "gc/Marking-inl.h"
 #include "gc/Nursery-inl.h"
 #include "gc/PrivateIterators-inl.h"
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/Stack-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::Swap;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -22,27 +22,27 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpShared.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 #include "wasm/WasmJS.h"
 
 #include "gc/GC-inl.h"
 #include "gc/Nursery-inl.h"
 #include "gc/PrivateIterators-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/NativeObject-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using JS::MapTypeToTraceKind;
 
 using mozilla::DebugOnly;
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -15,16 +15,17 @@
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "jsutil.h"
 
 #include "gc/GC.h"
 #include "gc/Memory.h"
+#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::gcstats;
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -10,20 +10,21 @@
 
 #include "jsutil.h"
 #include "NamespaceImports.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/PublicIterators.h"
 #include "gc/Zone.h"
+#include "util/Text.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/Shape.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
 #include "gc/GC-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/ObjectGroup-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using mozilla::DebugOnly;
--- a/js/src/gdb/tests/test-JSString.cpp
+++ b/js/src/gdb/tests/test-JSString.cpp
@@ -1,14 +1,14 @@
 #include "gdb-tests.h"
 
 #include "vm/JSContext.h"
 // When JSGC_ANALYSIS is #defined, Rooted<JSFlatString*> needs the definition
 // of JSFlatString in order to figure out its ThingRootKind
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 FRAGMENT(JSString, simple) {
   AutoSuppressHazardsForTest noanalysis;
 
   JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
   JS::Rooted<JSString*> x(cx, JS_NewStringCopyN(cx, "x", 1));
   JS::Rooted<JSString*> z(cx, JS_NewStringCopyZ(cx, "z"));
 
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -31,18 +31,18 @@
 #include "irregexp/RegExpParser.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Move.h"
 
 #include "frontend/TokenStream.h"
 #include "gc/GC.h"
 #include "irregexp/RegExpCharacters.h"
+#include "util/StringBuffer.h"
 #include "vm/ErrorReporting.h"
-#include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::irregexp;
 
 using mozilla::Move;
 using mozilla::PointerRangeSize;
 
 // ----------------------------------------------------------------------------
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -16,21 +16,21 @@
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 
 #include <type_traits>
 
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsnum.h"
-#include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "builtin/RegExp.h"
 #include "builtin/SelfHostingDefines.h"
+#include "builtin/String.h"
 #include "builtin/TypedObject.h"
 #include "gc/Nursery.h"
 #include "irregexp/NativeRegExpMacroAssembler.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/IonBuilder.h"
 #include "jit/IonIC.h"
 #include "jit/IonOptimizationLevels.h"
@@ -38,24 +38,25 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "jit/StackSlotAllocator.h"
+#include "util/Unicode.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #include "vm/TypedArrayObject.h"
-#include "vm/Unicode.h"
 #include "vtune/VTuneWrapper.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Interpreter-inl.h"
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -10,17 +10,17 @@
 #include "mozilla/HashFunctions.h"
 
 #include <algorithm>
 
 #include "jsfriendapi.h"
 #include "jstypes.h"
 
 #include "js/Value.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 namespace jit {
 
 typedef uint32_t RecoverOffset;
 typedef uint32_t SnapshotOffset;
 typedef uint32_t BailoutId;
 
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -2,26 +2,26 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Casting.h"
 
 #include "jsmath.h"
-#include "jsstr.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/RelativeTimeFormat.h"
 #include "builtin/MapObject.h"
 #include "builtin/SIMD.h"
+#include "builtin/String.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -10,26 +10,27 @@
 #include "mozilla/EndianUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include <ctype.h>
 
 #include "jslibmath.h"
-#include "jsstr.h"
 
 #include "builtin/RegExp.h"
+#include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineInspector.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 #include "js/Conversions.h"
+#include "util/Text.h"
 
 #include "jsboolinlines.h"
 
 #include "vm/JSAtom-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/OptimizationTracking.h"
 
 #include "ds/Sort.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "js/TrackedOptimizationInfo.h"
+#include "util/Text.h"
 
 #include "vm/ObjectGroup-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -3,32 +3,32 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/Recover.h"
 
 #include "jsapi.h"
 #include "jsmath.h"
-#include "jsstr.h"
 
 #include "builtin/RegExp.h"
 #include "builtin/SIMD.h"
+#include "builtin/String.h"
 #include "builtin/TypedObject.h"
 #include "gc/Heap.h"
 #include "jit/JitSpewer.h"
 #include "jit/JSJitFrameIter.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "jit/VMFunctions.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 bool
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -20,16 +20,17 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/SharedICHelpers.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/Interpreter.h"
+#include "vm/StringType.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/SharedICHelpers-inl.h"
 #include "vm/Interpreter-inl.h"
 
 using mozilla::BitwiseCast;
 
 namespace js {
--- a/js/src/jit/mips-shared/Architecture-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Architecture-mips-shared.cpp
@@ -30,17 +30,17 @@ get_mips_flags()
 #else
 # ifdef __linux__
     FILE* fp = fopen("/proc/cpuinfo", "r");
     if (!fp)
         return flags;
 
     char buf[1024];
     memset(buf, 0, sizeof(buf));
-    fread(buf, sizeof(char), sizeof(buf) - 1, fp);
+    (void)fread(buf, sizeof(char), sizeof(buf) - 1, fp);
     fclose(fp);
     if (strstr(buf, "FPU"))
         flags |= HWCAP_FPU;
     if (strstr(buf, "Loongson"))
         flags |= HWCAP_LOONGSON;
     if (strstr(buf, "mips32r2") || strstr(buf, "mips64r2"))
         flags |= HWCAP_R2;
 # endif
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -762,19 +762,16 @@ class MIPSBufferWithExecutableCopy : pub
         }
         this->putBytes(numBytes, code);
         return !this->oom();
     }
 };
 
 class AssemblerMIPSShared : public AssemblerShared
 {
-#ifdef JS_JITSPEW
-   Sprinter* printer;
-#endif
   public:
 
     enum Condition {
         Equal,
         NotEqual,
         Above,
         AboveOrEqual,
         Below,
@@ -890,16 +887,20 @@ class AssemblerMIPSShared : public Assem
 
     js::Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
 
     CompactBufferWriter jumpRelocations_;
     CompactBufferWriter dataRelocations_;
 
     MIPSBufferWithExecutableCopy m_buffer;
 
+#ifdef JS_JITSPEW
+   Sprinter* printer;
+#endif
+
   public:
     AssemblerMIPSShared()
       : m_buffer(),
 #ifdef JS_JITSPEW
        printer(nullptr),
 #endif
        isFinished(false)
     { }
--- a/js/src/jit/mips32/CodeGenerator-mips32.cpp
+++ b/js/src/jit/mips32/CodeGenerator-mips32.cpp
@@ -544,21 +544,16 @@ void
 CodeGeneratorMIPS::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
 {
     Register64 input = ToRegister64(lir->getInt64Operand(0));
     mozilla::DebugOnly<FloatRegister> output = ToFloatRegister(lir->output());
 
     MInt64ToFloatingPoint* mir = lir->mir();
     MIRType toType = mir->type();
 
-    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(input.low);
-    regs.take(input.high);
-    Register temp = regs.takeAny();
-
     masm.setupWasmABICall();
     masm.passABIArg(input.high);
     masm.passABIArg(input.low);
 
     if (lir->mir()->isUnsigned())
         if (toType == MIRType::Double)
             masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Uint64ToDouble, MoveOp::DOUBLE);
         else
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -714,16 +714,17 @@ JitRuntime::generateVMWrapper(JSContext*
             masm.PushEmptyRooted(f.outParamRootType);
             outParamSize = masm.framePushed() - pushed;
         }
         break;
 
       case Type_Bool:
       case Type_Int32:
         MOZ_ASSERT(sizeof(uintptr_t) == sizeof(uint32_t));
+        MOZ_FALLTHROUGH;
       case Type_Pointer:
         outParamSize = sizeof(uintptr_t);
         masm.reserveStack(outParamSize);
         break;
 
       case Type_Double:
         outParamSize = sizeof(double);
         masm.reserveStack(outParamSize);
@@ -826,16 +827,17 @@ JitRuntime::generateVMWrapper(JSContext*
 
       case Type_Value:
         masm.loadValue(Address(StackPointer, 0), JSReturnOperand);
         masm.freeStack(sizeof(Value));
         break;
 
       case Type_Int32:
         MOZ_ASSERT(sizeof(uintptr_t) == sizeof(uint32_t));
+        MOZ_FALLTHROUGH;
       case Type_Pointer:
         masm.load32(Address(StackPointer, 0), ReturnReg);
         masm.freeStack(sizeof(uintptr_t));
         break;
 
       case Type_Bool:
         masm.load8ZeroExtend(Address(StackPointer, 0), ReturnReg);
         masm.freeStack(sizeof(uintptr_t));
--- a/js/src/jit/shared/Lowering-shared.cpp
+++ b/js/src/jit/shared/Lowering-shared.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
 using namespace js;
 using namespace jit;
 
 bool
 LIRGeneratorShared::ShouldReorderCommutative(MDefinition* lhs, MDefinition* rhs, MInstruction* ins)
 {
     // lhs and rhs are used by the commutative operator.
--- a/js/src/jsapi-tests/testErrorInterceptor.cpp
+++ b/js/src/jsapi-tests/testErrorInterceptor.cpp
@@ -1,13 +1,13 @@
 #include "jsapi.h"
 
 #include "jsapi-tests/tests.h"
 
-#include "vm/StringBuffer.h"
+#include "util/StringBuffer.h"
 
 // Tests for JS_GetErrorInterceptorCallback and JS_SetErrorInterceptorCallback.
 
 
 namespace {
 const double EXN_VALUE = 3.14;
 
 static JS::PersistentRootedString gLatestMessage;
--- a/js/src/jsapi-tests/testIndexToString.cpp
+++ b/js/src/jsapi-tests/testIndexToString.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsnum.h"
-#include "jsstr.h"
 
+#include "builtin/String.h"
 #include "jsapi-tests/tests.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using mozilla::ArrayLength;
 
 static const struct TestPair {
     uint32_t num;
     const char* expected;
 } tests[] = {
     { 0, "0" },
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "jsapi-tests/tests.h"
 #include "vm/JSAtom.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 using mozilla::ArrayLength;
 
 BEGIN_TEST(testAtomizedIsNotPinned)
 {
     /* Try to pick a string that won't be interned by other tests in this runtime. */
     static const char someChars[] = "blah blah blah? blah blah blah";
     JS::Rooted<JSAtom*> atom(cx, js::Atomize(cx, someChars, ArrayLength(someChars)));
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -3,17 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <limits>
 #include <string.h>
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "js/Printf.h"
 #include "jsapi-tests/tests.h"
 
 using namespace js;
 
 class AutoInflatedString {
     JSContext * const cx;
--- a/js/src/jsapi-tests/testSavedStacks.cpp
+++ b/js/src/jsapi-tests/testSavedStacks.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsfriendapi.h"
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "builtin/TestingFunctions.h"
 #include "jsapi-tests/tests.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSCompartment.h"
 #include "vm/SavedStacks.h"
 
 BEGIN_TEST(testSavedStacks_withNoStack)
--- a/js/src/jsapi-tests/testSharedImmutableStringsCache.cpp
+++ b/js/src/jsapi-tests/testSharedImmutableStringsCache.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/IntegerRange.h"
 
 #include "js/Vector.h"
 #include "jsapi-tests/tests.h"
 #include "threading/Thread.h"
+#include "util/Text.h"
 #include "vm/SharedImmutableStringsCache.h"
 
 const int NUM_THREADS = 256;
 const int NUM_ITERATIONS = 256;
 
 const int NUM_STRINGS = 4;
 const char16_t *const STRINGS[NUM_STRINGS] = {
     u"uno",
--- a/js/src/jsapi-tests/testStringBuffer.cpp
+++ b/js/src/jsapi-tests/testStringBuffer.cpp
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi-tests/tests.h"
+#include "util/StringBuffer.h"
 #include "vm/JSAtom.h"
-#include "vm/StringBuffer.h"
 
 BEGIN_TEST(testStringBuffer_finishString)
 {
     JSString* str = JS_NewStringCopyZ(cx, "foopy");
     CHECK(str);
 
     JS::Rooted<JSAtom*> atom(cx, js::AtomizeString(cx, str));
     CHECK(atom);
--- a/js/src/jsapi-tests/testUTF8.cpp
+++ b/js/src/jsapi-tests/testUTF8.cpp
@@ -3,17 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Range.h"
 
 #include "jsapi.h"
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "js/CharacterEncoding.h"
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testUTF8_badUTF8)
 {
     static const char badUTF8[] = "...\xC0...";
     JSString* str = JS_NewStringCopyZ(cx, badUTF8);
--- a/js/src/jsapi-tests/testUbiNode.cpp
+++ b/js/src/jsapi-tests/testUbiNode.cpp
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/TestingFunctions.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeDominatorTree.h"
 #include "js/UbiNodePostOrder.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "jsapi-tests/tests.h"
+#include "util/Text.h"
 #include "vm/SavedFrame.h"
 
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using namespace js;
 
 // A helper JS::ubi::Node concrete implementation that can be used to make mock
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsfriendapi.h"
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "jsapi-tests/tests.h"
 #include "vm/JSScript.h"
 
 #include "vm/JSScript-inl.h"
 
 static bool
 GetBuildId(JS::BuildIdCharVector* buildId)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -25,28 +25,28 @@
 
 #include "jsarray.h"
 #include "jsbool.h"
 #include "jsdate.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsmath.h"
 #include "jsnum.h"
-#include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/Eval.h"
 #include "builtin/JSON.h"
 #include "builtin/MapObject.h"
 #include "builtin/Promise.h"
 #include "builtin/RegExp.h"
 #include "builtin/Stream.h"
-#include "builtin/SymbolObject.h"
+#include "builtin/String.h"
+#include "builtin/Symbol.h"
 #ifdef ENABLE_SIMD
 # include "builtin/SIMD.h"
 #endif
 #ifdef ENABLE_BINARYDATA
 # include "builtin/TypedObject.h"
 #endif
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
@@ -61,16 +61,18 @@
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
@@ -80,31 +82,30 @@
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
 #include "vm/SavedStacks.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
-#include "vm/String.h"
-#include "vm/StringBuffer.h"
-#include "vm/Symbol.h"
+#include "vm/StringType.h"
+#include "vm/SymbolType.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 #include "wasm/AsmJS.h"
 #include "wasm/WasmModule.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSFunction-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/SavedStacks-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::Maybe;
 using mozilla::PodCopy;
 using mozilla::Some;
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -19,26 +19,27 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Sort.h"
 #include "gc/Heap.h"
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
 #include "js/Conversions.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
-#include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/Caches-inl.h"
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/Interpreter-inl.h"
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -9,22 +9,22 @@
  */
 
 #include "jsboolinlines.h"
 
 #include "jsapi.h"
 #include "jstypes.h"
 
 #include "jit/InlinableNatives.h"
+#include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
-#include "vm/StringBuffer.h"
 
 #include "vm/BooleanObject-inl.h"
 
 using namespace js;
 
 const Class BooleanObject::class_ = {
     "Boolean",
     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean)
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -23,30 +23,30 @@
 #include "mozilla/Sprintf.h"
 
 #include <ctype.h>
 #include <math.h>
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsnum.h"
-#include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
+#include "builtin/String.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
-#include "vm/String.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 #include "vm/Time.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::Atomic;
 using mozilla::ArrayLength;
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -18,25 +18,26 @@
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "js/CharacterEncoding.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/SavedStacks.h"
 #include "vm/SelfHosting.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 
 #include "vm/ErrorObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/SavedStacks-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -15,31 +15,31 @@
 #include "mozilla/RangedPtr.h"
 
 #ifdef HAVE_LOCALECONV
 #include <locale.h>
 #endif
 #include <math.h>
 #include <string.h>
 
-#include "jsstr.h"
 #include "jstypes.h"
 
+#include "builtin/String.h"
 #include "double-conversion/double-conversion.h"
 #include "js/Conversions.h"
 #include "util/DoubleToString.h"
+#include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
-#include "vm/StringBuffer.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/NumberObject-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using namespace js;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::MinNumberValue;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -9,17 +9,17 @@
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Range.h"
 
 #include "NamespaceImports.h"
 
 #include "js/Conversions.h"
 
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 
 // This macro is should be `one' if current compiler supports builtin functions
 // like __builtin_sadd_overflow.
 #if __GNUC__ >= 5
     // GCC 5 and above supports these functions.
     #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1
 #else
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -27,17 +27,17 @@ with Files('gc/**'):
 with Files('jit/**'):
     BUG_COMPONENT = component_jit
 
 # File-specific metadata
 for gcfile in ['devtools/rootAnalysis', 'devtools/gc-ubench', 'devtools/gctrace']:
     with Files(gcfile):
         BUG_COMPONENT = component_gc
 
-for stlfile in ['jsarray.*', 'jsbool*', 'jsdate.*', 'jsnum.*', 'jsstr.*']:
+for stlfile in ['jsarray.*', 'jsbool*', 'jsdate.*', 'jsnum.*']:
     with Files(stlfile):
         BUG_COMPONENT = component_stl
 
 with Files('builtin/intl/*'):
     BUG_COMPONENT = component_intl
 
 
 if CONFIG['JS_BUNDLED_EDITLINE']:
@@ -163,17 +163,18 @@ UNIFIED_SOURCES += [
     'builtin/ModuleObject.cpp',
     'builtin/Object.cpp',
     'builtin/Profilers.cpp',
     'builtin/Promise.cpp',
     'builtin/Reflect.cpp',
     'builtin/ReflectParse.cpp',
     'builtin/SIMD.cpp',
     'builtin/Stream.cpp',
-    'builtin/SymbolObject.cpp',
+    'builtin/String.cpp',
+    'builtin/Symbol.cpp',
     'builtin/TestingFunctions.cpp',
     'builtin/TypedObject.cpp',
     'builtin/WeakMapObject.cpp',
     'builtin/WeakSetObject.cpp',
     'devtools/sharkctl.cpp',
     'ds/Bitmap.cpp',
     'ds/LifoAlloc.cpp',
     'ds/MemoryProtectionExceptionHandler.cpp',
@@ -286,31 +287,33 @@ UNIFIED_SOURCES += [
     'jit/VMFunctions.cpp',
     'jit/WasmBCE.cpp',
     'jsapi.cpp',
     'jsbool.cpp',
     'jsdate.cpp',
     'jsexn.cpp',
     'jsfriendapi.cpp',
     'jsnum.cpp',
-    'jsstr.cpp',
     'perf/jsperf.cpp',
     'proxy/BaseProxyHandler.cpp',
     'proxy/CrossCompartmentWrapper.cpp',
     'proxy/DeadObjectProxy.cpp',
     'proxy/OpaqueCrossCompartmentWrapper.cpp',
     'proxy/Proxy.cpp',
     'proxy/ScriptedProxyHandler.cpp',
     'proxy/SecurityWrapper.cpp',
     'proxy/Wrapper.cpp',
     'threading/Mutex.cpp',
     'threading/ProtectedData.cpp',
     'util/AllocPolicy.cpp',
     'util/NativeStack.cpp',
     'util/Printf.cpp',
+    'util/StringBuffer.cpp',
+    'util/Text.cpp',
+    'util/Unicode.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
     'vm/AsyncFunction.cpp',
     'vm/AsyncIteration.cpp',
     'vm/BytecodeUtil.cpp',
     'vm/Caches.cpp',
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
@@ -352,29 +355,27 @@ UNIFIED_SOURCES += [
     'vm/SavedStacks.cpp',
     'vm/Scope.cpp',
     'vm/SelfHosting.cpp',
     'vm/Shape.cpp',
     'vm/SharedArrayObject.cpp',
     'vm/SharedImmutableStringsCache.cpp',
     'vm/Stack.cpp',
     'vm/Stopwatch.cpp',
-    'vm/String.cpp',
-    'vm/StringBuffer.cpp',
+    'vm/StringType.cpp',
     'vm/StructuredClone.cpp',
-    'vm/Symbol.cpp',
+    'vm/SymbolType.cpp',
     'vm/TaggedProto.cpp',
     'vm/Time.cpp',
     'vm/TypedArrayObject.cpp',
     'vm/TypeInference.cpp',
     'vm/UbiNode.cpp',
     'vm/UbiNodeCensus.cpp',
     'vm/UbiNodeShortestPaths.cpp',
     'vm/UnboxedObject.cpp',
-    'vm/Unicode.cpp',
     'vm/Value.cpp',
     'vm/Xdr.cpp',
     'wasm/AsmJS.cpp',
     'wasm/WasmBaselineCompile.cpp',
     'wasm/WasmBinaryIterator.cpp',
     'wasm/WasmBinaryToAST.cpp',
     'wasm/WasmBinaryToText.cpp',
     'wasm/WasmBuiltins.cpp',
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "NamespaceImports.h"
 
 #include "js/Wrapper.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 using namespace js;
 
 template <class Base>
 bool
 SecurityWrapper<Base>::enter(JSContext* cx, HandleObject wrapper, HandleId id,
                              Wrapper::Action act, bool mayThrow, bool* bp) const
 {
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -17,25 +17,26 @@
 #else
 #include <sys/wait.h>
 #include <unistd.h>
 #endif
 
 #include "jsapi.h"
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
-#include "jsstr.h"
 
+#include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "js/Conversions.h"
 #include "js/Wrapper.h"
 #include "shell/jsshell.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSObject.h"
-#include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
 
 #include "vm/JSObject-inl.h"
 
 #ifdef XP_WIN
 # ifndef PATH_MAX
 #  define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
 # endif
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -85,32 +85,33 @@
 #include "perf/jsperf.h"
 #include "shell/jsoptparse.h"
 #include "shell/jsshell.h"
 #include "shell/OSObject.h"
 #include "threading/ConditionVariable.h"
 #include "threading/ExclusiveData.h"
 #include "threading/LockGuard.h"
 #include "threading/Thread.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Monitor.h"
 #include "vm/MutexIDs.h"
 #include "vm/Printer.h"
 #include "vm/Shape.h"
 #include "vm/SharedArrayObject.h"
-#include "vm/StringBuffer.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 #include "wasm/WasmJS.h"
 
 #include "vm/ErrorObject-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/JSCompartment-inl.h"
--- a/js/src/shell/jsshell.cpp
+++ b/js/src/shell/jsshell.cpp
@@ -8,17 +8,17 @@
 
 #include "shell/jsshell.h"
 
 #include "mozilla/Sprintf.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
-#include "vm/StringBuffer.h"
+#include "util/StringBuffer.h"
 
 using namespace JS;
 
 namespace js {
 namespace shell {
 
 // Generate 'usage' and 'help' properties for the given object.
 // JS_DefineFunctionsWithHelp will define individual function objects with both
rename from js/src/vm/StringBuffer.cpp
rename to js/src/util/StringBuffer.cpp
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/util/StringBuffer.cpp
@@ -1,20 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "vm/StringBuffer.h"
+#include "util/StringBuffer.h"
 
 #include "mozilla/Range.h"
 
 #include "vm/JSObject-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using namespace js;
 
 template <typename CharT, class Buffer>
 static CharT*
 ExtractWellSized(JSContext* cx, Buffer& cb)
 {
     size_t capacity = cb.capacity();
rename from js/src/vm/StringBuffer.h
rename to js/src/util/StringBuffer.h
--- a/js/src/vm/StringBuffer.h
+++ b/js/src/util/StringBuffer.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef vm_StringBuffer_h
-#define vm_StringBuffer_h
+#ifndef util_StringBuffer_h
+#define util_StringBuffer_h
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MaybeOneOf.h"
 
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 namespace js {
@@ -341,9 +341,9 @@ ValueToStringBuffer(JSContext* cx, const
 inline bool
 BooleanToStringBuffer(bool b, StringBuffer& sb)
 {
     return b ? sb.append("true") : sb.append("false");
 }
 
 }  /* namespace js */
 
-#endif /* vm_StringBuffer_h */
+#endif /* util_StringBuffer_h */
new file mode 100644
--- /dev/null
+++ b/js/src/util/Text.cpp
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "util/Text.h"
+
+#include "mozilla/PodOperations.h"
+
+#include "gc/GC.h"
+#include "js/GCAPI.h"
+#include "vm/JSContext.h"
+#include "vm/StringType.h"
+
+using namespace JS;
+using namespace js;
+using js::gc::AutoSuppressGC;
+using mozilla::PodCopy;
+
+template <typename CharT>
+const CharT*
+js_strchr_limit(const CharT* s, char16_t c, const CharT* limit)
+{
+    while (s < limit) {
+        if (*s == c)
+            return s;
+        s++;
+    }
+    return nullptr;
+}
+
+template const Latin1Char*
+js_strchr_limit(const Latin1Char* s, char16_t c, const Latin1Char* limit);
+
+template const char16_t*
+js_strchr_limit(const char16_t* s, char16_t c, const char16_t* limit);
+
+JS_PUBLIC_API(char*)
+js_strdup(const char* s)
+{
+    return DuplicateString(s).release();
+}
+
+int32_t
+js_fputs(const char16_t* s, FILE* f)
+{
+    while (*s != 0) {
+        if (fputwc(wchar_t(*s), f) == WEOF)
+            return WEOF;
+        s++;
+    }
+    return 1;
+}
+
+UniqueChars
+js::DuplicateString(JSContext* cx, const char* s)
+{
+    size_t n = strlen(s) + 1;
+    auto ret = cx->make_pod_array<char>(n);
+    if (!ret)
+        return ret;
+    PodCopy(ret.get(), s, n);
+    return ret;
+}
+
+UniqueTwoByteChars
+js::DuplicateString(JSContext* cx, const char16_t* s)
+{
+    size_t n = js_strlen(s) + 1;
+    auto ret = cx->make_pod_array<char16_t>(n);
+    if (!ret)
+        return ret;
+    PodCopy(ret.get(), s, n);
+    return ret;
+}
+
+UniqueChars
+js::DuplicateString(const char* s)
+{
+    size_t n = strlen(s) + 1;
+    UniqueChars ret(js_pod_malloc<char>(n));
+    if (!ret)
+        return ret;
+    PodCopy(ret.get(), s, n);
+    return ret;
+}
+
+UniqueChars
+js::DuplicateString(const char* s, size_t n)
+{
+    UniqueChars ret(js_pod_malloc<char>(n + 1));
+    if (!ret)
+        return nullptr;
+    PodCopy(ret.get(), s, n);
+    ret[n] = 0;
+    return ret;
+}
+
+UniqueTwoByteChars
+js::DuplicateString(const char16_t* s)
+{
+    return DuplicateString(s, js_strlen(s));
+}
+
+UniqueTwoByteChars
+js::DuplicateString(const char16_t* s, size_t n)
+{
+    UniqueTwoByteChars ret(js_pod_malloc<char16_t>(n + 1));
+    if (!ret)
+        return nullptr;
+    PodCopy(ret.get(), s, n);
+    ret[n] = 0;
+    return ret;
+}
+
+char16_t*
+js::InflateString(JSContext* cx, const char* bytes, size_t length)
+{
+    char16_t* chars = cx->pod_malloc<char16_t>(length + 1);
+    if (!chars)
+        return nullptr;
+    CopyAndInflateChars(chars, bytes, length);
+    chars[length] = 0;
+    return chars;
+}
+
+template <typename CharT>
+bool
+js::DeflateStringToBuffer(JSContext* maybecx, const CharT* src, size_t srclen,
+                          char* dst, size_t* dstlenp)
+{
+    size_t dstlen = *dstlenp;
+    if (srclen > dstlen) {
+        for (size_t i = 0; i < dstlen; i++)
+            dst[i] = char(src[i]);
+        if (maybecx) {
+            AutoSuppressGC suppress(maybecx);
+            JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
+                                      JSMSG_BUFFER_TOO_SMALL);
+        }
+        return false;
+    }
+    for (size_t i = 0; i < srclen; i++)
+        dst[i] = char(src[i]);
+    *dstlenp = srclen;
+    return true;
+}
+
+template bool
+js::DeflateStringToBuffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
+                          char* dst, size_t* dstlenp);
+
+template bool
+js::DeflateStringToBuffer(JSContext* maybecx, const char16_t* src, size_t srclen,
+                          char* dst, size_t* dstlenp);
+
+/*
+ * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
+ * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
+ */
+uint32_t
+js::OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char)
+{
+    MOZ_ASSERT(ucs4Char <= unicode::NonBMPMax);
+
+    if (ucs4Char < 0x80) {
+        utf8Buffer[0] = uint8_t(ucs4Char);
+        return 1;
+    }
+
+    uint32_t a = ucs4Char >> 11;
+    uint32_t utf8Length = 2;
+    while (a) {
+        a >>= 5;
+        utf8Length++;
+    }
+
+    MOZ_ASSERT(utf8Length <= 4);
+
+    uint32_t i = utf8Length;
+    while (--i) {
+        utf8Buffer[i] = uint8_t((ucs4Char & 0x3F) | 0x80);
+        ucs4Char >>= 6;
+    }
+
+    utf8Buffer[0] = uint8_t(0x100 - (1 << (8 - utf8Length)) + ucs4Char);
+    return utf8Length;
+}
+
+size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, JSLinearString* str,
+                         uint32_t quote)
+{
+    size_t len = str->length();
+    AutoCheckCannotGC nogc;
+    return str->hasLatin1Chars()
+           ? PutEscapedStringImpl(buffer, bufferSize, out, str->latin1Chars(nogc), len, quote)
+           : PutEscapedStringImpl(buffer, bufferSize, out, str->twoByteChars(nogc), len, quote);
+}
+
+template <typename CharT>
+size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
+                         size_t length, uint32_t quote)
+{
+    enum {
+        STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
+    } state;
+
+    MOZ_ASSERT(quote == 0 || quote == '\'' || quote == '"');
+    MOZ_ASSERT_IF(!buffer, bufferSize == 0);
+    MOZ_ASSERT_IF(out, !buffer);
+
+    if (bufferSize == 0)
+        buffer = nullptr;
+    else
+        bufferSize--;
+
+    const CharT* charsEnd = chars + length;
+    size_t n = 0;
+    state = FIRST_QUOTE;
+    unsigned shift = 0;
+    unsigned hex = 0;
+    unsigned u = 0;
+    char c = 0;  /* to quell GCC warnings */
+
+    for (;;) {
+        switch (state) {
+          case STOP:
+            goto stop;
+          case FIRST_QUOTE:
+            state = CHARS;
+            goto do_quote;
+          case LAST_QUOTE:
+            state = STOP;
+          do_quote:
+            if (quote == 0)
+                continue;
+            c = (char)quote;
+            break;
+          case CHARS:
+            if (chars == charsEnd) {
+                state = LAST_QUOTE;
+                continue;
+            }
+            u = *chars++;
+            if (u < ' ') {
+                if (u != 0) {
+                    const char* escape = strchr(js_EscapeMap, (int)u);
+                    if (escape) {
+                        u = escape[1];
+                        goto do_escape;
+                    }
+                }
+                goto do_hex_escape;
+            }
+            if (u < 127) {
+                if (u == quote || u == '\\')
+                    goto do_escape;
+                c = (char)u;
+            } else if (u < 0x100) {
+                goto do_hex_escape;
+            } else {
+                shift = 16;
+                hex = u;
+                u = 'u';
+                goto do_escape;
+            }
+            break;
+          do_hex_escape:
+            shift = 8;
+            hex = u;
+            u = 'x';
+          do_escape:
+            c = '\\';
+            state = ESCAPE_START;
+            break;
+          case ESCAPE_START:
+            MOZ_ASSERT(' ' <= u && u < 127);
+            c = (char)u;
+            state = ESCAPE_MORE;
+            break;
+          case ESCAPE_MORE:
+            if (shift == 0) {
+                state = CHARS;
+                continue;
+            }
+            shift -= 4;
+            u = 0xF & (hex >> shift);
+            c = (char)(u + (u < 10 ? '0' : 'A' - 10));
+            break;
+        }
+        if (buffer) {
+            MOZ_ASSERT(n <= bufferSize);
+            if (n != bufferSize) {
+                buffer[n] = c;
+            } else {
+                buffer[n] = '\0';
+                buffer = nullptr;
+            }
+        } else if (out) {
+            if (!out->put(&c, 1))
+                return size_t(-1);
+        }
+        n++;
+    }
+  stop:
+    if (buffer)
+        buffer[n] = '\0';
+    return n;
+}
+
+template size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const Latin1Char* chars,
+                         size_t length, uint32_t quote);
+
+template size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char* chars,
+                         size_t length, uint32_t quote);
+
+template size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char16_t* chars,
+                         size_t length, uint32_t quote);
+
+template size_t
+js::PutEscapedString(char* buffer, size_t bufferSize, const Latin1Char* chars, size_t length,
+                     uint32_t quote);
+
+template size_t
+js::PutEscapedString(char* buffer, size_t bufferSize, const char16_t* chars, size_t length,
+                     uint32_t quote);
new file mode 100644
--- /dev/null
+++ b/js/src/util/Text.h
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef util_Text_h
+#define util_Text_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string>
+
+#include "jsutil.h"
+#include "NamespaceImports.h"
+
+#include "js/Utility.h"
+#include "util/Unicode.h"
+#include "vm/Printer.h"
+
+class JSLinearString;
+
+/*
+ * Shorthands for ASCII (7-bit) decimal and hex conversion.
+ * Manually inline isdigit and isxdigit for performance; MSVC doesn't do this for us.
+ */
+#define JS7_ISDEC(c)    ((((unsigned)(c)) - '0') <= 9)
+#define JS7_ISA2F(c)    ((((((unsigned)(c)) - 'a') <= 5) || (((unsigned)(c)) - 'A') <= 5))
+#define JS7_UNDEC(c)    ((c) - '0')
+#define JS7_ISOCT(c)    ((((unsigned)(c)) - '0') <= 7)
+#define JS7_UNOCT(c)    (JS7_UNDEC(c))
+#define JS7_ISHEX(c)    ((c) < 128 && (JS7_ISDEC(c) || JS7_ISA2F(c)))
+#define JS7_UNHEX(c)    (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a')
+#define JS7_ISLET(c)    ((c) < 128 && isalpha(c))
+
+static MOZ_ALWAYS_INLINE size_t
+js_strlen(const char16_t* s)
+{
+    return std::char_traits<char16_t>::length(s);
+}
+
+template <typename CharT>
+extern const CharT*
+js_strchr_limit(const CharT* s, char16_t c, const CharT* limit);
+
+extern int32_t
+js_fputs(const char16_t* s, FILE* f);
+
+namespace js {
+
+class StringBuffer;
+
+template <typename Char1, typename Char2>
+inline bool
+EqualChars(const Char1* s1, const Char2* s2, size_t len);
+
+template <typename Char1>
+inline bool
+EqualChars(const Char1* s1, const Char1* s2, size_t len)
+{
+    return mozilla::PodEqual(s1, s2, len);
+}
+
+template <typename Char1, typename Char2>
+inline bool
+EqualChars(const Char1* s1, const Char2* s2, size_t len)
+{
+    for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) {
+        if (*s1 != *s2)
+            return false;
+    }
+    return true;
+}
+
+// Return less than, equal to, or greater than zero depending on whether
+// s1 is less than, equal to, or greater than s2.
+template <typename Char1, typename Char2>
+inline int32_t
+CompareChars(const Char1* s1, size_t len1, const Char2* s2, size_t len2)
+{
+    size_t n = Min(len1, len2);
+    for (size_t i = 0; i < n; i++) {
+        if (int32_t cmp = s1[i] - s2[i])
+            return cmp;
+    }
+
+    return int32_t(len1 - len2);
+}
+
+// Return s advanced past any Unicode white space characters.
+template <typename CharT>
+static inline const CharT*
+SkipSpace(const CharT* s, const CharT* end)
+{
+    MOZ_ASSERT(s <= end);
+
+    while (s < end && unicode::IsSpace(*s))
+        s++;
+
+    return s;
+}
+
+extern UniqueChars
+DuplicateString(JSContext* cx, const char* s);
+
+extern UniqueTwoByteChars
+DuplicateString(JSContext* cx, const char16_t* s);
+
+/*
+ * These variants do not report OOMs, you must arrange for OOMs to be reported
+ * yourself.
+ */
+extern UniqueChars
+DuplicateString(const char* s);
+
+extern UniqueChars
+DuplicateString(const char* s, size_t n);
+
+extern UniqueTwoByteChars
+DuplicateString(const char16_t* s);
+
+extern UniqueTwoByteChars
+DuplicateString(const char16_t* s, size_t n);
+
+/*
+ * Inflate bytes in ASCII encoding to char16_t code units. Return null on error,
+ * otherwise return the char16_t buffer that was malloc'ed. A null char is
+ * appended.
+ */
+extern char16_t*
+InflateString(JSContext* cx, const char* bytes, size_t length);
+
+/*
+ * Inflate bytes to JS chars in an existing buffer. 'dst' must be large
+ * enough for 'srclen' char16_t code units. The buffer is NOT null-terminated.
+ */
+inline void
+CopyAndInflateChars(char16_t* dst, const char* src, size_t srclen)
+{
+    for (size_t i = 0; i < srclen; i++)
+        dst[i] = (unsigned char) src[i];
+}
+
+inline void
+CopyAndInflateChars(char16_t* dst, const JS::Latin1Char* src, size_t srclen)
+{
+    for (size_t i = 0; i < srclen; i++)
+        dst[i] = src[i];
+}
+
+/*
+ * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for
+ * 'length chars. The buffer is NOT null-terminated. The destination length
+ * must to be initialized with the buffer size and will contain on return the
+ * number of copied bytes.
+ */
+template <typename CharT>
+extern bool
+DeflateStringToBuffer(JSContext* maybecx, const CharT* chars,
+                      size_t charsLength, char* bytes, size_t* length);
+
+/*
+ * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
+ * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
+ */
+extern uint32_t
+OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char);
+
+extern size_t
+PutEscapedStringImpl(char* buffer, size_t size, GenericPrinter* out, JSLinearString* str,
+                     uint32_t quote);
+
+template <typename CharT>
+extern size_t
+PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
+                     size_t length, uint32_t quote);
+
+/*
+ * Write str into buffer escaping any non-printable or non-ASCII character
+ * using \escapes for JS string literals.
+ * Guarantees that a NUL is at the end of the buffer unless size is 0. Returns
+ * the length of the written output, NOT including the NUL. Thus, a return
+ * value of size or more means that the output was truncated. If buffer
+ * is null, just returns the length of the output. If quote is not 0, it must
+ * be a single or double quote character that will quote the output.
+*/
+inline size_t
+PutEscapedString(char* buffer, size_t size, JSLinearString* str, uint32_t quote)
+{
+    size_t n = PutEscapedStringImpl(buffer, size, nullptr, str, quote);
+
+    /* PutEscapedStringImpl can only fail with a file. */
+    MOZ_ASSERT(n != size_t(-1));
+    return n;
+}
+
+template <typename CharT>
+inline size_t
+PutEscapedString(char* buffer, size_t bufferSize, const CharT* chars, size_t length, uint32_t quote)
+{
+    size_t n = PutEscapedStringImpl(buffer, bufferSize, nullptr, chars, length, quote);
+
+    /* PutEscapedStringImpl can only fail with a file. */
+    MOZ_ASSERT(n != size_t(-1));
+    return n;
+}
+
+inline bool
+EscapedStringPrinter(GenericPrinter& out, JSLinearString* str, uint32_t quote)
+{
+    return PutEscapedStringImpl(nullptr, 0, &out, str, quote) != size_t(-1);
+}
+
+inline bool
+EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint32_t quote)
+{
+    return PutEscapedStringImpl(nullptr, 0, &out, chars, length, quote) != size_t(-1);
+}
+
+/*
+ * Write str into file escaping any non-printable or non-ASCII character.
+ * If quote is not 0, it must be a single or double quote character that
+ * will quote the output.
+*/
+inline bool
+FileEscapedString(FILE* fp, JSLinearString* str, uint32_t quote)
+{
+    Fprinter out(fp);
+    bool res = EscapedStringPrinter(out, str, quote);
+    out.finish();
+    return res;
+}
+
+inline bool
+FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
+{
+    Fprinter out(fp);
+    bool res = EscapedStringPrinter(out, chars, length, quote);
+    out.finish();
+    return res;
+}
+
+bool
+EncodeURI(JSContext* cx, StringBuffer& sb, const char* chars, size_t length);
+
+} // namespace js
+
+#endif // util_Text_h
rename from js/src/vm/Unicode.cpp
rename to js/src/util/Unicode.cpp
--- a/js/src/vm/Unicode.cpp
+++ b/js/src/util/Unicode.cpp
@@ -1,16 +1,16 @@
 /* Generated by make_unicode.py DO NOT MODIFY */
 /* Unicode version: 10.0.0 */
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
-#include "vm/Unicode.h"
+#include "util/Unicode.h"
 
 using namespace js;
 using namespace js::unicode;
 
 /*
  * So how does indexing work?
  * First let's have a look at a char16_t, 16-bits:
  *              [................]
rename from js/src/vm/Unicode.h
rename to js/src/util/Unicode.h
--- a/js/src/vm/Unicode.h
+++ b/js/src/util/Unicode.h
@@ -1,19 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef vm_Unicode_h
-#define vm_Unicode_h
+#ifndef util_Unicode_h
+#define util_Unicode_h
 
 #include "jspubtd.h"
-#include "vm/UnicodeNonBMP.h"
+
+#include "util/UnicodeNonBMP.h"
 
 namespace js {
 namespace unicode {
 
 extern const bool js_isidstart[];
 extern const bool js_isident[];
 extern const bool js_isspace[];
 
@@ -596,9 +597,9 @@ UTF16Decode(char16_t lead, char16_t trai
     MOZ_ASSERT(IsTrailSurrogate(trail));
 
     return (lead << 10) + trail + (NonBMPMin - (LeadSurrogateMin << 10) - TrailSurrogateMin);
 }
 
 } /* namespace unicode */
 } /* namespace js */
 
-#endif /* vm_Unicode_h */
+#endif /* util_Unicode_h */
rename from js/src/vm/UnicodeData.txt
rename to js/src/util/UnicodeData.txt
rename from js/src/vm/UnicodeNonBMP.h
rename to js/src/util/UnicodeNonBMP.h
--- a/js/src/vm/UnicodeNonBMP.h
+++ b/js/src/util/UnicodeNonBMP.h
@@ -2,18 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 /* Generated by make_unicode.py DO NOT MODIFY */
 /* Unicode version: 10.0.0 */
 
-#ifndef vm_UnicodeNonBMP_h
-#define vm_UnicodeNonBMP_h
+#ifndef util_UnicodeNonBMP_h
+#define util_UnicodeNonBMP_h
 
 // |macro| receives the following arguments
 //   macro(FROM, TO, LEAD, TRAIL_FROM, TRAIL_TO, DIFF)
 //     FROM:       code point where the range starts
 //     TO:         code point where the range ends
 //     LEAD:       common lead surrogate of FROM and TO
 //     TRAIL_FROM: trail surrogate of FROM
 //     TRAIL_FROM: trail surrogate of TO
@@ -63,9 +63,9 @@
 // U+1E922 ADLAM SMALL LETTER ALIF .. U+1E943 ADLAM SMALL LETTER SHA
 #define FOR_EACH_NON_BMP_REV_CASE_FOLDING(macro) \
     macro(0x10428, 0x1044f, 0xd801, 0xdc28, 0xdc4f, -40) \
     macro(0x104d8, 0x104fb, 0xd801, 0xdcd8, 0xdcfb, -40) \
     macro(0x10cc0, 0x10cf2, 0xd803, 0xdcc0, 0xdcf2, -64) \
     macro(0x118c0, 0x118df, 0xd806, 0xdcc0, 0xdcdf, -32) \
     macro(0x1e922, 0x1e943, 0xd83a, 0xdd22, 0xdd43, -34)
 
-#endif /* vm_UnicodeNonBMP_h */
+#endif /* util_UnicodeNonBMP_h */
rename from js/src/vm/make_unicode.py
rename to js/src/util/make_unicode.py
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_ArrayObject_inl_h
 #define vm_ArrayObject_inl_h
 
 #include "vm/ArrayObject.h"
 
 #include "gc/GCTrace.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/TypeInference-inl.h"
 
 namespace js {
 
 inline void
 ArrayObject::setLength(JSContext* cx, uint32_t length)
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -20,37 +20,37 @@
 #include <algorithm>
 #include <ctype.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsnum.h"
-#include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
+#include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/SourceNotes.h"
 #include "gc/FreeOp.h"
 #include "gc/GCInternals.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
+#include "util/StringBuffer.h"
 #include "vm/CodeCoverage.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
-#include "vm/StringBuffer.h"
 
 #include "gc/PrivateIterators-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 
 using namespace js;
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -13,16 +13,17 @@
 #include <stdio.h>
 #ifdef XP_WIN
 # include <process.h>
 # define getpid _getpid
 #else
 # include <unistd.h>
 #endif
 
+#include "util/Text.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSScript.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 
 // This file contains a few functions which are used to produce files understood
 // by lcov tools. A detailed description of the format is available in the man
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -9,17 +9,17 @@
 
 #include "mozilla/Range.h"
 
 #include "jsexn.h"
 
 #include "js/CallArgs.h"
 #include "js/CharacterEncoding.h"
 #include "vm/GlobalObject.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/SavedStacks-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -11,18 +11,18 @@
 #include "jsnum.h"
 
 #include "gc/PublicIterators.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitFrames.h"
 #include "jit/JSJitFrameIter.h"
+#include "util/StringBuffer.h"
 #include "vm/JSScript.h"
-#include "vm/StringBuffer.h"
 
 #include "gc/Marking-inl.h"
 
 using namespace js;
 
 using mozilla::DebugOnly;
 
 GeckoProfilerThread::GeckoProfilerThread()
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -17,17 +17,17 @@
 #include "builtin/JSON.h"
 #include "builtin/MapObject.h"
 #include "builtin/ModuleObject.h"
 #include "builtin/Object.h"
 #include "builtin/Promise.h"
 #include "builtin/RegExp.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/Stream.h"
-#include "builtin/SymbolObject.h"
+#include "builtin/Symbol.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakMapObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/FreeOp.h"
 #include "js/ProtoKey.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/HelperThreads.h"
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -5,27 +5,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_Interpreter_inl_h
 #define vm_Interpreter_inl_h
 
 #include "vm/Interpreter.h"
 
 #include "jsnum.h"
-#include "jsstr.h"
 
+#include "builtin/String.h"
 #include "jit/Ion.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/JSCompartment.h"
 
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/Stack-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 namespace js {
 
 /*
  * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
  * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
  * one is received, act as if the value were the function's ArgumentsObject.
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -15,40 +15,41 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/Sprintf.h"
 
 #include <string.h>
 
 #include "jsarray.h"
 #include "jslibmath.h"
 #include "jsnum.h"
-#include "jsstr.h"
 
 #include "builtin/Eval.h"
+#include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/Jit.h"
+#include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Opcodes.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
 #include "vm/Stopwatch.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/GeckoProfiler-inl.h"
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -32,17 +32,17 @@
 #include "vm/JSScript.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayObject.h"
 
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/ReceiverGuard-inl.h"
 #include "vm/Stack-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
--- a/js/src/vm/JSAtom-inl.h
+++ b/js/src/vm/JSAtom-inl.h
@@ -9,17 +9,17 @@
 
 #include "vm/JSAtom.h"
 
 #include "mozilla/RangedPtr.h"
 
 #include "jsnum.h"
 
 #include "vm/Runtime.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 
 inline jsid
 AtomToId(JSAtom* atom)
 {
     JS_STATIC_ASSERT(JSID_INT_MIN == 0);
 
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -12,29 +12,30 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Unused.h"
 
 #include <string.h>
 
-#include "jsstr.h"
 #include "jstypes.h"
 
+#include "builtin/String.h"
 #include "gc/Marking.h"
+#include "util/Text.h"
 #include "vm/JSContext.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 #include "vm/Xdr.h"
 
 #include "gc/AtomMarking-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 using mozilla::Nothing;
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -11,17 +11,17 @@
 
 #include "builtin/Object.h"
 #include "jit/JitFrames.h"
 #include "proxy/Proxy.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSCompartment.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
 namespace js {
 
 class CompartmentChecker
 {
     JSCompartment* compartment;
 
   public:
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -25,19 +25,19 @@
 # include <string>
 #endif // ANDROID
 #ifdef XP_WIN
 # include <processthreadsapi.h>
 #endif // XP_WIN
 
 #include "jsexn.h"
 #include "jspubtd.h"
-#include "jsstr.h"
 #include "jstypes.h"
 
+#include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/PcScriptCache.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "util/DoubleToString.h"
 #include "util/NativeStack.h"
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -14,44 +14,44 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Range.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
-#include "jsstr.h"
 #include "jstypes.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/SelfHostingDefines.h"
+#include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/Proxy.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
 #include "vm/SharedImmutableStringsCache.h"
-#include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 #include "wasm/AsmJS.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/Stack-inl.h"
 
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -10,18 +10,18 @@
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Sprintf.h"
 
 #include <ctype.h>
 
 #include "jsarray.h"
 #include "jsnum.h"
 
+#include "util/StringBuffer.h"
 #include "vm/JSCompartment.h"
-#include "vm/StringBuffer.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::RangedPtr;
 
 JSONParserBase::~JSONParserBase()
--- a/js/src/vm/JSONParser.h
+++ b/js/src/vm/JSONParser.h
@@ -8,17 +8,17 @@
 #define vm_JSONParser_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Range.h"
 
 #include "jspubtd.h"
 
 #include "ds/IdValuePair.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 
 // JSONParser base class. JSONParser is templatized to work on either Latin1
 // or TwoByte input strings, JSONParserBase holds all state and methods that
 // can be shared between the two encodings.
 class MOZ_STACK_CLASS JSONParserBase
 {
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -17,31 +17,32 @@
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
-#include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
-#include "builtin/SymbolObject.h"
+#include "builtin/String.h"
+#include "builtin/Symbol.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/Proxy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
+#include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -10,17 +10,17 @@
 #include "mozilla/MemoryReporting.h"
 
 #include "gc/Barrier.h"
 #include "js/Conversions.h"
 #include "js/GCVector.h"
 #include "js/HeapAPI.h"
 #include "vm/Printer.h"
 #include "vm/Shape.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 #include "vm/Xdr.h"
 
 namespace JS {
 struct ClassInfo;
 } // namespace JS
 
 namespace js {
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -31,29 +31,30 @@
 #include "gc/FreeOp.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonCode.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/Opcodes.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
 #include "vm/SharedImmutableStringsCache.h"
-#include "vm/StringBuffer.h"
 #include "vm/Xdr.h"
 #include "vtune/VTuneWrapper.h"
 
 #include "gc/Marking-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSFunction-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -13,18 +13,18 @@
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
-#include "vm/String.h"
-#include "vm/Symbol.h"
+#include "vm/StringType.h"
+#include "vm/SymbolType.h"
 #include "vm/WrapperObject.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmModule.h"
 
 using mozilla::MallocSizeOf;
 using mozilla::Move;
 using mozilla::PodCopy;
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -17,17 +17,17 @@
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/JSObject.h"
 #include "vm/Shape.h"
 #include "vm/ShapedObject.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 #include "vm/TypeInference.h"
 
 namespace js {
 
 class Shape;
 class TenuringTracer;
 
 /*
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -4,34 +4,35 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/RegExpObject.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
-#include "jsstr.h"
+#include "builtin/String.h"
 #ifdef DEBUG
 #include "jsutil.h"
 #endif
 
 #include "builtin/RegExp.h"
 #include "frontend/TokenStream.h"
 #include "gc/HashUtil.h"
 #ifdef DEBUG
 #include "irregexp/RegExpBytecode.h"
 #endif
 #include "irregexp/RegExpParser.h"
+#include "util/StringBuffer.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #ifdef DEBUG
-#include "vm/Unicode.h"
+#include "util/Unicode.h"
 #endif
 #include "vm/Xdr.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -44,17 +44,17 @@
 #include "vm/DateTime.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/JSAtom.h"
 #include "vm/JSScript.h"
 #include "vm/Scope.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Stack.h"
 #include "vm/Stopwatch.h"
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 #include "wasm/WasmSignalHandlers.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
 namespace js {
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -21,22 +21,22 @@
 
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
 #include "js/Vector.h"
+#include "util/StringBuffer.h"
 #include "vm/Debugger.h"
 #include "vm/GeckoProfiler.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSScript.h"
 #include "vm/SavedFrame.h"
-#include "vm/StringBuffer.h"
 #include "vm/Time.h"
 #include "vm/WrapperObject.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
 
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -4,19 +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/. */
 
 #include "vm/Scope.h"
 
 #include "builtin/ModuleObject.h"
 #include "gc/Allocator.h"
 #include "gc/FreeOp.h"
+#include "util/StringBuffer.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSScript.h"
-#include "vm/StringBuffer.h"
 #include "wasm/WasmInstance.h"
 
 #include "gc/ObjectKind-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using mozilla::Maybe;
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 
 #include "jsarray.h"
 #include "jsdate.h"
 #include "jsfriendapi.h"
-#include "jsstr.h"
 #include "selfhosted.out.h"
 
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/IntlObject.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/RelativeTimeFormat.h"
@@ -27,38 +26,39 @@
 #include "builtin/ModuleObject.h"
 #include "builtin/Object.h"
 #include "builtin/Promise.h"
 #include "builtin/Reflect.h"
 #include "builtin/RegExp.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/SIMD.h"
 #include "builtin/Stream.h"
+#include "builtin/String.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakMapObject.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/CharacterEncoding.h"
 #include "js/Date.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/Printer.h"
 #include "vm/RegExpObject.h"
-#include "vm/String.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "gc/PrivateIterators-inl.h"
 #include "vm/BooleanObject-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSFunction-inl.h"
 #include "vm/JSObject-inl.h"
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/PodOperations.h"
 
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "js/HashTable.h"
+#include "util/Text.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 
 #include "vm/Caches-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -25,18 +25,18 @@
 #include "gc/Rooting.h"
 #include "js/HashTable.h"
 #include "js/MemoryMetrics.h"
 #include "js/RootingAPI.h"
 #include "js/UbiNode.h"
 #include "vm/JSAtom.h"
 #include "vm/ObjectGroup.h"
 #include "vm/Printer.h"
-#include "vm/String.h"
-#include "vm/Symbol.h"
+#include "vm/StringType.h"
+#include "vm/SymbolType.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4800)
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
 
--- a/js/src/vm/SharedImmutableStringsCache.cpp
+++ b/js/src/vm/SharedImmutableStringsCache.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/SharedImmutableStringsCache-inl.h"
 
-#include "jsstr.h"
+#include "builtin/String.h"
+#include "util/Text.h"
 
 namespace js {
 
 SharedImmutableString::SharedImmutableString(
     ExclusiveData<SharedImmutableStringsCache::Inner>::Guard& locked,
     SharedImmutableStringsCache::StringBox* box)
   : cache_(locked)
   , box_(box)
--- a/js/src/vm/SharedImmutableStringsCache.h
+++ b/js/src/vm/SharedImmutableStringsCache.h
@@ -8,17 +8,17 @@
 #define vm_SharedImmutableStringsCache_h
 
 #include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 
 #include <cstring>
 #include <new> // for placement new
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "js/HashTable.h"
 #include "js/Utility.h"
 
 #include "threading/ExclusiveData.h"
 
 #include "vm/MutexIDs.h"
 
--- a/js/src/vm/StringObject.h
+++ b/js/src/vm/StringObject.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_StringObject_h
 #define vm_StringObject_h
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "vm/JSObject.h"
 #include "vm/Shape.h"
 
 namespace js {
 
 class StringObject : public NativeObject
 {
rename from js/src/vm/String-inl.h
rename to js/src/vm/StringType-inl.h
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef vm_String_inl_h
-#define vm_String_inl_h
+#ifndef vm_StringType_inl_h
+#define vm_StringType_inl_h
 
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/Range.h"
 
 #include "gc/Allocator.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "vm/JSCompartment.h"
@@ -423,9 +423,9 @@ JSExternalString::finalize(js::FreeOp* f
         fop->free_(nonInlineCharsRaw());
         return;
     }
 
     const JSStringFinalizer* fin = externalFinalizer();
     fin->finalize(fin, const_cast<char16_t*>(rawTwoByteChars()));
 }
 
-#endif /* vm_String_inl_h */
+#endif /* vm_StringType_inl_h */
rename from js/src/vm/String.cpp
rename to js/src/vm/StringType.cpp
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/StringType.cpp
@@ -1,34 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "vm/String-inl.h"
+#include "vm/StringType-inl.h"
 
+#include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include "gc/Marking.h"
 #include "gc/Nursery.h"
 #include "js/UbiNode.h"
+#include "util/StringBuffer.h"
 #include "vm/GeckoProfiler.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
+using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::RangedPtr;
 using mozilla::RoundUpPow2;
 using mozilla::Unused;
 
 using JS::AutoCheckCannotGC;
@@ -751,16 +755,175 @@ JSDependentString::dumpRepresentation(js
     if (mozilla::Maybe<size_t> offset = baseOffset())
         out.printf("%*soffset: %zu\n", indent, "", *offset);
 
     out.printf("%*sbase: ", indent, "");
     base()->dumpRepresentation(out, indent);
 }
 #endif
 
+bool
+js::EqualChars(JSLinearString* str1, JSLinearString* str2)
+{
+    MOZ_ASSERT(str1->length() == str2->length());
+
+    size_t len = str1->length();
+
+    AutoCheckCannotGC nogc;
+    if (str1->hasTwoByteChars()) {
+        if (str2->hasTwoByteChars())
+            return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len);
+
+        return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
+    }
+
+    if (str2->hasLatin1Chars())
+        return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
+
+    return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
+}
+
+bool
+js::HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start)
+{
+    MOZ_ASSERT(start + pat->length() <= text->length());
+
+    size_t patLen = pat->length();
+
+    AutoCheckCannotGC nogc;
+    if (text->hasLatin1Chars()) {
+        const Latin1Char* textChars = text->latin1Chars(nogc) + start;
+        if (pat->hasLatin1Chars())
+            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
+
+        return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
+    }
+
+    const char16_t* textChars = text->twoByteChars(nogc) + start;
+    if (pat->hasTwoByteChars())
+        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
+
+    return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
+}
+
+bool
+js::EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result)
+{
+    if (str1 == str2) {
+        *result = true;
+        return true;
+    }
+
+    size_t length1 = str1->length();
+    if (length1 != str2->length()) {
+        *result = false;
+        return true;
+    }
+
+    JSLinearString* linear1 = str1->ensureLinear(cx);
+    if (!linear1)
+        return false;
+    JSLinearString* linear2 = str2->ensureLinear(cx);
+    if (!linear2)
+        return false;
+
+    *result = EqualChars(linear1, linear2);
+    return true;
+}
+
+bool
+js::EqualStrings(JSLinearString* str1, JSLinearString* str2)
+{
+    if (str1 == str2)
+        return true;
+
+    size_t length1 = str1->length();
+    if (length1 != str2->length())
+        return false;
+
+    return EqualChars(str1, str2);
+}
+
+int32_t
+js::CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2)
+{
+    AutoCheckCannotGC nogc;
+    return s2->hasLatin1Chars()
+           ? CompareChars(s1, len1, s2->latin1Chars(nogc), s2->length())
+           : CompareChars(s1, len1, s2->twoByteChars(nogc), s2->length());
+}
+
+static int32_t
+CompareStringsImpl(JSLinearString* str1, JSLinearString* str2)
+{
+    size_t len1 = str1->length();
+    size_t len2 = str2->length();
+
+    AutoCheckCannotGC nogc;
+    if (str1->hasLatin1Chars()) {
+        const Latin1Char* chars1 = str1->latin1Chars(nogc);
+        return str2->hasLatin1Chars()
+               ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
+               : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
+    }
+
+    const char16_t* chars1 = str1->twoByteChars(nogc);
+    return str2->hasLatin1Chars()
+           ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
+           : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
+}
+
+bool
+js::CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result)
+{
+    MOZ_ASSERT(str1);
+    MOZ_ASSERT(str2);
+
+    if (str1 == str2) {
+        *result = 0;
+        return true;
+    }
+
+    JSLinearString* linear1 = str1->ensureLinear(cx);
+    if (!linear1)
+        return false;
+
+    JSLinearString* linear2 = str2->ensureLinear(cx);
+    if (!linear2)
+        return false;
+
+    *result = CompareStringsImpl(linear1, linear2);
+    return true;
+}
+
+int32_t
+js::CompareAtoms(JSAtom* atom1, JSAtom* atom2)
+{
+    return CompareStringsImpl(atom1, atom2);
+}
+
+bool
+js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes)
+{
+    size_t length = strlen(asciiBytes);
+#ifdef DEBUG
+    for (size_t i = 0; i != length; ++i)
+        MOZ_ASSERT(unsigned(asciiBytes[i]) <= 127);
+#endif
+    if (length != str->length())
+        return false;
+
+    const Latin1Char* latin1 = reinterpret_cast<const Latin1Char*>(asciiBytes);
+
+    AutoCheckCannotGC nogc;
+    return str->hasLatin1Chars()
+           ? PodEqual(latin1, str->latin1Chars(nogc), length)
+           : EqualChars(latin1, str->twoByteChars(nogc), length);
+}
+
 template <typename CharT>
 /* static */ bool
 JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp)
 {
     CharT ch = *s;
 
     if (!JS7_ISDEC(ch))
         return false;
@@ -1727,8 +1890,151 @@ JSString::fillWithRepresentatives(JSCont
                                  CheckLatin1))
     {
         return false;
     }
 
     MOZ_ASSERT(index == 22);
     return true;
 }
+
+
+/*** Conversions *********************************************************************************/
+
+const char*
+js::ValueToPrintable(JSContext* cx, const Value& vArg, JSAutoByteString* bytes, bool asSource)
+{
+    RootedValue v(cx, vArg);
+    JSString* str;
+    if (asSource)
+        str = ValueToSource(cx, v);
+    else
+        str = ToString<CanGC>(cx, v);
+    if (!str)
+        return nullptr;
+    str = QuoteString(cx, str, 0);
+    if (!str)
+        return nullptr;
+    return bytes->encodeLatin1(cx, str);
+}
+
+template <AllowGC allowGC>
+JSString*
+js::ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
+{
+    /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
+    MOZ_ASSERT(!arg.isString());
+
+    Value v = arg;
+    if (!v.isPrimitive()) {
+        MOZ_ASSERT(!cx->helperThread());
+        if (!allowGC)
+            return nullptr;
+        RootedValue v2(cx, v);
+        if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
+            return nullptr;
+        v = v2;
+    }
+
+    JSString* str;
+    if (v.isString()) {
+        str = v.toString();
+    } else if (v.isInt32()) {
+        str = Int32ToString<allowGC>(cx, v.toInt32());
+    } else if (v.isDouble()) {
+        str = NumberToString<allowGC>(cx, v.toDouble());
+    } else if (v.isBoolean()) {
+        str = BooleanToString(cx, v.toBoolean());
+    } else if (v.isNull()) {
+        str = cx->names().null;
+    } else if (v.isSymbol()) {
+        MOZ_ASSERT(!cx->helperThread());
+        if (allowGC) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                      JSMSG_SYMBOL_TO_STRING);
+        }
+        return nullptr;
+    } else {
+        MOZ_ASSERT(v.isUndefined());
+        str = cx->names().undefined;
+    }
+    return str;
+}
+
+template JSString*
+js::ToStringSlow<CanGC>(JSContext* cx, HandleValue arg);
+
+template JSString*
+js::ToStringSlow<NoGC>(JSContext* cx, const Value& arg);
+
+JS_PUBLIC_API(JSString*)
+js::ToStringSlow(JSContext* cx, HandleValue v)
+{
+    return ToStringSlow<CanGC>(cx, v);
+}
+
+static JSString*
+SymbolToSource(JSContext* cx, Symbol* symbol)
+{
+    RootedString desc(cx, symbol->description());
+    SymbolCode code = symbol->code();
+    if (code != SymbolCode::InSymbolRegistry && code != SymbolCode::UniqueSymbol) {
+        // Well-known symbol.
+        MOZ_ASSERT(uint32_t(code) < JS::WellKnownSymbolLimit);
+        return desc;
+    }
+
+    StringBuffer buf(cx);
+    if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(") : !buf.append("Symbol("))
+        return nullptr;
+    if (desc) {
+        desc = StringToSource(cx, desc);
+        if (!desc || !buf.append(desc))
+            return nullptr;
+    }
+    if (!buf.append(')'))
+        return nullptr;
+    return buf.finishString();
+}
+
+JSString*
+js::ValueToSource(JSContext* cx, HandleValue v)
+{
+    if (!CheckRecursionLimit(cx))
+        return nullptr;
+    assertSameCompartment(cx, v);
+
+    if (v.isUndefined())
+        return cx->names().void0;
+    if (v.isString())
+        return StringToSource(cx, v.toString());
+    if (v.isSymbol())
+        return SymbolToSource(cx, v.toSymbol());
+    if (v.isPrimitive()) {
+        /* Special case to preserve negative zero, _contra_ toString. */
+        if (v.isDouble() && IsNegativeZero(v.toDouble())) {
+            static const Latin1Char negativeZero[] = {'-', '0'};
+
+            return NewStringCopyN<CanGC>(cx, negativeZero, mozilla::ArrayLength(negativeZero));
+        }
+        return ToString<CanGC>(cx, v);
+    }
+
+    RootedValue fval(cx);
+    RootedObject obj(cx, &v.toObject());
+    if (!GetProperty(cx, obj, obj, cx->names().toSource, &fval))
+        return nullptr;
+    if (IsCallable(fval)) {
+        RootedValue v(cx);
+        if (!js::Call(cx, fval, obj, &v))
+            return nullptr;
+
+        return ToString<CanGC>(cx, v);
+    }
+
+    return ObjectToSource(cx, obj);
+}
+
+JSString*
+js::StringToSource(JSContext* cx, JSString* str)
+{
+    return QuoteString(cx, str, '"');
+}
rename from js/src/vm/String.h
rename to js/src/vm/StringType.h
--- a/js/src/vm/String.h
+++ b/js/src/vm/StringType.h
@@ -1,33 +1,33 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef vm_String_h
-#define vm_String_h
+#ifndef vm_StringType_h
+#define vm_StringType_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Range.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "jsstr.h"
 
+#include "builtin/String.h"
 #include "gc/Barrier.h"
 #include "gc/Cell.h"
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
 #include "js/RootingAPI.h"
-
+#include "util/Text.h"
 #include "vm/Printer.h"
 
 class JSDependentString;
 class JSExtensibleString;
 class JSExternalString;
 class JSInlineString;
 class JSRope;
 
@@ -1486,16 +1486,147 @@ NewStringCopyUTF8Z(JSContext* cx, const 
 }
 
 JSString*
 NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n, const JSStringFinalizer* fin,
                        bool* allocatedExternal);
 
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
 
+template <AllowGC allowGC>
+extern JSString*
+ConcatStrings(JSContext* cx,
+              typename MaybeRooted<JSString*, allowGC>::HandleType left,
+              typename MaybeRooted<JSString*, allowGC>::HandleType right);
+
+/*
+ * Test if strings are equal. The caller can call the function even if str1
+ * or str2 are not GC-allocated things.
+ */
+extern bool
+EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result);
+
+/* Use the infallible method instead! */
+extern bool
+EqualStrings(JSContext* cx, JSLinearString* str1, JSLinearString* str2, bool* result) = delete;
+
+/* EqualStrings is infallible on linear strings. */
+extern bool
+EqualStrings(JSLinearString* str1, JSLinearString* str2);
+
+/**
+ * Compare two strings that are known to be the same length.
+ * Exposed for the JITs; for ordinary uses, EqualStrings() is more sensible.
+ *
+ * Precondition: str1->length() == str2->length().
+ */
+extern bool
+EqualChars(JSLinearString* str1, JSLinearString* str2);
+
+/*
+ * Return less than, equal to, or greater than zero depending on whether
+ * `s1[0..len1]` is less than, equal to, or greater than `s2`.
+ */
+extern int32_t
+CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2);
+
+/*
+ * Compare two strings, like CompareChars, but store the result in `*result`.
+ * This flattens the strings and therefore can fail.
+ */
+extern bool
+CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
+
+/*
+ * Same as CompareStrings but for atoms.  Don't use this to just test
+ * for equality; use this when you need an ordering on atoms.
+ */
+extern int32_t
+CompareAtoms(JSAtom* atom1, JSAtom* atom2);
+
+/*
+ * Return true if the string matches the given sequence of ASCII bytes.
+ */
+extern bool
+StringEqualsAscii(JSLinearString* str, const char* asciiBytes);
+
+extern int
+StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start);
+
+/**
+ * Return true if the string contains a pattern at |start|.
+ *
+ * Precondition: `text` is long enough that this might be true;
+ * that is, it has at least `start + pat->length()` characters.
+ */
+extern bool
+HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start);
+
+/*
+ * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt).
+ * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden
+ * and constitute API misuse.
+ */
+JSString*
+SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengthInt);
+
+
+/*** Conversions *********************************************************************************/
+
+/*
+ * Convert a value to a printable C string.
+ */
+extern const char*
+ValueToPrintable(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false);
+
+/*
+ * Convert a non-string value to a string, returning null after reporting an
+ * error, otherwise returning a new string reference.
+ */
+template <AllowGC allowGC>
+extern JSString*
+ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg);
+
+/*
+ * Convert the given value to a string.  This method includes an inline
+ * fast-path for the case where the value is already a string; if the value is
+ * known not to be a string, use ToStringSlow instead.
+ */
+template <AllowGC allowGC>
+static MOZ_ALWAYS_INLINE JSString*
+ToString(JSContext* cx, JS::HandleValue v)
+{
+    if (v.isString())
+        return v.toString();
+    return ToStringSlow<allowGC>(cx, v);
+}
+
+/*
+ * This function implements E-262-3 section 9.8, toString. Convert the given
+ * value to a string of characters appended to the given buffer. On error, the
+ * passed buffer may have partial results appended.
+ */
+inline bool
+ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb);
+
+/*
+ * Convert a value to its source expression, returning null after reporting
+ * an error, otherwise returning a new string reference.
+ */
+extern JSString*
+ValueToSource(JSContext* cx, HandleValue v);
+
+/*
+ * Convert a JSString to its source expression; returns null after reporting an
+ * error, otherwise returns a new string reference. No Handle needed since the
+ * input is dead after the GC.
+ */
+extern JSString*
+StringToSource(JSContext* cx, JSString* str);
+
 } /* namespace js */
 
 // Addon IDs are interned atoms which are never destroyed. This detail is
 // not exposed outside the API.
 class JSAddonId : public JSAtom
 {};
 
 MOZ_ALWAYS_INLINE bool
@@ -1706,9 +1837,9 @@ template<>
 inline JSString*
 TenuredCell::as<JSString>() {
     MOZ_ASSERT(is<JSString>());
     return reinterpret_cast<JSString*>(this);
 }
 }
 }
 
-#endif /* vm_String_h */
+#endif /* vm_StringType_h */
rename from js/src/vm/Symbol.cpp
rename to js/src/vm/SymbolType.cpp
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/SymbolType.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "vm/Symbol.h"
+#include "vm/SymbolType.h"
 
-#include "builtin/SymbolObject.h"
+#include "builtin/Symbol.h"
 #include "gc/Allocator.h"
 #include "gc/Rooting.h"
+#include "util/StringBuffer.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
-#include "vm/StringBuffer.h"
 
 #include "vm/JSCompartment-inl.h"
 
 using JS::Symbol;
 using namespace js;
 
 Symbol*
 Symbol::newInternal(JSContext* cx, JS::SymbolCode code, uint32_t hash, JSAtom* description,
rename from js/src/vm/Symbol.h
rename to js/src/vm/SymbolType.h
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/SymbolType.h
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef vm_Symbol_h
-#define vm_Symbol_h
+#ifndef vm_SymbolType_h
+#define vm_SymbolType_h
 
 #include "mozilla/Attributes.h"
 
 #include <stdio.h>
 
 #include "jsapi.h"
 
 #include "gc/Barrier.h"
 #include "gc/Tracer.h"
 #include "js/AllocPolicy.h"
 #include "js/GCHashTable.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "vm/Printer.h"
-#include "vm/String.h"
+#include "vm/StringType.h"
 
 namespace js {
 class AutoLockForExclusiveAccess;
 } // namespace js
 
 namespace JS {
 
 class Symbol : public js::gc::TenuredCell
@@ -142,9 +142,9 @@ class SymbolRegistry : public GCHashSet<
 };
 
 // ES6 rev 27 (2014 Aug 24) 19.4.3.3
 bool
 SymbolDescriptiveString(JSContext* cx, JS::Symbol* sym, JS::MutableHandleValue result);
 
 } /* namespace js */
 
-#endif /* vm_Symbol_h */
+#endif /* vm_SymbolType_h */
--- a/js/src/vm/TraceLoggingGraph.cpp
+++ b/js/src/vm/TraceLoggingGraph.cpp
@@ -12,21 +12,22 @@
 #else
 #include <unistd.h>
 #endif
 
 #include "mozilla/EndianUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ScopeExit.h"
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "js/UniquePtr.h"
 #include "threading/LockGuard.h"
 #include "threading/Thread.h"
+#include "util/Text.h"
 #include "vm/TraceLogging.h"
 
 #ifndef DEFAULT_TRACE_LOG_DIR
 # if defined(_WIN32)
 #  define DEFAULT_TRACE_LOG_DIR "."
 # else
 #  define DEFAULT_TRACE_LOG_DIR "/tmp/"
 # endif
--- a/js/src/vm/TraceLoggingTypes.cpp
+++ b/js/src/vm/TraceLoggingTypes.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/TraceLoggingTypes.h"
 
+#include "vm/StringType.h"
+
 class JSLinearString;
 
 uint32_t
 TLStringToTextId(JSLinearString* str)
 {
 #define NAME(textId) if (js::StringEqualsAscii(str, #textId)) return TraceLogger_ ## textId;
     TRACELOGGER_TREE_ITEMS(NAME)
     TRACELOGGER_LOG_ITEMS(NAME)
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TraceLoggingTypes_h
 #define TraceLoggingTypes_h
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "js/AllocPolicy.h"
 
 // Tree items, meaning they have a start and stop and form a nested tree.
 #define TRACELOGGER_TREE_ITEMS(_)                     \
     _(AnnotateScripts)                                \
     _(Baseline)                                       \
     _(BaselineCompilation)                            \
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -10,17 +10,17 @@
 #define vm_TypeInference_inl_h
 
 #include "vm/TypeInference.h"
 
 #include "mozilla/BinarySearch.h"
 #include "mozilla/Casting.h"
 #include "mozilla/PodOperations.h"
 
-#include "builtin/SymbolObject.h"
+#include "builtin/Symbol.h"
 #include "gc/GC.h"
 #include "jit/BaselineJIT.h"
 #include "vm/ArrayObject.h"
 #include "vm/BooleanObject.h"
 #include "vm/NumberObject.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -8,17 +8,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Sprintf.h"
 
 #include "jsapi.h"
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "gc/HashUtil.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CompileInfo.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/JitCompartment.h"
 #include "jit/OptimizationTracking.h"
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -8,34 +8,35 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Range.h"
 #include "mozilla/Scoped.h"
 
 #include <algorithm>
 
-#include "jsstr.h"
+#include "builtin/String.h"
 
 #include "jit/IonCode.h"
 #include "js/Debug.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
+#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
-#include "vm/String.h"
-#include "vm/Symbol.h"
+#include "vm/StringType.h"
+#include "vm/SymbolType.h"
 
 #include "vm/Debugger-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 
 using mozilla::RangedPtr;
 using JS::DispatchTyped;
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/UbiNodeCensus.h"
 
+#include "util/Text.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
--- a/js/src/vm/UbiNodeShortestPaths.cpp
+++ b/js/src/vm/UbiNodeShortestPaths.cpp
@@ -4,17 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/UbiNodeShortestPaths.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 
-#include "jsstr.h"
+#include "builtin/String.h"
+#include "util/Text.h"
 
 namespace JS {
 namespace ubi {
 
 JS_PUBLIC_API(BackEdge::Ptr)
 BackEdge::clone() const
 {
     BackEdge::Ptr clone(js_new<BackEdge>());
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -19,29 +19,30 @@
 #include "wasm/AsmJS.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Compression.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Unused.h"
 
 #include "jsmath.h"
-#include "jsstr.h"
 #include "jsutil.h"
 
 #include "builtin/SIMD.h"
+#include "builtin/String.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/Wrapper.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/ErrorReporting.h"
 #include "vm/SelfHosting.h"
-#include "vm/StringBuffer.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmSerialize.h"
--- a/js/src/wasm/WasmBinaryToText.cpp
+++ b/js/src/wasm/WasmBinaryToText.cpp
@@ -15,18 +15,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmBinaryToText.h"
 
 #include "jsnum.h"
 
+#include "util/StringBuffer.h"
 #include "vm/ArrayBufferObject.h"
-#include "vm/StringBuffer.h"
 #include "wasm/WasmAST.h"
 #include "wasm/WasmBinaryToAST.h"
 #include "wasm/WasmDebug.h"
 #include "wasm/WasmTextUtils.h"
 #include "wasm/WasmTypes.h"
 
 using namespace js;
 using namespace js::wasm;
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -17,16 +17,17 @@
  */
 
 #include "wasm/WasmCompile.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Unused.h"
 
 #include "jit/ProcessExecutableMemory.h"
+#include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -19,18 +19,19 @@
 #include "wasm/WasmDebug.h"
 
 #include "mozilla/BinarySearch.h"
 
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/MacroAssembler.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/Debugger.h"
-#include "vm/StringBuffer.h"
 #include "wasm/WasmBinaryToText.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -19,16 +19,17 @@
 #include "wasm/WasmGenerator.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/EnumeratedRange.h"
 #include "mozilla/SHA1.h"
 
 #include <algorithm>
 
+#include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmStubs.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -23,19 +23,20 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/RangedPtr.h"
 
 #include "builtin/Promise.h"
 #include "gc/FreeOp.h"
 #include "jit/AtomicOperations.h"
 #include "jit/JitOptions.h"
 #include "js/Printf.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/Interpreter.h"
-#include "vm/String.h"
-#include "vm/StringBuffer.h"
+#include "vm/StringType.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmStubs.h"
 #include "wasm/WasmValidate.h"
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -18,18 +18,18 @@
 
 #include "wasm/WasmTextToBinary.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Maybe.h"
 
 #include "jsnum.h"
-#include "jsstr.h"
-
+
+#include "builtin/String.h"
 #include "ds/LifoAlloc.h"
 #include "js/CharacterEncoding.h"
 #include "js/HashTable.h"
 #include "js/Printf.h"
 #include "util/DoubleToString.h"
 #include "wasm/WasmAST.h"
 #include "wasm/WasmTypes.h"
 #include "wasm/WasmValidate.h"
--- a/js/src/wasm/WasmTextUtils.cpp
+++ b/js/src/wasm/WasmTextUtils.cpp
@@ -13,17 +13,17 @@
  * 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/WasmTextUtils.h"
 
-#include "vm/StringBuffer.h"
+#include "util/StringBuffer.h"
 #include "wasm/WasmTypes.h"
 
 using namespace js;
 using namespace jit;
 using namespace wasm;
 
 using mozilla::IsNaN;
 
--- a/js/src/wasm/WasmTextUtils.h
+++ b/js/src/wasm/WasmTextUtils.h
@@ -16,17 +16,17 @@
  * limitations under the License.
  */
 
 #ifndef wasm_text_utils
 #define wasm_text_utils
 
 #include "NamespaceImports.h"
 
-#include "vm/StringBuffer.h"
+#include "util/StringBuffer.h"
 
 namespace js {
 namespace wasm {
 
 template<size_t base>
 MOZ_MUST_USE bool
 RenderInBase(StringBuffer& sb, uint64_t num);
 
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1070,17 +1070,17 @@ public abstract class GeckoApp extends G
         mRootLayout = (RelativeLayout) findViewById(R.id.root_layout);
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
         mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
         mLayerView = (GeckoView) findViewById(R.id.layer_view);
 
         final GeckoSession session = new GeckoSession();
         // If the view already has a session, we need to ensure it is closed.
         if (mLayerView.getSession() != null) {
-            mLayerView.getSession().closeWindow();
+            mLayerView.getSession().close();
         }
         mLayerView.setSession(session);
         mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
 
         session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
                                         "chrome://browser/content/browser.xul");
         session.setContentDelegate(this);
 
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -2,41 +2,38 @@
  * 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/. */
 
 package org.mozilla.gecko.customtabs;
 
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Browser;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.support.v4.util.SparseArrayCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
 import android.support.v7.widget.Toolbar;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.ListView;
 import android.widget.ProgressBar;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.Clipboard;
 import org.mozilla.gecko.DoorHangerPopup;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.FormAssistPopup;
@@ -46,26 +43,23 @@ import org.mozilla.gecko.preferences.Gec
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SnackbarBuilder;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.permissions.Permissions;
-import org.mozilla.gecko.prompts.Prompt;
-import org.mozilla.gecko.prompts.PromptListItem;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.text.TextSelection;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.ColorUtil;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.IntentUtils;
 import org.mozilla.gecko.util.PackageUtil;
-import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.webapps.WebApps;
 import org.mozilla.gecko.widget.ActionModePresenter;
 import org.mozilla.gecko.widget.GeckoPopupMenu;
 import org.mozilla.geckoview.GeckoSession;
 import org.mozilla.geckoview.GeckoSessionSettings;
 import org.mozilla.geckoview.GeckoView;
 
 import java.util.List;
@@ -174,17 +168,17 @@ public class CustomTabsActivity extends 
     @Override
     public void onPause() {
         mGeckoSession.setActive(false);
         super.onPause();
     }
 
     @Override
     public void onDestroy() {
-        mGeckoSession.closeWindow();
+        mGeckoSession.close();
         mTextSelection.destroy();
         mFormAssistPopup.destroy();
         mDoorHangerPopup.destroy();
         mPromptService.destroy();
 
         super.onDestroy();
     }
 
@@ -606,19 +600,19 @@ public class CustomTabsActivity extends 
 
     @Override
     public void onCanGoForward(GeckoSession session, boolean canGoForward) {
         mCanGoForward = canGoForward;
         updateMenuItemForward();
     }
 
     @Override
-    public boolean onLoadUri(final GeckoSession session, final String urlStr,
-                             final TargetWindow where) {
-        if (where != TargetWindow.NEW) {
+    public boolean onLoadRequest(final GeckoSession session, final String urlStr,
+                                 final int target) {
+        if (target != GeckoSession.NavigationDelegate.TARGET_WINDOW_NEW) {
             return false;
         }
 
         final Uri uri = Uri.parse(urlStr);
         if (uri == null) {
             // We can't handle this, so deny it.
             Log.w(LOGTAG, "Failed to parse URL for navigation: " + urlStr);
             return true;
@@ -647,17 +641,17 @@ public class CustomTabsActivity extends 
         }
 
         return true;
     }
 
     @Override
     public void onNewSession(final GeckoSession session, final String uri,
                              final GeckoSession.Response<GeckoSession> response) {
-        // We should never get here because we abort loads that need a new session in onLoadUri()
+        // We should never get here because we abort loads that need a new session in onLoadRequest()
         throw new IllegalStateException("Unexpected new session");
     }
 
     /* GeckoSession.ProgressDelegate */
     @Override
     public void onPageStart(GeckoSession session, String url) {
         mCurrentUrl = url;
         mCanStop = true;
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -1,38 +1,29 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.webapps;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.customtabs.CustomTabsIntent;
-import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
-import android.support.v7.widget.Toolbar;
 import android.util.Log;
-import android.view.View;
 import android.view.Window;
 import android.view.WindowManager;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.DoorHangerPopup;
 import org.mozilla.gecko.FormAssistPopup;
 import org.mozilla.gecko.GeckoAccessibility;
@@ -209,17 +200,17 @@ public class WebAppActivity extends AppC
     @Override
     public void onPause() {
         mGeckoSession.setActive(false);
         super.onPause();
     }
 
     @Override
     public void onDestroy() {
-        mGeckoSession.closeWindow();
+        mGeckoSession.close();
         mTextSelection.destroy();
         mFormAssistPopup.destroy();
         mDoorHangerPopup.destroy();
         mPromptService.destroy();
         super.onDestroy();
     }
 
     @Override
@@ -374,26 +365,26 @@ public class WebAppActivity extends AppC
     }
 
     @Override // GeckoSession.ContentDelegate
     public void onFullScreen(GeckoSession session, boolean fullScreen) {
         updateFullScreenContent(fullScreen);
     }
 
     @Override
-    public boolean onLoadUri(final GeckoSession session, final String urlStr,
-                             final TargetWindow where) {
+    public boolean onLoadRequest(final GeckoSession session, final String urlStr,
+                                 final int target) {
         final Uri uri = Uri.parse(urlStr);
         if (uri == null) {
             // We can't really handle this, so deny it?
             Log.w(LOGTAG, "Failed to parse URL for navigation: " + urlStr);
             return true;
         }
 
-        if (mManifest.isInScope(uri) && where != TargetWindow.NEW) {
+        if (mManifest.isInScope(uri) && target != TARGET_WINDOW_NEW) {
             // This is in scope and wants to load in the same frame, so
             // let Gecko handle it.
             return false;
         }
 
         if ("javascript".equals(uri.getScheme())) {
             // These URIs will fail the scope check but should still be loaded in the PWA.
             return false;
@@ -425,17 +416,17 @@ public class WebAppActivity extends AppC
             }
         }
         return true;
     }
 
     @Override
     public void onNewSession(final GeckoSession session, final String uri,
                              final GeckoSession.Response<GeckoSession> response) {
-        // We should never get here because we abort loads that need a new session in onLoadUri()
+        // We should never get here because we abort loads that need a new session in onLoadRequest()
         throw new IllegalStateException("Unexpected new session");
     }
 
     private void updateFullScreen() {
         boolean fullScreen = mIsFullScreenContent || mIsFullScreenMode;
         if (ActivityUtils.isFullScreen(this) == fullScreen) {
             return;
         }
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -53,17 +53,17 @@ open class BaseSessionTest(noErrorCollec
 
             assertThat("Read parcel matches written parcel",
                        parcel.dataPosition(), Matchers.equalTo(pos))
         } finally {
             parcel.recycle()
         }
     }
 
-    fun GeckoSession.openWindow() =
+    fun GeckoSession.open() =
             sessionRule.openSession(this)
 
     fun GeckoSession.waitForPageStop() =
             sessionRule.waitForPageStop(this)
 
     fun GeckoSession.waitForPageStops(count: Int) =
             sessionRule.waitForPageStops(this, count)
 
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
@@ -19,24 +19,24 @@ import org.junit.runner.RunWith
 class NavigationDelegateTest : BaseSessionTest() {
 
     @Test fun load() {
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 1, order = intArrayOf(1))
-            override fun onLoadUri(session: GeckoSession, uri: String,
-                                   where: GeckoSession.NavigationDelegate.TargetWindow): Boolean {
+            override fun onLoadRequest(session: GeckoSession, uri: String,
+                                       where: Int): Boolean {
                 assertThat("Session should not be null", session, notNullValue())
                 assertThat("URI should not be null", uri, notNullValue())
                 assertThat("URI should match", uri, endsWith(HELLO_HTML_PATH))
                 assertThat("Where should not be null", where, notNullValue())
                 assertThat("Where should match", where,
-                           equalTo(GeckoSession.NavigationDelegate.TargetWindow.CURRENT))
+                           equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
                 return false
             }
 
             @AssertCalled(count = 1, order = intArrayOf(2))
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("Session should not be null", session, notNullValue())
                 assertThat("URL should not be null", url, notNullValue())
                 assertThat("URL should match", url, endsWith(HELLO_HTML_PATH))
@@ -65,21 +65,21 @@ class NavigationDelegateTest : BaseSessi
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.waitForPageStop()
 
         sessionRule.session.reload()
         sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 1, order = intArrayOf(1))
-            override fun onLoadUri(session: GeckoSession, uri: String,
-                                   where: GeckoSession.NavigationDelegate.TargetWindow): Boolean {
+            override fun onLoadRequest(session: GeckoSession, uri: String,
+                                       where: Int): Boolean {
                 assertThat("URI should match", uri, endsWith(HELLO_HTML_PATH))
                 assertThat("Where should match", where,
-                           equalTo(GeckoSession.NavigationDelegate.TargetWindow.CURRENT))
+                           equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
                 return false
             }
 
             @AssertCalled(count = 1, order = intArrayOf(2))
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should match", url, endsWith(HELLO_HTML_PATH))
             }
 
@@ -114,21 +114,21 @@ class NavigationDelegateTest : BaseSessi
             }
         })
 
         sessionRule.session.goBack()
         sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 1, order = intArrayOf(1))
-            override fun onLoadUri(session: GeckoSession, uri: String,
-                                   where: GeckoSession.NavigationDelegate.TargetWindow): Boolean {
+            override fun onLoadRequest(session: GeckoSession, uri: String,
+                                       where: Int): Boolean {
                 assertThat("URI should match", uri, endsWith(HELLO_HTML_PATH))
                 assertThat("Where should match", where,
-                           equalTo(GeckoSession.NavigationDelegate.TargetWindow.CURRENT))
+                           equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
                 return false
             }
 
             @AssertCalled(count = 1, order = intArrayOf(2))
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should match", url, endsWith(HELLO_HTML_PATH))
             }
 
@@ -148,21 +148,21 @@ class NavigationDelegateTest : BaseSessi
             }
         })
 
         sessionRule.session.goForward()
         sessionRule.waitForPageStop()
 
         sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 1, order = intArrayOf(1))
-            override fun onLoadUri(session: GeckoSession, uri: String,
-                                   where: GeckoSession.NavigationDelegate.TargetWindow): Boolean {
+            override fun onLoadRequest(session: GeckoSession, uri: String,
+                                       where: Int): Boolean {
                 assertThat("URI should match", uri, endsWith(HELLO2_HTML_PATH))
                 assertThat("Where should match", where,
-                           equalTo(GeckoSession.NavigationDelegate.TargetWindow.CURRENT))
+                           equalTo(GeckoSession.NavigationDelegate.TARGET_WINDOW_CURRENT))
                 return false
             }
 
             @AssertCalled(count = 1, order = intArrayOf(2))
             override fun onLocationChange(session: GeckoSession, url: String) {
                 assertThat("URL should match", url, endsWith(HELLO2_HTML_PATH))
             }
 
@@ -181,18 +181,18 @@ class NavigationDelegateTest : BaseSessi
                                       response: GeckoSession.Response<GeckoSession>) {
             }
         })
     }
 
     @Test fun onLoadUri_returnTrueCancelsLoad() {
         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 2)
-            override fun onLoadUri(session: GeckoSession, uri: String,
-                                   where: GeckoSession.NavigationDelegate.TargetWindow): Boolean {
+            override fun onLoadRequest(session: GeckoSession, uri: String,
+                                       where: Int): Boolean {
                 return uri.endsWith(HELLO_HTML_PATH)
             }
         })
 
         sessionRule.session.loadTestPath(HELLO_HTML_PATH)
         sessionRule.session.loadTestPath(HELLO2_HTML_PATH)
         sessionRule.waitForPageStop()
 
@@ -230,17 +230,17 @@ class NavigationDelegateTest : BaseSessi
     fun onNewSession_doesNotAllowOpened() {
         sessionRule.session.loadTestPath(NEW_SESSION_HTML_PATH)
         sessionRule.waitForPageStop()
 
         sessionRule.delegateDuringNextWait(object : Callbacks.NavigationDelegate {
             @AssertCalled(count = 1)
             override fun onNewSession(session: GeckoSession, uri: String, response: GeckoSession.Response<GeckoSession>) {
                 var newSession = GeckoSession(session.settings)
-                newSession.openWindow()
+                newSession.open()
                 response.respond(newSession)
             }
         })
 
         sessionRule.session.synthesizeTap(5, 5)
         sessionRule.waitUntilCalled(GeckoSession.NavigationDelegate::class, "onNewSession")
     }
 }
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
@@ -5,22 +5,16 @@ import org.mozilla.gecko.mozglue.SafeInt
 import org.mozilla.geckoview.GeckoSession;
 import org.mozilla.geckoview.GeckoSessionSettings;
 import org.mozilla.geckoview.GeckoView;
 
 import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
 
 public class TestRunnerActivity extends Activity {
     private static final String LOGTAG = "TestRunnerActivity";
 
     GeckoSession mSession;
     GeckoView mView;
 
     private GeckoSession.NavigationDelegate mNavigationDelegate = new GeckoSession.NavigationDelegate() {
@@ -35,17 +29,17 @@ public class TestRunnerActivity extends 
         }
 
         @Override
         public void onCanGoForward(GeckoSession session, boolean canGoForward) {
 
         }
 
         @Override
-        public boolean onLoadUri(GeckoSession session, String uri, TargetWindow where) {
+        public boolean onLoadRequest(GeckoSession session, String uri, int target) {
             // Allow Gecko to load all URIs
             return false;
         }
 
         @Override
         public void onNewSession(GeckoSession session, String uri, GeckoSession.Response<GeckoSession> response) {
             response.respond(createSession(session.getSettings()));
         }
@@ -59,17 +53,17 @@ public class TestRunnerActivity extends 
 
         @Override
         public void onFocusRequest(GeckoSession session) {
 
         }
 
         @Override
         public void onCloseRequest(GeckoSession session) {
-            session.closeWindow();
+            session.close();
         }
 
         @Override
         public void onFullScreen(GeckoSession session, boolean fullScreen) {
 
         }
 
         @Override
@@ -102,32 +96,32 @@ public class TestRunnerActivity extends 
 
         final Intent intent = getIntent();
         GeckoSession.preload(this, new String[] { "-purgecaches" },
                              intent.getExtras(), false /* no multiprocess, see below */);
 
         // We can't use e10s because we get deadlocked when quickly creating and
         // destroying sessions. Bug 1348361.
         mSession = createSession();
-        mSession.openWindow(this);
+        mSession.open(this);
 
         // If we were passed a URI in the Intent, open it
         final Uri uri = intent.getData();
         if (uri != null) {
             mSession.loadUri(uri);
         }
 
         mView = new GeckoView(this);
         mView.setSession(mSession);
         setContentView(mView);
     }
 
     @Override
     protected void onDestroy() {
-        mSession.closeWindow();
+        mSession.close();
         super.onDestroy();
     }
 
     public GeckoView getGeckoView() {
         return mView;
     }
 
     public GeckoSession getGeckoSession() {
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -732,31 +732,31 @@ public class GeckoSessionTestRule extend
         for (final Class<?> cls : CALLBACK_CLASSES) {
             if (cls != null) {
                 getCallbackSetter(cls).invoke(session, mCallbackProxy);
             }
         }
     }
 
     /**
-     * Call openWindow() on a session, and ensure it's ready for use by the test. In particular,
+     * Call open() on a session, and ensure it's ready for use by the test. In particular,
      * remove any extra calls recorded as part of opening the session.
      *
      * @param session Session to open.
      */
     public void openSession(final GeckoSession session) {
         final boolean e10s = session.getSettings().getBoolean(
                 GeckoSessionSettings.USE_MULTIPROCESS);
 
         if (e10s) {
             // Give any pending calls a chance to catch up.
             loopUntilIdle(/* timeout */ 0);
         }
 
-        session.openWindow(mInstrumentation.getTargetContext());
+        session.open(mInstrumentation.getTargetContext());
 
         if (!e10s) {
             return;
         }
 
         // Under e10s, we receive an initial about:blank load; don't expose that to the test.
         // The about:blank load is bounded by onLocationChange and onPageStop calls,
         // so find the first about:blank onLocationChange, then the next onPageStop,
@@ -796,17 +796,17 @@ public class GeckoSessionTestRule extend
      */
     public void performTestEndCheck() {
         mWaitScopeDelegates.clear();
         mTestScopeDelegates.clear();
     }
 
     protected void cleanupSession(final GeckoSession session) {
         if (session.isOpen()) {
-            session.closeWindow();
+            session.close();
         }
     }
 
     protected void cleanupStatement() throws Throwable {
         for (final GeckoSession session : mSubSessions) {
             cleanupSession(session);
         }
         cleanupSession(mMainSession);
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
@@ -35,17 +35,17 @@ class Callbacks private constructor() {
         }
 
         override fun onCanGoBack(session: GeckoSession, canGoBack: Boolean) {
         }
 
         override fun onCanGoForward(session: GeckoSession, canGoForward: Boolean) {
         }
 
-        override fun onLoadUri(session: GeckoSession, uri: String, where: GeckoSession.NavigationDelegate.TargetWindow): Boolean {
+        override fun onLoadRequest(session: GeckoSession, uri: String, where: Int): Boolean {
             return false;
         }
 
         override fun onNewSession(session: GeckoSession, uri: String, response: GeckoSession.Response<GeckoSession>) {
             response.respond(null)
         }
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -122,52 +122,61 @@ public class GeckoSession extends LayerS
         new GeckoSessionHandler<NavigationDelegate>(
             "GeckoViewNavigation", this,
             new String[]{
                 "GeckoView:LocationChange",
                 "GeckoView:OnLoadUri",
                 "GeckoView:OnNewSession"
             }
         ) {
+            // This needs to match nsIBrowserDOMWindow.idl
+            private int convertGeckoTarget(int geckoTarget) {
+                switch (geckoTarget) {
+                    case 0: // OPEN_DEFAULTWINDOW
+                    case 1: // OPEN_CURRENTWINDOW
+                        return NavigationDelegate.TARGET_WINDOW_CURRENT;
+                    default: // OPEN_NEWWINDOW, OPEN_NEWTAB, OPEN_SWITCHTAB
+                        return NavigationDelegate.TARGET_WINDOW_NEW;
+                }
+            }
+
             @Override
             public void handleMessage(final NavigationDelegate delegate,
                                       final String event,
                                       final GeckoBundle message,
                                       final EventCallback callback) {
                 if ("GeckoView:LocationChange".equals(event)) {
                     delegate.onLocationChange(GeckoSession.this,
                                               message.getString("uri"));
                     delegate.onCanGoBack(GeckoSession.this,
                                          message.getBoolean("canGoBack"));
                     delegate.onCanGoForward(GeckoSession.this,
                                             message.getBoolean("canGoForward"));
                 } else if ("GeckoView:OnLoadUri".equals(event)) {
                     final String uri = message.getString("uri");
-                    final NavigationDelegate.TargetWindow where =
-                        NavigationDelegate.TargetWindow.forGeckoValue(
-                            message.getInt("where"));
+                    final int where = convertGeckoTarget(message.getInt("where"));
                     final boolean result =
-                        delegate.onLoadUri(GeckoSession.this, uri, where);
+                        delegate.onLoadRequest(GeckoSession.this, uri, where);
                     callback.sendSuccess(result);
                 } else if ("GeckoView:OnNewSession".equals(event)) {
                     final String uri = message.getString("uri");
                     delegate.onNewSession(GeckoSession.this, uri,
                         new Response<GeckoSession>() {
                             @Override
                             public void respond(GeckoSession session) {
                                 if (session == null) {
                                     callback.sendSuccess(null);
                                     return;
                                 }
 
                                 if (session.isOpen()) {
                                     throw new IllegalArgumentException("Must use an unopened GeckoSession instance");
                                 }
 
-                                session.openWindow(null);
+                                session.open(null);
                                 callback.sendSuccess(session.getId());
                             }
                         });
                 }
             }
         };
 
     private final GeckoSessionHandler<ProgressDelegate> mProgressHandler =
@@ -588,17 +597,30 @@ public class GeckoSession extends LayerS
     public boolean isOpen() {
         return mWindow != null;
     }
 
     /* package */ boolean isReady() {
         return mNativeQueue.isReady();
     }
 
-    public void openWindow(final @Nullable Context appContext) {
+    /**
+     * Opens the session.
+     *
+     * The session is in a 'closed' state when first created. Opening it creates
+     * the underlying Gecko objects necessary to load a page, etc. Most GeckoSession
+     * methods only take affect on an open session, and are queued until the session
+     * is opened here. Opening a session is an asynchronous operation. You can check
+     * the current state via isOpen().
+     *
+     * Call this when you are ready to use a GeckoSession instance.
+     *
+     * @param appContext An application context
+     */
+    public void open(final @Nullable Context appContext) {
         ThreadUtils.assertOnUiThread();
 
         if (isOpen()) {
             throw new IllegalStateException("Session is open");
         }
 
         if (appContext != null) {
             final boolean multiprocess =
@@ -629,17 +651,24 @@ public class GeckoSession extends LayerS
                 GeckoBundle.class, mSettings.asBundle(),
                 String.class, chromeUri,
                 screenId, isPrivate, mId);
         }
 
         onWindowChanged();
     }
 
-    public void closeWindow() {
+    /**
+     * Closes the session.
+     *
+     * This frees the underlying Gecko objects and unloads the current page. The session may be
+     * reopened later, but page state is not restored. Call this when you are finished using
+     * a GeckoSession instance.
+     */
+    public void close() {
         ThreadUtils.assertOnUiThread();
 
         if (!isOpen()) {
             Log.w(LOGTAG, "Attempted to close a GeckoSession that was already closed.");
             return;
         }
 
         if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
@@ -1394,59 +1423,31 @@ public class GeckoSession extends LayerS
 
         /**
         * The view's ability to go forward has changed.
         * @param session The GeckoSession that initiated the callback.
         * @param canGoForward The new value for the ability.
         */
         void onCanGoForward(GeckoSession session, boolean canGoForward);
 
-        enum TargetWindow {
-            DEFAULT(0),
-            CURRENT(1),
-            NEW(2);
-
-            private static final TargetWindow[] sValues = TargetWindow.values();
-            private int mValue;
-
-            private TargetWindow(int value) {
-                mValue = value;
-            }
-
-            public static TargetWindow forValue(int value) {
-                return sValues[value];
-            }
-
-            public static TargetWindow forGeckoValue(int value) {
-                // DEFAULT(0),
-                // CURRENT(1),
-                // NEW(2),
-                // NEWTAB(3),
-                // SWITCHTAB(4);
-                final TargetWindow[] sMap = {
-                    DEFAULT,
-                    CURRENT,
-                    NEW,
-                    NEW,
-                    NEW
-                };
-                return sMap[value];
-            }
-        }
+        public static final int TARGET_WINDOW_NONE = 0;
+        public static final int TARGET_WINDOW_CURRENT = 1;
+        public static final int TARGET_WINDOW_NEW = 2;
 
         /**
-        * A request to open an URI.
-        * @param session The GeckoSession that initiated the callback.
-        * @param uri The URI to be loaded.
-        * @param where The target window.
-        *
-        * @return Whether or not the load was handled. Returning false will allow Gecko
-        *         to continue the load as normal.
-        */
-        boolean onLoadUri(GeckoSession session, String uri, TargetWindow where);
+         * A request to open an URI.
+         * @param session The GeckoSession that initiated the callback.
+         * @param uri The URI to be loaded.
+         * @param target The target where the window has requested to open. One of
+         *               TARGET_WINDOW_*.
+         *
+         * @return Whether or not the load was handled. Returning false will allow Gecko
+         *         to continue the load as normal.
+         */
+        boolean onLoadRequest(GeckoSession session, String uri, int target);
 
         /**
         * A request has been made to open a new session. The URI is provided only for
         * informational purposes. Do not call GeckoSession.loadUri() here. Additionally, the
         * returned GeckoSession must be a newly-created one.
         *
         * @param session The GeckoSession that initiated the callback.
         * @param uri The URI to be loaded.
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -187,28 +187,41 @@ public class GeckoView extends FrameLayo
      * @param color Cover color.
      */
     public void coverUntilFirstPaint(final int color) {
         if (mSurfaceView != null) {
             mSurfaceView.setBackgroundColor(color);
         }
     }
 
+    public GeckoSession releaseSession() {
+        if (mSession == null) {
+            return null;
+        }
+
+        GeckoSession session = mSession;
+        mSession.releaseDisplay(mDisplay.release());
+        mSession.getOverscrollEdgeEffect().setInvalidationCallback(null);
+        mSession.getCompositorController().setFirstPaintCallback(null);
+        mSession = null;
+        return session;
+    }
+
     public void setSession(final GeckoSession session) {
         if (mSession != null && mSession.isOpen()) {
             throw new IllegalStateException("Current session is open");
         }
 
-        if (mSession != null) {
-            mSession.releaseDisplay(mDisplay.release());
-        }
-        if (session != null) {
-            mDisplay.acquire(session.acquireDisplay());
+        releaseSession();
+        mSession = session;
+        if (mSession == null) {
+          return;
         }
 
+        mDisplay.acquire(session.acquireDisplay());
         final Context context = getContext();
         session.getOverscrollEdgeEffect().setTheme(context);
         session.getOverscrollEdgeEffect().setInvalidationCallback(new Runnable() {
             @Override
             public void run() {
                 if (Build.VERSION.SDK_INT >= 16) {
                     GeckoView.this.postInvalidateOnAnimation();
                 } else {
@@ -227,18 +240,16 @@ public class GeckoView extends FrameLayo
         }
 
         session.getCompositorController().setFirstPaintCallback(new Runnable() {
             @Override
             public void run() {
                 coverUntilFirstPaint(Color.TRANSPARENT);
             }
         });
-
-        mSession = session;
     }
 
     public GeckoSession getSession() {
         return mSession;
     }
 
     public EventDispatcher getEventDispatcher() {
         return mSession.getEventDispatcher();
@@ -258,37 +269,39 @@ public class GeckoView extends FrameLayo
 
     @Override
     public void onAttachedToWindow() {
         if (mSession == null) {
             setSession(new GeckoSession());
         }
 
         if (!mSession.isOpen()) {
-            mSession.openWindow(getContext().getApplicationContext());
+            mSession.open(getContext().getApplicationContext());
         }
 
         mSession.getTextInputController().setView(this);
 
         super.onAttachedToWindow();
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
-        mSession.getTextInputController().setView(this);
+        if (mSession != null) {
+          mSession.getTextInputController().setView(null);
+        }
 
         if (mStateSaved) {
             // If we saved state earlier, we don't want to close the window.
             return;
         }
 
         if (mSession != null && mSession.isOpen()) {
-            mSession.closeWindow();
+            mSession.close();
         }
     }
 
     @Override
     public boolean gatherTransparentRegion(final Region region) {
         // For detecting changes in SurfaceView layout, we take a shortcut here and
         // override gatherTransparentRegion, instead of registering a layout listener,
         // which is more expensive.
--- 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
@@ -14,20 +14,18 @@ import android.os.Bundle;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.WindowManager;
 
 import java.util.Locale;
 
 import org.mozilla.gecko.GeckoThread;
-import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.geckoview.GeckoSession;
 import org.mozilla.geckoview.GeckoSessionSettings;
-import org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource;
 import org.mozilla.geckoview.GeckoSession.Response;
 import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
 import org.mozilla.geckoview.GeckoView;
 
 public class GeckoViewActivity extends Activity {
     private static final String LOGTAG = "GeckoViewActivity";
     private static final String DEFAULT_URL = "https://mozilla.org";
     private static final String USE_MULTIPROCESS_EXTRA = "use_multiprocess";
@@ -181,17 +179,17 @@ public class GeckoViewActivity extends A
         @Override
         public void onFocusRequest(final GeckoSession session) {
             Log.i(LOGTAG, "Content requesting focus");
         }
 
         @Override
         public void onCloseRequest(final GeckoSession session) {
             if (session != mGeckoSession) {
-                session.closeWindow();
+                session.close();
             }
         }
 
         @Override
         public void onContextMenu(GeckoSession session, int screenX, int screenY,
                                   String uri, String elementSrc) {
             Log.d(LOGTAG, "onContextMenu screenX=" + screenX +
                           " screenY=" + screenY + " uri=" + uri +
@@ -343,19 +341,19 @@ public class GeckoViewActivity extends A
         public void onCanGoBack(GeckoSession session, boolean canGoBack) {
         }
 
         @Override
         public void onCanGoForward(GeckoSession session, boolean value) {
         }
 
         @Override
-        public boolean onLoadUri(final GeckoSession session, final String uri,
-                                 final TargetWindow where) {
-            Log.d(LOGTAG, "onLoadUri=" + uri + " where=" + where);
+        public boolean onLoadRequest(final GeckoSession session, final String uri,
+                                     final int target) {
+            Log.d(LOGTAG, "onLoadRequest=" + uri + " where=" + target);
             return false;
         }
 
         @Override
         public void onNewSession(final GeckoSession session, final String uri, Response<GeckoSession> response) {
             response.respond(null);
         }
     }
--- a/taskcluster/ci/release-bouncer-sub/kind.yml
+++ b/taskcluster/ci/release-bouncer-sub/kind.yml
@@ -26,17 +26,17 @@ job-defaults:
          mozilla-release:
             - project:releng:bouncer:action:submission
             - project:releng:bouncer:server:production
          default:
             - project:releng:bouncer:action:submission
             - project:releng:bouncer:server:staging
    run-on-projects: []
    shipping-phase: promote
-   locales-file: browser/locales/l10n-changesets.json
+   locales-file: browser/locales/shipped-locales
 
 jobs:
    devedition:
       bouncer-platforms: ['linux', 'linux64', 'osx', 'win', 'win64']
       bouncer-products: ['complete-mar', 'installer', 'installer-ssl', 'partial-mar', 'stub-installer']
       shipping-product: devedition
 
    fennec:
--- a/taskcluster/taskgraph/transforms/bouncer_submission.py
+++ b/taskcluster/taskgraph/transforms/bouncer_submission.py
@@ -87,18 +87,24 @@ def make_task_worker(config, jobs):
     for job in jobs:
         resolve_keyed_by(
             job, 'worker-type', item_name=job['name'], project=config.params['project']
         )
         resolve_keyed_by(
             job, 'scopes', item_name=job['name'], project=config.params['project']
         )
 
-        # No need to filter out ja-JP-mac, we need to upload both
-        all_locales = list(sorted(parse_locales_file(job['locales-file']).keys()))
+        # No need to filter out ja-JP-mac, we need to upload both; but we do
+        # need to filter out the platforms they come with
+        all_locales = sorted([
+            locale
+            for locale in parse_locales_file(job['locales-file']).keys()
+            if locale not in ('linux', 'win32', 'osx')
+        ])
+
         job['worker']['locales'] = all_locales
         job['worker']['entries'] = craft_bouncer_entries(config, job)
 
         del job['locales-file']
         del job['bouncer-platforms']
         del job['bouncer-products']
 
         if job['worker']['entries']:
@@ -171,16 +177,21 @@ def craft_paths_per_bouncer_platform(pro
             # Some bouncer product like stub-installer are only meant to be on Windows.
             # Thus no default value is defined there
             continue
 
         file_name = file_name_template.format(
             version=current_version, previous_version=strip_build_data(previous_version)
         )
 
+        # We currently have a sole win32 stub installer that is to be used
+        # in both windows platforms to toggle between full installers
+        if 'Installer.exe' in file_name and ftp_platform == 'win64':
+            ftp_platform = 'win32'
+
         path_template = CONFIG_PER_BOUNCER_PRODUCT[bouncer_product]['path_template']
         file_relative_location = path_template.format(
             product=product.lower(),
             version=current_version,
             build_number=current_build_number,
             update_folder='update/' if '-mar' in bouncer_product else '',
             ftp_platform=ftp_platform,
             file=file_name,