Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 12 May 2014 16:32:53 -0400
changeset 203025 4b6d63b05a0a54ad7be7f1fc251e3a03b7a3a2fc
parent 202947 fa3e1d8fa2768f910b33b4545a579a7798e4d8c1 (current diff)
parent 203024 5892d2b327dd756ab18ceefe9db66db8731555ad (diff)
child 203057 86e58070041fbe7c9e3b275bbbb1e67ac14c7f22
child 203115 cb0e825052bc05a2bd08b1b681b2961935b440e5
child 203133 4d0a95ece9c6d4e3ef041f991bde744d5983ca17
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
first release with
nightly linux32
4b6d63b05a0a / 32.0a1 / 20140513030201 / files
nightly linux64
4b6d63b05a0a / 32.0a1 / 20140513030201 / files
nightly mac
4b6d63b05a0a / 32.0a1 / 20140513030201 / files
nightly win32
4b6d63b05a0a / 32.0a1 / 20140513030201 / files
nightly win64
4b6d63b05a0a / 32.0a1 / 20140513030201 / 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 m-c.
intl/uconv/ucvko/johabjamo.uf
intl/uconv/ucvko/johabjamo.ut
intl/uconv/ucvko/nsJohabToUnicode.cpp
intl/uconv/ucvko/nsJohabToUnicode.h
intl/uconv/ucvko/nsUnicodeToJohab.cpp
intl/uconv/ucvko/nsUnicodeToJohab.h
intl/uconv/ucvlatin/nsT61ToUnicode.cpp
intl/uconv/ucvlatin/nsT61ToUnicode.h
intl/uconv/ucvlatin/nsUnicodeToT61.cpp
intl/uconv/ucvlatin/nsUnicodeToT61.h
intl/uconv/ucvlatin/t61.uf
intl/uconv/ucvlatin/t61.ut
uriloader/exthandler/unix/nsMeegoExternalSharingAppService.cpp
--- a/browser/components/customizableui/content/panelUI.xml
+++ b/browser/components/customizableui/content/panelUI.xml
@@ -308,18 +308,27 @@
               this._mainView.style.removeProperty("height");
               this.showMainView();
               this._mainViewObserver.disconnect();
               break;
           }
         ]]></body>
       </method>
 
+      <method name="_shouldSetHeight">
+        <body><![CDATA[
+          return this.getAttribute("nosubviews") != "true";
+        ]]></body>
+      </method>
+
       <method name="_setMaxHeight">
         <body><![CDATA[
+          if (!this._shouldSetHeight())
+            return;
+
           // Ignore the mutation that'll fire when we set the height of
           // the main view.
           this.ignoreMutations = true;
           this._mainView.style.height =
             this.getBoundingClientRect().height + "px";
           this.ignoreMutations = false;
         ]]></body>
       </method>
@@ -334,17 +343,17 @@
             let newHeight = this._heightOfSubview(this._currentSubView, this._subViews);
             this._viewContainer.style.height = newHeight + "px";
           }
         ]]></body>
       </method>
       <method name="_syncContainerWithMainView">
         <body><![CDATA[
           // Check that this panel is still alive:
-          if (!this._panel || !this._panel.parentNode) {
+          if (!this._panel || !this._panel.parentNode || !this._shouldSetHeight()) {
             return;
           }
 
           if (!this.ignoreMutations && !this.showingSubView && !this._transitioning) {
             let height;
             if (this.showingSubViewAsMainView) {
               height = this._heightOfSubview(this._mainView);
             } else {
old mode 100644
new mode 100755
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -229,16 +229,17 @@ run-if = os == "win"
 [browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js]
 [browser_webconsole_bug_804845_ctrl_key_nav.js]
 run-if = os == "mac"
 [browser_webconsole_bug_817834_add_edited_input_to_history.js]
 [browser_webconsole_bug_821877_csp_errors.js]
 [browser_webconsole_bug_837351_securityerrors.js]
 [browser_webconsole_bug_846918_hsts_invalid-headers.js]
 [browser_webconsole_bug_915141_toggle_response_logging_with_keyboard.js]
+[browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
 [browser_webconsole_cached_autocomplete.js]
 [browser_webconsole_change_font_size.js]
 [browser_webconsole_chrome.js]
 [browser_webconsole_closure_inspection.js]
 [browser_webconsole_completion.js]
 [browser_webconsole_console_extras.js]
 [browser_webconsole_console_logging_api.js]
 [browser_webconsole_count.js]
new file mode 100755
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function test() {
+  Task.spawn(runner).then(finishTest);
+
+  function* runner() {
+    const {tab} = yield loadTab("data:text/html;charset=utf8,<title>Test for Bug 1006027");
+
+    const target = TargetFactory.forTab(tab);
+    const hud = yield openConsole(tab);
+
+    hud.jsterm.execute("console.log('bug1006027')");
+
+    yield waitForMessages({
+      webconsole: hud,
+      messages: [{
+        name: "console.log",
+        text: "bug1006027",
+        category: CATEGORY_WEBDEV,
+        severity: SEVERITY_LOG,
+      }],
+    });
+
+    info('hud.outputNode.textContent:\n'+hud.outputNode.textContent);
+    let timestampNodes = hud.outputNode.querySelectorAll('span.timestamp');
+    let aTimestampMilliseconds = Array.prototype.map.call(timestampNodes,
+      function (value) {
+         // We are parsing timestamps as local time, relative to the begin of the epoch.
+         // This is not the correct value of the timestamp, but good enough for comparison.
+         return Date.parse('T'+String.trim(value.textContent));
+      });
+
+    let minTimestamp = Math.min.apply(null, aTimestampMilliseconds);
+    let maxTimestamp = Math.max.apply(null, aTimestampMilliseconds);
+    ok(Math.abs(maxTimestamp - minTimestamp) < 1000, "console.log message timestamp spread < 1000ms confirmed");
+  }
+}
+
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1019,32 +1019,110 @@ endif
 $(filter %.s,$(CPPSRCS:%.cc=%.s)): %.s: %.cc $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD)
 	$(CCC) -S $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
 $(filter %.s,$(CSRCS:%.c=%.s)): %.s: %.c $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD)
 	$(CC) -S $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
-$(filter %.i,$(CPPSRCS:%.cpp=%.i)): %.i: %.cpp $(call mkdir_deps,$(MDDEPDIR))
+ifneq (,$(filter %.i,$(MAKECMDGOALS)))
+# Call as $(call _group_srcs,extension,$(SRCS)) - this will create a list
+# of the full sources, as well as the $(notdir) version. So:
+#   foo.cpp sub/bar.cpp
+# becomes:
+#   foo.cpp sub/bar.cpp bar.cpp
+#
+# This way we can match both 'make sub/bar.i' and 'make bar.i'
+_group_srcs = $(sort $(patsubst %.$1,%.i,$(filter %.$1,$2 $(notdir $2))))
+_PREPROCESSED_CPP_FILES := $(call _group_srcs,cpp,$(CPPSRCS))
+_PREPROCESSED_CC_FILES := $(call _group_srcs,cc,$(CPPSRCS))
+_PREPROCESSED_C_FILES := $(call _group_srcs,c,$(CSRCS))
+_PREPROCESSED_CMM_FILES := $(call _group_srcs,mm,$(CMMSRCS))
+
+# Hack up VPATH so we can reach the sources. Eg: 'make Parser.i' may need to
+# reach $(srcdir)/frontend/Parser.i
+VPATH += $(addprefix $(srcdir)/,$(sort $(dir $(CPPSRCS) $(CSRCS) $(CMMSRCS))))
+
+# Make preprocessed files PHONY so they are always executed, since they are
+# manual targets and we don't necessarily write to $@.
+.PHONY: $(_PREPROCESSED_CPP_FILES) $(_PREPROCESSED_CC_FILES) $(_PREPROCESSED_C_FILES) $(_PREPROCESSED_CMM_FILES)
+
+$(_PREPROCESSED_CPP_FILES): %.i: %.cpp $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD)
+	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
 	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
-$(filter %.i,$(CPPSRCS:%.cc=%.i)): %.i: %.cc $(call mkdir_deps,$(MDDEPDIR))
+$(_PREPROCESSED_CC_FILES): %.i: %.cc $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD)
+	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
 	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
-$(filter %.i,$(CSRCS:%.c=%.i)): %.i: %.c $(call mkdir_deps,$(MDDEPDIR))
+$(_PREPROCESSED_C_FILES): %.i: %.c $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD)
+	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
 	$(CC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
-$(filter %.i,$(CMMSRCS:%.mm=%.i)): %.i: %.mm $(call mkdir_deps,$(MDDEPDIR))
+$(_PREPROCESSED_CMM_FILES): %.i: %.mm $(call mkdir_deps,$(MDDEPDIR))
 	$(REPORT_BUILD)
+	$(addprefix $(MKDIR) -p ,$(filter-out .,$(@D)))
 	$(CCC) -C $(PREPROCESS_OPTION)$@ $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $($(notdir $<)_FLAGS) $(TARGET_LOCAL_INCLUDES) $(_VPATH_SRCS)
 
+# Default to pre-processing the actual unified file. This can be overridden
+# at the command-line to pre-process only the individual source file.
+PP_UNIFIED ?= 1
+
+# PP_REINVOKE gets set on the sub-make to prevent us from going in an
+# infinite loop if the filename doesn't exist in the unified source files.
+ifndef PP_REINVOKE
+
+MATCH_cpp = \(cpp\|cc\)
+UPPER_c = C
+UPPER_cpp = CPP
+UPPER_mm = CMM
+
+# When building with PP_UNIFIED=0, we also have to look in the Unified files to
+# find a matching pathname.
+_get_all_sources = $1 $(if $(filter Unified%,$1),$(shell sed -n 's/\#include "\(.*\)"$$/\1/p' $(filter Unified%,$1)))
+all_cpp_sources := $(call _get_all_sources,$(CPPSRCS))
+all_mm_sources := $(call _get_all_sources,$(CMMSRCS))
+all_c_sources := $(call _get_all_sources,$(CSRCS))
+all_sources := $(all_cpp_sources) $(all_cmm_sources) $(all_c_sources)
+
+# The catch-all %.i rule runs when we pass in a .i filename that doesn't match
+# one of the *SRCS variables. The two code paths depend on whether or not
+# we are requesting a unified file (PP_UNIFIED=1, the default) or not:
+#
+# PP_UNIFIED=1:
+#  - Look for it in any of the Unified files, and re-exec make with
+#    Unified_foo0.i as the target. This gets us the full unified preprocessed
+#    file.
+#
+# PP_UNIFIED=0:
+#  - If the .i filename is in *SRCS, or in a Unified filename, then we re-exec
+#    make with that filename as the target. The *SRCS variables are modified
+#    to have the Unified sources appended to them so that the static pattern
+#    rules will match.
+%.i: FORCE
+ifeq ($(PP_UNIFIED),1)
+	@$(MAKE) PP_REINVOKE=1 \
+	    $(or $(addsuffix .i, \
+              $(foreach type,c cpp mm, \
+	        $(if $(filter Unified%,$($(UPPER_$(type))SRCS)), \
+	          $(shell grep -l '#include "\(.*/\)\?$(basename $@).$(or $(MATCH_$(type)),$(type))"' Unified*.$(type) | sed 's/\.$(type)$$//') \
+            ))),$(error "File not found for preprocessing: $@"))
+else
+	@$(MAKE) PP_REINVOKE=1 $@ \
+	    $(foreach type,c cpp mm,$(UPPER_$(type))SRCS="$(all_$(type)_sources)")
+endif
+
+endif
+
+endif
+
 $(RESFILE): %.res: %.rc
 	$(REPORT_BUILD)
 	@echo Creating Resource file: $@
 ifdef GNU_CC
 	$(RC) $(RCFLAGS) $(filter-out -U%,$(DEFINES)) $(INCLUDES:-I%=--include-dir %) $(OUTOPTION)$@ $(_VPATH_SRCS)
 else
 	$(RC) $(RCFLAGS) -r $(DEFINES) $(INCLUDES) $(OUTOPTION)$@ $(_VPATH_SRCS)
 endif
--- a/config/system-headers
+++ b/config/system-headers
@@ -1310,19 +1310,16 @@ SelectMultipleContentItemsPage.h
 QtSparql/qsparqlconnection.h
 QtSparql/qsparqlquery.h
 QtSparql/qsparqlresult.h
 #endif
 
 #if MOZ_TREE_PIXMAN!=1
 pixman.h
 #endif
-#if MOZ_ENABLE_MEEGOTOUCHSHARE
-shareuiinterface.h
-#endif
 #if MOZ_NATIVE_LIBVPX==1
 vpx/vpx_codec.h
 vpx/vpx_decoder.h
 vpx/vpx_encoder.h
 vpx/vp8cx.h
 vpx/vp8dx.h
 vpx_mem/vpx_mem.h
 #endif
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -742,16 +742,17 @@ GK_ATOM(oniccdetected, "oniccdetected")
 GK_ATOM(oniccinfochange, "oniccinfochange")
 GK_ATOM(oniccundetected, "oniccundetected")
 GK_ATOM(onincoming, "onincoming")
 GK_ATOM(oninput, "oninput")
 GK_ATOM(oninvalid, "oninvalid")
 GK_ATOM(onkeydown, "onkeydown")
 GK_ATOM(onkeypress, "onkeypress")
 GK_ATOM(onkeyup, "onkeyup")
+GK_ATOM(onlanguagechange, "onlanguagechange")
 GK_ATOM(onlevelchange, "onlevelchange")
 GK_ATOM(onLoad, "onLoad")
 GK_ATOM(onload, "onload")
 GK_ATOM(onpopstate, "onpopstate")
 GK_ATOM(only, "only")               // this one is not an event
 GK_ATOM(onmessage, "onmessage")
 GK_ATOM(onmousedown, "onmousedown")
 GK_ATOM(onmouseenter, "onmouseenter")
--- a/content/media/MediaCache.cpp
+++ b/content/media/MediaCache.cpp
@@ -1854,34 +1854,42 @@ MediaCacheStream::NotifyDataEnded(nsresu
     // Disconnect from other streams sharing our resource, since they
     // should continue trying to load. Our load might have been deliberately
     // canceled and that shouldn't affect other streams.
     mResourceID = gMediaCache->AllocateResourceID();
   }
 
   FlushPartialBlockInternal(true);
 
-  if (!mDidNotifyDataEnded) {
-    MediaCache::ResourceStreamIterator iter(mResourceID);
-    while (MediaCacheStream* stream = iter.Next()) {
-      if (NS_SUCCEEDED(aStatus)) {
-        // We read the whole stream, so remember the true length
-        stream->mStreamLength = mChannelOffset;
-      }
-      NS_ASSERTION(!stream->mDidNotifyDataEnded, "Stream already ended!");
+  MediaCache::ResourceStreamIterator iter(mResourceID);
+  while (MediaCacheStream* stream = iter.Next()) {
+    if (NS_SUCCEEDED(aStatus)) {
+      // We read the whole stream, so remember the true length
+      stream->mStreamLength = mChannelOffset;
+    }
+    if (!stream->mDidNotifyDataEnded) {
       stream->mDidNotifyDataEnded = true;
       stream->mNotifyDataEndedStatus = aStatus;
       stream->mClient->CacheClientNotifyDataEnded(aStatus);
     }
   }
 
   mChannelEnded = true;
   gMediaCache->QueueUpdate();
 }
 
+void
+MediaCacheStream::NotifyChannelRecreated()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
+  mChannelEnded = false;
+  mDidNotifyDataEnded = false;
+}
+
 MediaCacheStream::~MediaCacheStream()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mPinCount, "Unbalanced Pin");
 
   if (gMediaCache) {
     NS_ASSERTION(mClosed, "Stream was not closed");
     gMediaCache->ReleaseStream(this);
--- a/content/media/MediaCache.h
+++ b/content/media/MediaCache.h
@@ -267,16 +267,20 @@ public:
   void NotifyDataReceived(int64_t aSize, const char* aData,
                           nsIPrincipal* aPrincipal);
   // Notifies the cache that the current bytes should be written to disk.
   // Called on the main thread.
   void FlushPartialBlock();
   // Notifies the cache that the channel has closed with the given status.
   void NotifyDataEnded(nsresult aStatus);
 
+  // Notifies the stream that the channel is reopened. The stream should
+  // reset variables such as |mDidNotifyDataEnded|.
+  void NotifyChannelRecreated();
+
   // These methods can be called on any thread.
   // Cached blocks associated with this stream will not be evicted
   // while the stream is pinned.
   void Pin();
   void Unpin();
   // See comments above for NotifyDataLength about how the length
   // can vary over time. Returns -1 if no length is known. Returns the
   // reported length if we haven't got any better information. If
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -482,18 +482,17 @@ ChannelMediaResource::OnStopRequest(nsIR
   return NS_OK;
 }
 
 nsresult
 ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
                                         uint32_t aFlags)
 {
   mChannel = aNew;
-  SetupChannelHeaders();
-  return NS_OK;
+  return SetupChannelHeaders();
 }
 
 struct CopySegmentClosure {
   nsCOMPtr<nsIPrincipal> mPrincipal;
   ChannelMediaResource*  mResource;
 };
 
 NS_METHOD
@@ -604,55 +603,57 @@ nsresult ChannelMediaResource::OpenChann
 
   mListener = new Listener(this);
   NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
 
   if (aStreamListener) {
     *aStreamListener = mListener;
     NS_ADDREF(*aStreamListener);
   } else {
-    mChannel->SetNotificationCallbacks(mListener.get());
+    nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
+    NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStreamListener> listener = mListener.get();
 
     // Ensure that if we're loading cross domain, that the server is sending
     // an authorizing Access-Control header.
     MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
     NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     dom::HTMLMediaElement* element = owner->GetMediaElement();
     NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     if (element->ShouldCheckAllowOrigin()) {
       nsRefPtr<nsCORSListenerProxy> crossSiteListener =
         new nsCORSListenerProxy(mListener,
                                 element->NodePrincipal(),
                                 false);
-      nsresult rv = crossSiteListener->Init(mChannel);
+      NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
+      rv = crossSiteListener->Init(mChannel);
+      NS_ENSURE_SUCCESS(rv, rv);
       listener = crossSiteListener;
-      NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
-      NS_ENSURE_SUCCESS(rv, rv);
     } else {
-      nsresult rv = nsContentUtils::GetSecurityManager()->
+      rv = nsContentUtils::GetSecurityManager()->
         CheckLoadURIWithPrincipal(element->NodePrincipal(),
                                   mURI,
                                   nsIScriptSecurityManager::STANDARD);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    SetupChannelHeaders();
+    rv = SetupChannelHeaders();
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsresult rv = mChannel->AsyncOpen(listener, nullptr);
+    rv = mChannel->AsyncOpen(listener, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
     // Tell the media element that we are fetching data from a channel.
     element->DownloadResumed(true);
   }
 
   return NS_OK;
 }
 
-void ChannelMediaResource::SetupChannelHeaders()
+nsresult ChannelMediaResource::SetupChannelHeaders()
 {
   // Always use a byte range request even if we're reading from the start
   // of the resource.
   // This enables us to detect if the stream supports byte range
   // requests, and therefore seeking, early.
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
   if (hc) {
     // Use |mByteRange| for a specific chunk, or |mOffset| if seeking in a
@@ -663,32 +664,31 @@ void ChannelMediaResource::SetupChannelH
       mOffset = mByteRange.mStart;
     } else {
       rangeString.AppendInt(mOffset);
     }
     rangeString.Append("-");
     if (!mByteRange.IsNull()) {
       rangeString.AppendInt(mByteRange.mEnd);
     }
-    hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
+    nsresult rv = hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     // Send Accept header for video and audio types only (Bug 489071)
     NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
     MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
-    if (!owner) {
-      return;
-    }
+    NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     dom::HTMLMediaElement* element = owner->GetMediaElement();
-    if (!element) {
-      return;
-    }
+    NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
     element->SetRequestHeaders(hc);
   } else {
     NS_ASSERTION(mOffset == 0, "Don't know how to seek on this channel type");
+    return NS_ERROR_FAILURE;
   }
+  return NS_OK;
 }
 
 nsresult ChannelMediaResource::Close()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   mCacheStream.Close();
   CloseChannel();
@@ -941,16 +941,19 @@ ChannelMediaResource::RecreateChannel()
   // We have cached the Content-Type, which should not change. Give a hint to
   // the channel to avoid a sniffing failure, which would be expected because we
   // are probably seeking in the middle of the bitstream, and sniffing relies
   // on the presence of a magic number at the beginning of the stream.
   NS_ASSERTION(!GetContentType().IsEmpty(),
       "When recreating a channel, we should know the Content-Type.");
   mChannel->SetContentType(GetContentType());
 
+  // Tell the cache to reset the download status when the channel is reopened.
+  mCacheStream.NotifyChannelRecreated();
+
   return rv;
 }
 
 void
 ChannelMediaResource::DoNotifyDataReceived()
 {
   mDataReceivedEvent.Revoke();
   mDecoder->NotifyBytesDownloaded();
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -634,17 +634,17 @@ protected:
                            uint32_t aCount);
   nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, uint32_t aFlags);
 
   // Opens the channel, using an HTTP byte range request to start at mOffset
   // if possible. Main thread only.
   nsresult OpenChannel(nsIStreamListener** aStreamListener);
   nsresult RecreateChannel();
   // Add headers to HTTP request. Main thread only.
-  void SetupChannelHeaders();
+  nsresult SetupChannelHeaders();
   // Closes the channel. Main thread only.
   void CloseChannel();
 
   // Parses 'Content-Range' header and returns results via parameters.
   // Returns error if header is not available, values are not parse-able or
   // values are out of range.
   nsresult ParseContentRangeHeader(nsIHttpChannel * aHttpChan,
                                    int64_t& aRangeStart,
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -57,24 +57,26 @@ AudioOutputObserver::AudioOutputObserver
   , mSamplesSaved(0)
 {
   // Buffers of 10ms chunks
   mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
 }
 
 AudioOutputObserver::~AudioOutputObserver()
 {
+  Clear();
 }
 
 void
 AudioOutputObserver::Clear()
 {
   while (mPlayoutFifo->size() > 0) {
     (void) mPlayoutFifo->Pop();
   }
+  mSaved = nullptr;
 }
 
 FarEndAudioChunk *
 AudioOutputObserver::Pop()
 {
   return (FarEndAudioChunk *) mPlayoutFifo->Pop();
 }
 
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -19,20 +19,18 @@
 #include "nsOSHelperAppService.h"
 #include "nsExternalProtocolHandler.h"
 #include "nsPrefetchService.h"
 #include "nsOfflineCacheUpdate.h"
 #include "nsLocalHandlerApp.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "nsDBusHandlerApp.h"
 #endif 
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ENABLE_MEEGOTOUCHSHARE)
+#if defined(MOZ_WIDGET_ANDROID)
 #include "nsExternalSharingAppService.h"
-#endif
-#if defined(MOZ_WIDGET_ANDROID)
 #include "nsExternalURLHandlerService.h"
 #endif
 
 // session history
 #include "nsSHEntry.h"
 #include "nsSHEntryShared.h"
 #include "nsSHistory.h"
 #include "nsSHTransaction.h"
@@ -80,20 +78,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsExterna
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefetchService, Init)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsOfflineCacheUpdateService,
                                          nsOfflineCacheUpdateService::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsOfflineCacheUpdate)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PlatformLocalHandlerApp_t)
 #ifdef MOZ_ENABLE_DBUS
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDBusHandlerApp)
 #endif 
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ENABLE_MEEGOTOUCHSHARE)
+#if defined(MOZ_WIDGET_ANDROID)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsExternalSharingAppService)
-#endif
-#if defined(MOZ_WIDGET_ANDROID)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsExternalURLHandlerService)
 #endif
 
 // session history
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHEntry)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHTransaction)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHistory)
 
@@ -110,20 +106,18 @@ NS_DEFINE_NAMED_CID(NS_EXTERNALHELPERAPP
 NS_DEFINE_NAMED_CID(NS_EXTERNALPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_PREFETCHSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_OFFLINECACHEUPDATESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_OFFLINECACHEUPDATE_CID);
 NS_DEFINE_NAMED_CID(NS_LOCALHANDLERAPP_CID);
 #ifdef MOZ_ENABLE_DBUS
 NS_DEFINE_NAMED_CID(NS_DBUSHANDLERAPP_CID);
 #endif
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ENABLE_MEEGOTOUCHSHARE)
+#if defined(MOZ_WIDGET_ANDROID)
 NS_DEFINE_NAMED_CID(NS_EXTERNALSHARINGAPPSERVICE_CID);
-#endif
-#if defined(MOZ_WIDGET_ANDROID)
 NS_DEFINE_NAMED_CID(NS_EXTERNALURLHANDLERSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_SHENTRY_CID);
 NS_DEFINE_NAMED_CID(NS_SHTRANSACTION_CID);
 NS_DEFINE_NAMED_CID(NS_SHISTORY_CID);
 NS_DEFINE_NAMED_CID(NS_SHISTORY_INTERNAL_CID);
 NS_DEFINE_NAMED_CID(NS_DOWNLOADHISTORY_CID);
 
@@ -139,20 +133,18 @@ const mozilla::Module::CIDEntry kDocShel
   { &kNS_EXTERNALPROTOCOLHANDLER_CID, false, nullptr, nsExternalProtocolHandlerConstructor },
   { &kNS_PREFETCHSERVICE_CID, false, nullptr, nsPrefetchServiceConstructor },
   { &kNS_OFFLINECACHEUPDATESERVICE_CID, false, nullptr, nsOfflineCacheUpdateServiceConstructor },
   { &kNS_OFFLINECACHEUPDATE_CID, false, nullptr, nsOfflineCacheUpdateConstructor },
   { &kNS_LOCALHANDLERAPP_CID, false, nullptr, PlatformLocalHandlerApp_tConstructor },
 #ifdef MOZ_ENABLE_DBUS
   { &kNS_DBUSHANDLERAPP_CID, false, nullptr, nsDBusHandlerAppConstructor },
 #endif
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ENABLE_MEEGOTOUCHSHARE)
+#if defined(MOZ_WIDGET_ANDROID)
   { &kNS_EXTERNALSHARINGAPPSERVICE_CID, false, nullptr, nsExternalSharingAppServiceConstructor },
-#endif
-#if defined(MOZ_WIDGET_ANDROID)
   { &kNS_EXTERNALURLHANDLERSERVICE_CID, false, nullptr, nsExternalURLHandlerServiceConstructor },
 #endif
   { &kNS_SHENTRY_CID, false, nullptr, nsSHEntryConstructor },
   { &kNS_SHTRANSACTION_CID, false, nullptr, nsSHTransactionConstructor },
   { &kNS_SHISTORY_CID, false, nullptr, nsSHistoryConstructor },
   { &kNS_SHISTORY_INTERNAL_CID, false, nullptr, nsSHistoryConstructor },
   { &kNS_DOWNLOADHISTORY_CID, false, nullptr, nsDownloadHistoryConstructor },
   { nullptr }
@@ -192,20 +184,18 @@ const mozilla::Module::ContractIDEntry k
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default", &kNS_EXTERNALPROTOCOLHANDLER_CID },
   { NS_PREFETCHSERVICE_CONTRACTID, &kNS_PREFETCHSERVICE_CID },
   { NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &kNS_OFFLINECACHEUPDATESERVICE_CID },
   { NS_OFFLINECACHEUPDATE_CONTRACTID, &kNS_OFFLINECACHEUPDATE_CID },
   { NS_LOCALHANDLERAPP_CONTRACTID, &kNS_LOCALHANDLERAPP_CID },
 #ifdef MOZ_ENABLE_DBUS
   { NS_DBUSHANDLERAPP_CONTRACTID, &kNS_DBUSHANDLERAPP_CID },
 #endif
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ENABLE_MEEGOTOUCHSHARE)
+#if defined(MOZ_WIDGET_ANDROID)
   { NS_EXTERNALSHARINGAPPSERVICE_CONTRACTID, &kNS_EXTERNALSHARINGAPPSERVICE_CID },
-#endif
-#if defined(MOZ_WIDGET_ANDROID)
   { NS_EXTERNALURLHANDLERSERVICE_CONTRACTID, &kNS_EXTERNALURLHANDLERSERVICE_CID },
 #endif
   { NS_SHENTRY_CONTRACTID, &kNS_SHENTRY_CID },
   { NS_SHTRANSACTION_CONTRACTID, &kNS_SHTRANSACTION_CID },
   { NS_SHISTORY_CONTRACTID, &kNS_SHISTORY_CID },
   { NS_SHISTORY_INTERNAL_CONTRACTID, &kNS_SHISTORY_INTERNAL_CID },
   { NS_DOWNLOADHISTORY_CONTRACTID, &kNS_DOWNLOADHISTORY_CID },
   { nullptr }
--- a/dom/apps/src/PermissionsTable.jsm
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -321,16 +321,21 @@ this.PermissionsTable =  { geolocation: 
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            "video-capture": {
                              app: PROMPT_ACTION,
                              privileged: PROMPT_ACTION,
                              certified: PROMPT_ACTION
                            },
+                           "feature-detection": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
                          };
 
 /**
  * Append access modes to the permission name as suffixes.
  *   e.g. permission name 'contacts' with ['read', 'write'] =
  *   ['contacts-read', contacts-write']
  * @param string aPermName
  * @param array aAccess
--- a/dom/archivereader/ArchiveZipFile.cpp
+++ b/dom/archivereader/ArchiveZipFile.cpp
@@ -391,18 +391,18 @@ ArchiveZipFile::CreateSlice(uint64_t aSt
                                               mContentType,
                                               aStart,
                                               mLength,
                                               mCentral,
                                               mArchiveReader);
   return t.forget();
 }
 
-NS_IMPL_CYCLE_COLLECTION(ArchiveZipFile,
-                         mArchiveReader)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ArchiveZipFile, nsDOMFileCC,
+                                   mArchiveReader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ArchiveZipFile)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
   NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
   NS_INTERFACE_MAP_ENTRY(nsIMutable)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMFileCC)
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -136,17 +136,17 @@ JSStructuredCloneCallbacks gConsoleCallb
 };
 
 class ConsoleCallData MOZ_FINAL : public LinkedListElement<ConsoleCallData>
 {
 public:
   ConsoleCallData()
     : mMethodName(Console::MethodLog)
     , mPrivate(false)
-    , mTimeStamp(JS_Now())
+    , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
     , mMonotonicTimer(0)
   {
     MOZ_COUNT_CTOR(ConsoleCallData);
   }
 
   ~ConsoleCallData()
   {
     MOZ_COUNT_DTOR(ConsoleCallData);
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -346,69 +346,97 @@ Navigator::GetAppVersion(nsAString& aApp
 NS_IMETHODIMP
 Navigator::GetAppName(nsAString& aAppName)
 {
   NS_GetNavigatorAppName(aAppName);
   return NS_OK;
 }
 
 /**
- * JS property navigator.language, exposed to web content.
- * Take first value from Accept-Languages (HTTP header), which is
- * the "content language" freely set by the user in the Pref window.
- *
- * Do not use UI language (chosen app locale) here.
- * See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers"
+ * Returns the value of Accept-Languages (HTTP header) as a nsTArray of
+ * languages. The value is set in the preference by the user ("Content
+ * Languages").
  *
- * "en", "en-US" and "i-cherokee" and "" are valid.
- * Fallback in case of invalid pref should be "" (empty string), to
- * let site do fallback, e.g. to site's local language.
+ * "en", "en-US" and "i-cherokee" and "" are valid languages tokens.
+ *
+ * An empty array will be returned if there is no valid languages.
  */
-NS_IMETHODIMP
-Navigator::GetLanguage(nsAString& aLanguage)
+void
+Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages)
 {
   // E.g. "de-de, en-us,en".
   const nsAdoptingString& acceptLang =
     Preferences::GetLocalizedString("intl.accept_languages");
 
-  // Take everything before the first "," or ";", without trailing space.
+  // Split values on commas.
   nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
-  const nsSubstring &firstLangPart = langTokenizer.nextToken();
-  nsCharSeparatedTokenizer qTokenizer(firstLangPart, ';');
-  aLanguage.Assign(qTokenizer.nextToken());
-
-  // Checks and fixups:
-  // replace "_" with "-" to avoid POSIX/Windows "en_US" notation.
-  if (aLanguage.Length() > 2 && aLanguage[2] == char16_t('_')) {
-    aLanguage.Replace(2, 1, char16_t('-')); // TODO replace all
-  }
-
-  // Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47
-  // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe".
-  if (aLanguage.Length() <= 2) {
-    return NS_OK;
+  while (langTokenizer.hasMoreTokens()) {
+    nsDependentSubstring lang = langTokenizer.nextToken();
+
+    // Replace "_" with "-" to avoid POSIX/Windows "en_US" notation.
+    // NOTE: we should probably rely on the pref being set correctly.
+    if (lang.Length() > 2 && lang[2] == char16_t('_')) {
+      lang.Replace(2, 1, char16_t('-'));
+    }
+
+    // Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47
+    // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe".
+    // NOTE: we should probably rely on the pref being set correctly.
+    if (lang.Length() > 2) {
+      nsCharSeparatedTokenizer localeTokenizer(lang, '-');
+      int32_t pos = 0;
+      bool first = true;
+      while (localeTokenizer.hasMoreTokens()) {
+        const nsSubstring& code = localeTokenizer.nextToken();
+
+        if (code.Length() == 2 && !first) {
+          nsAutoString upper(code);
+          ToUpperCase(upper);
+          lang.Replace(pos, code.Length(), upper);
+        }
+
+        pos += code.Length() + 1; // 1 is the separator
+        first = false;
+      }
+    }
+
+    aLanguages.AppendElement(lang);
   }
-
-  nsCharSeparatedTokenizer localeTokenizer(aLanguage, '-');
-  int32_t pos = 0;
-  bool first = true;
-  while (localeTokenizer.hasMoreTokens()) {
-    const nsSubstring& code = localeTokenizer.nextToken();
-
-    if (code.Length() == 2 && !first) {
-      nsAutoString upper(code);
-      ToUpperCase(upper);
-      aLanguage.Replace(pos, code.Length(), upper);
-    }
-
-    pos += code.Length() + 1; // 1 is the separator
-    first = false;
+}
+
+/**
+ * Do not use UI language (chosen app locale) here but the first value set in
+ * the Accept Languages header, see ::GetAcceptLanguages().
+ *
+ * See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers" for
+ * the reasons why.
+ */
+NS_IMETHODIMP
+Navigator::GetLanguage(nsAString& aLanguage)
+{
+  nsTArray<nsString> languages;
+  GetLanguages(languages);
+  if (languages.Length() >= 1) {
+    aLanguage.Assign(languages[0]);
+  } else {
+    aLanguage.Truncate();
   }
 
-  return NS_OK;
+    return NS_OK;
+}
+
+void
+Navigator::GetLanguages(nsTArray<nsString>& aLanguages)
+{
+  GetAcceptLanguages(aLanguages);
+
+  // The returned value is cached by the binding code. The window listen to the
+  // accept languages change and will clear the cache when needed. It has to
+  // take care of dispatching the DOM event already and the invalidation and the
+  // event has to be timed correctly.
 }
 
 NS_IMETHODIMP
 Navigator::GetPlatform(nsAString& aPlatform)
 {
   return NS_GetNavigatorPlatform(aPlatform);
 }
 
@@ -1438,16 +1466,62 @@ Navigator::GetDataStores(nsPIDOMWindow* 
 }
 
 already_AddRefed<Promise>
 Navigator::GetDataStores(const nsAString& aName, ErrorResult& aRv)
 {
   return GetDataStores(mWindow, aName, aRv);
 }
 
+already_AddRefed<Promise>
+Navigator::GetFeature(const nsAString& aName)
+{
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
+  nsRefPtr<Promise> p = new Promise(go);
+
+#if defined(XP_LINUX)
+  if (aName.EqualsLiteral("hardware.memory")) {
+    static int memLevel = 1;
+    if (memLevel == 1) {
+      FILE* f = fopen("/proc/meminfo", "r");
+      if (!f) {
+        p->MaybeReject(NS_LITERAL_STRING("CannotOpenMeminfo"));
+        return p.forget();
+      }
+
+      int memTotal;
+      int n = fscanf(f, "MemTotal: %d kB\n", &memTotal);
+      fclose(f);
+
+      if (memTotal == 0 || n != 1) {
+        p->MaybeReject(NS_LITERAL_STRING("Abnormal"));
+        return p.forget();
+      }
+      // From KB to MB
+      memTotal /= 1024;
+
+      // round the value up to the next power of two
+      while (memLevel <= memTotal) {
+        memLevel *= 2;
+      }
+    }
+    p->MaybeResolve(memLevel);
+  } // hardware.memory
+  else
+#endif
+  {
+    // resolve with <undefined> because the feature name is not supported
+    p->MaybeResolve(JS::UndefinedHandleValue);
+  }
+
+  return p.forget();
+
+}
+
+
 PowerManager*
 Navigator::GetMozPower(ErrorResult& aRv)
 {
   if (!mPowerManager) {
     if (!mWindow) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
@@ -2412,16 +2486,25 @@ Navigator::HasPermissionSettingsSupport(
 bool
 Navigator::HasNetworkStatsSupport(JSContext* /* unused */, JSObject* aGlobal)
 {
   nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
   return CheckPermission(win, "networkstats-manage");
 }
 
 /* static */
+bool
+Navigator::HasFeatureDetectionSupport(JSContext* /* unused */, JSObject* aGlobal)
+{
+  nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
+  return CheckPermission(win, "feature-detection");
+}
+
+
+/* static */
 already_AddRefed<nsPIDOMWindow>
 Navigator::GetWindowFromGlobal(JSObject* aGlobal)
 {
   nsCOMPtr<nsPIDOMWindow> win =
     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(aGlobal));
   MOZ_ASSERT(!win || win->IsInnerWindow());
   return win.forget();
 }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -159,16 +159,20 @@ public:
   battery::BatteryManager* GetBattery(ErrorResult& aRv);
 
   static already_AddRefed<Promise> GetDataStores(nsPIDOMWindow* aWindow,
                                                  const nsAString& aName,
                                                  ErrorResult& aRv);
 
   already_AddRefed<Promise> GetDataStores(const nsAString &aName,
                                           ErrorResult& aRv);
+
+  // Feature Detection API
+  already_AddRefed<Promise> GetFeature(const nsAString &aName);
+
   bool Vibrate(uint32_t aDuration);
   bool Vibrate(const nsTArray<uint32_t>& aDuration);
   uint32_t MaxTouchPoints();
   void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)
   {
     aRv = GetAppCodeName(aAppCodeName);
   }
   void GetOscpu(nsString& aOscpu, ErrorResult& aRv)
@@ -246,16 +250,18 @@ public:
                               uint64_t aInnerWindowID,
                               ErrorResult& aRv);
 #endif // MOZ_MEDIA_NAVIGATOR
   bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
                     JS::Handle<jsid> aId,
                     JS::MutableHandle<JSPropertyDescriptor> aDesc);
   void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
                            ErrorResult& aRv);
+  void GetLanguages(nsTArray<nsString>& aLanguages);
+  void GetAcceptLanguages(nsTArray<nsString>& aLanguages);
 
   // WebIDL helper methods
   static bool HasBatterySupport(JSContext* /* unused*/, JSObject* /*unused */);
   static bool HasPowerSupport(JSContext* /* unused */, JSObject* aGlobal);
   static bool HasPhoneNumberSupport(JSContext* /* unused */, JSObject* aGlobal);
   static bool HasIdleSupport(JSContext* /* unused */, JSObject* aGlobal);
   static bool HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */);
   static bool HasDesktopNotificationSupport(JSContext* /* unused*/,
@@ -310,16 +316,18 @@ public:
   static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal);
 
   static bool HasDownloadsSupport(JSContext* aCx, JSObject* aGlobal);
 
   static bool HasPermissionSettingsSupport(JSContext* aCx, JSObject* aGlobal);
 
   static bool HasNetworkStatsSupport(JSContext* aCx, JSObject* aGlobal);
 
+  static bool HasFeatureDetectionSupport(JSContext* aCx, JSObject* aGlobal);
+
   nsPIDOMWindow* GetParentObject() const
   {
     return GetWindow();
   }
 
   virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
 
 private:
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -565,24 +565,16 @@ WrapNative(JSContext *cx, nsISupports *n
 // Same as the WrapNative above, but use these if aIID is nsISupports' IID.
 static inline nsresult
 WrapNative(JSContext *cx, nsISupports *native,
            bool aAllowWrapping, JS::MutableHandle<JS::Value> vp)
 {
   return WrapNative(cx, native, nullptr, nullptr, vp, aAllowWrapping);
 }
 
-static inline nsresult
-WrapNative(JSContext *cx, nsISupports *native,
-           nsWrapperCache *cache, bool aAllowWrapping,
-           JS::MutableHandle<JS::Value> vp)
-{
-  return WrapNative(cx, native, cache, nullptr, vp, aAllowWrapping);
-}
-
 // Helper to handle torn-down inner windows.
 static inline nsresult
 SetParentToWindow(nsGlobalWindow *win, JSObject **parent)
 {
   MOZ_ASSERT(win);
   MOZ_ASSERT(win->IsInnerWindow());
   *parent = win->FastGetGlobalJSObject();
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -213,16 +213,17 @@
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/NavigatorBinding.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
@@ -1132,16 +1133,18 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
         // a strong reference.
         os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
                         false);
 
         // Watch for dom-storage2-changed so we can fire storage
         // events. Use a strong reference.
         os->AddObserver(mObserver, "dom-storage2-changed", false);
       }
+
+      Preferences::AddStrongObserver(mObserver, "intl.accept_languages");
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window, so freeze this
     // outer window here.
     Freeze();
 
     mObserver = nullptr;
@@ -1414,16 +1417,18 @@ nsGlobalWindow::CleanUp()
     DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
     DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
 #endif // MOZ_B2G
 
     if (mIdleService) {
       mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
     }
 
+    Preferences::RemoveObserver(mObserver, "intl.accept_languages");
+
     // Drop its reference to this dying window, in case for some bogus reason
     // the object stays around.
     mObserver->Forget();
     NS_RELEASE(mObserver);
   }
 
   if (mNavigator) {
     mNavigator->Invalidate();
@@ -11214,16 +11219,42 @@ nsGlobalWindow::Observe(nsISupports* aSu
 
     event->SetTrusted(true);
 
     bool dummy;
     return DispatchEvent(event, &dummy);
   }
 #endif // MOZ_B2G
 
+  if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+    MOZ_ASSERT(!nsCRT::strcmp(NS_ConvertUTF16toUTF8(aData).get(), "intl.accept_languages"));
+    MOZ_ASSERT(IsInnerWindow());
+
+    // The user preferred languages have changed, we need to fire an event on
+    // Window object and invalidate the cache for navigator.languages. It is
+    // done for every change which can be a waste of cycles but those should be
+    // fairly rare.
+    // We MUST invalidate navigator.languages before sending the event in the
+    // very likely situation where an event handler will try to read its value.
+
+    if (mNavigator) {
+      NavigatorBinding::ClearCachedLanguagesValue(mNavigator);
+    }
+
+    nsCOMPtr<nsIDOMEvent> event;
+    NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
+    nsresult rv = event->InitEvent(NS_LITERAL_STRING("languagechange"), false, false);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    event->SetTrusted(true);
+
+    bool dummy;
+    return DispatchEvent(event, &dummy);
+  }
+
   NS_WARNING("unrecognized topic in nsGlobalWindow::Observe");
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsGlobalWindow::CloneStorageEvent(const nsAString& aType,
                                   nsCOMPtr<nsIDOMStorageEvent>& aEvent)
 {
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -26,30 +26,33 @@ support-files =
 [test_domwindowutils.html]
 [test_e4x_for_each.html]
 [test_error.html]
 [test_getTranslationNodes.html]
 [test_getTranslationNodes_limit.html]
 [test_gsp-qualified.html]
 [test_gsp-quirks.html]
 [test_gsp-standards.html]
+[test_getFeature_with_perm.html]
+[test_getFeature_without_perm.html]
 [test_history_document_open.html]
 [test_history_state_null.html]
 [test_innersize_scrollport.html]
 [test_messageChannel.html]
 [test_messageChannel_cloning.html]
 [test_messageChannel_pingpong.html]
 [test_messageChannel_post.html]
 [test_messageChannel_pref.html]
 [test_messageChannel_start.html]
 [test_messagemanager_targetchain.html]
 [test_messageChannel_transferable.html]
 [test_messageChannel_unshipped.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
+[test_navigator_language.html]
 [test_nondomexception.html]
 [test_openDialogChromeOnly.html]
 [test_postMessage_solidus.html]
 [test_screen_orientation.html]
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setting_opener.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_getFeature_with_perm.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=979109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 979109</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983502">Mozilla Bug 983502</a>
+<script type="application/javascript">
+
+function testSupported() {
+  var mem;
+  navigator.getFeature("hardware.memory").then(function(mem) {
+
+    var isLinux = (navigator.platform.indexOf('Linux') != -1);
+    var isAndroid = !!navigator.userAgent.contains("Android");
+    var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent);
+
+    if (isLinux) {
+      info("It is Linux version:");
+    }
+    if (isAndroid) {
+      info("It is Android version");
+    }
+    if (isB2G) {
+      info("It is B2G version");
+    }
+
+    if (isLinux || isAndroid || isB2G) {
+      ok(typeof mem === 'number' && (mem) % 1 === 0, "We should receive an integer on this platform");
+      ok(mem > 0, "hardware.memory is supported on this platform. mem=" + mem + "MiB");
+    } else {
+      ok(typeof mem === 'undefined', "hardware.memory is not support on this platform");
+    }
+
+    SimpleTest.finish();
+
+  },function(mem) {
+    ok(false, "The Promise should not be rejected");
+  });
+}
+
+function testNotSupported() {
+  var tv;
+  navigator.getFeature("hardware.tv").then(function(tv) {
+    ok(typeof tv === 'undefined', "Resolve the Promise with undefined value (hardware.tv)");
+    testSupported();
+  },function(tv) {
+    ok(false, "The Promise should not be rejected")
+  });
+}
+
+SpecialPowers.pushPermissions([
+  {type: "feature-detection", allow: 1, context: document}
+], function() {
+  ok('getFeature' in navigator, "navigator.getFeature should exist");
+  testNotSupported();
+  ok(true, "Test DONE");
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_getFeature_without_perm.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=979109
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 979109</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=983502">Mozilla Bug 983502</a>
+<script type="application/javascript">
+
+
+is("getFeature" in navigator, false, "getFeature should not exist without permission");
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_navigator_language.html
@@ -0,0 +1,227 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=889335
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for NavigatorLanguage</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=889335">Mozilla Bug 889335</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript;version=1.7">
+  "use strict";
+
+  SimpleTest.waitForExplicitFinish();
+
+  /** Test for NavigatorLanguage **/
+  var prefValue = null;
+  var actualLanguageChangesFromHandler = 0;
+  var actualLanguageChangesFromAVL = 0;
+  var expectedLanguageChanges = 0;
+
+  function setUp() {
+    try {
+      prefValue = SpecialPowers.getCharPref('intl.accept_languages');
+    } catch (e) {
+    }
+  }
+
+  function tearDown() {
+    SpecialPowers.setCharPref('intl.accept_languages', prefValue);
+  }
+
+  var testValues = [
+    { accept_languages: 'foo', language: 'foo', languages: ['foo'] },
+    { accept_languages: '', language: '', languages: [] },
+    { accept_languages: 'foo,bar', language: 'foo', languages: [ 'foo', 'bar' ] },
+    { accept_languages: '  foo , bar ', language: 'foo', languages: [ 'foo', 'bar' ] },
+    { accept_languages: '  foo ; bar ', language: 'foo ; bar', languages: [ 'foo ; bar' ] },
+    { accept_languages: '_foo_', language: '_foo_', languages: ['_foo_'] },
+    { accept_languages: 'en_', language: 'en-', languages: ['en-'] },
+    { accept_languages: 'en__', language: 'en-_', languages: ['en-_'] },
+    { accept_languages: 'en_US, fr_FR', language: 'en-US', languages: ['en-US', 'fr-FR'] },
+    { accept_languages: 'en_US_CA', language: 'en-US_CA', languages: ['en-US_CA'] },
+    { accept_languages: 'en_us-ca', language: 'en-US-CA', languages: ['en-US-CA'] },
+    { accept_languages: 'en_us-cal, en_us-c', language: 'en-US-cal', languages: ['en-US-cal', 'en-US-c'] },
+  ];
+
+  var currentTestIdx = 0;
+  var tests = [];
+  function nextTest() {
+    currentTestIdx++;
+    if (currentTestIdx >= tests.length) {
+      tearDown();
+      SimpleTest.finish();
+    }
+
+    tests[currentTestIdx]();
+  }
+
+  // Check that the API is there.
+  tests.push(function testAPIPresence() {
+    ok('language' in window.navigator);
+    ok('languages' in window.navigator);
+    ok('onlanguagechange' in window);
+
+    nextTest();
+  });
+
+  // Check that calling navigator.languages return the same array, unless there
+  // was a change.
+  tests.push(function testArrayCached() {
+    var previous = navigator.languages;
+    is(navigator.languages, navigator.languages, "navigator.languages is cached");
+    is(navigator.languages, previous, "navigator.languages is cached");
+
+    window.onlanguagechange = function() {
+      isnot(navigator.languages, previous, "navigator.languages cached value was updated");
+      window.onlanguagechange = null;
+
+      nextTest();
+    }
+
+    setTimeout(function() {
+      SpecialPowers.setCharPref('intl.accept_languages', 'testArrayCached');
+    }, 0);
+  });
+
+  // Test that event handler inside the <body> works as expected and that the
+  // event has the expected properties.
+  tests.push(function testEventProperties() {
+    document.body.setAttribute('onlanguagechange',
+      "document.body.removeAttribute('onlanguagechange');" +
+      "is(event.cancelable, false); is(event.bubbles, false);" +
+      "nextTest();");
+
+    setTimeout(function() {
+      SpecialPowers.setCharPref('intl.accept_languages', 'testEventProperties');
+    }, 0);
+  });
+
+  // Check that the returned values such as the behavior when the underlying
+  // languages change.
+  tests.push(function testBasicBehaviour() {
+    function checkIfDoneAndProceed() {
+      if (actualLanguageChangesFromHandler == actualLanguageChangesFromAVL) {
+        if (genEvents.next().done) {
+          window.onlanguagechange = null;
+          window.removeEventListener('languagechange', languageChangeAVL);
+          nextTest();
+        }
+      }
+    }
+    window.onlanguagechange = function() {
+      actualLanguageChangesFromHandler++;
+      checkIfDoneAndProceed();
+    }
+    function languageChangeAVL() {
+      actualLanguageChangesFromAVL++;
+      checkIfDoneAndProceed();
+    }
+    window.addEventListener('languagechange', languageChangeAVL);
+
+    function* testEvents() {
+      for (var i = 0; i < testValues.length; ++i) {
+        var data = testValues[i];
+        setTimeout(function(data) {
+          SpecialPowers.setCharPref('intl.accept_languages', data.accept_languages);
+        }, 0, data);
+        expectedLanguageChanges++;
+        yield undefined;
+
+        is(actualLanguageChangesFromAVL, expectedLanguageChanges);
+        is(actualLanguageChangesFromHandler, expectedLanguageChanges);
+
+        is(navigator.language, data.language);
+        is(navigator.languages.length, data.languages.length);
+        if (navigator.languages.length > 0) {
+          is(navigator.languages[0], navigator.language)
+        }
+        for (var j = 0; j < navigator.languages.length; ++j) {
+          is(navigator.languages[j], data.languages[j]);
+        }
+      }
+    }
+
+    var genEvents = testEvents();
+    genEvents.next();
+  });
+
+  // Check that the orientationchange event isn't sent twice if the preference
+  // is set to the same value.
+  tests.push(function testOnlyFireIfRealChange() {
+    function* changeLanguage() {
+      setTimeout(function() {
+        SpecialPowers.setCharPref('intl.accept_languages', 'fr-CA');
+      });
+      yield undefined;
+
+      setTimeout(function() {
+        // Twice the same change, should fire only one event.
+        SpecialPowers.setCharPref('intl.accept_languages', 'fr-CA');
+        setTimeout(function() {
+          // A real change to tell the test it should now count how many changes were
+          // received until now.
+          SpecialPowers.setCharPref('intl.accept_languages', 'fr-FR');
+        });
+      });
+      yield undefined;
+    }
+
+    var genChanges = changeLanguage();
+
+    var doubleEventCount = 0;
+    window.onlanguagechange = function() {
+      if (navigator.language == 'fr-FR') {
+        is(1, doubleEventCount);
+        window.onlanguagechange = null;
+        nextTest();
+        return;
+      }
+
+      if (navigator.language == 'fr-CA') {
+        doubleEventCount++;
+      }
+      genChanges.next();
+    }
+
+    genChanges.next();
+  });
+
+  // Check that there is no crash when a change happen after a window listening
+  // to them is killed.
+  tests.push(function testThatAddingAnEventDoesNotHaveSideEffects() {
+    var frame = document.createElement('iframe');
+    frame.src = 'data:text/html,<script>window.onlanguagechange=function(){}<\/script>';
+    document.body.appendChild(frame);
+
+    frame.contentWindow.onload = function() {
+      document.body.removeChild(frame);
+      frame = null;
+
+      SpecialPowers.exactGC(window, function() {
+        // This should not crash.
+        SpecialPowers.setCharPref('intl.accept_languages', 'en-GB');
+
+        nextTest();
+      });
+    }
+  });
+
+  // There is one test using document.body.
+  addLoadEvent(function() {
+    setUp();
+    tests[0]();
+  });
+
+</script>
+</body>
+</html>
--- a/dom/datastore/DataStore.cpp
+++ b/dom/datastore/DataStore.cpp
@@ -17,17 +17,17 @@ namespace dom {
 
 NS_IMPL_ADDREF_INHERITED(DataStore, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(DataStore, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DataStore)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_CYCLE_COLLECTION(DataStore, mStore)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(DataStore, DOMEventTargetHelper, mStore)
 
 DataStore::DataStore(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
 {
 }
 
 already_AddRefed<DataStore>
 DataStore::Constructor(GlobalObject& aGlobal, ErrorResult& aRv)
@@ -180,9 +180,9 @@ DataStore::SetDataStoreImpl(DataStoreImp
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mStore);
 
   mStore = &aStore;
   mStore->SetEventTarget(*this, aRv);
 }
 
 } //namespace dom
-} //namespace mozilla
\ No newline at end of file
+} //namespace mozilla
--- a/dom/datastore/tests/file_basic.html
+++ b/dom/datastore/tests/file_basic.html
@@ -1,150 +1,31 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for DataStore - basic operation on a readonly db</title>
+  <script type="text/javascript" src="file_basic_common.js"></script>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
-  var gStore;
-
   function is(a, b, msg) {
     alert((a === b ? 'OK' : 'KO') + ' ' + msg)
   }
 
   function ok(a, msg) {
     alert((a ? 'OK' : 'KO')+ ' ' + msg)
   }
 
   function cbError() {
     alert('KO error');
   }
 
   function finish() {
     alert('DONE');
   }
 
-  function testGetDataStores() {
-    navigator.getDataStores('foo').then(function(stores) {
-      is(stores.length, 1, "getDataStores('foo') returns 1 element");
-      is(stores[0].name, 'foo', 'The dataStore.name is foo');
-      is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
-
-      var store = stores[0];
-      ok("get" in store, "store.get exists");
-      ok("put" in store, "store.put exists");
-      ok("add" in store, "store.add exists");
-      ok("remove" in store, "store.remove exists");
-      ok("clear" in store, "store.clear exists");
-      ok("revisionId" in store, "store.revisionId exists");
-      ok("getLength" in store, "store.getLength exists");
-      ok("sync" in store, "store.sync exists");
-
-      gStore = stores[0];
-
-      runTest();
-    }, cbError);
-  }
-
-  function testStoreGet(id, value) {
-    gStore.get(id).then(function(what) {
-      ok(true, "store.get() retrieves data");
-      is(what, value, "store.get(" + id + ") returns " + value);
-    }, function() {
-      ok(false, "store.get(" + id + ") retrieves data");
-    }).then(runTest, cbError);
-  }
-
-  function testStoreAdd(value) {
-    return gStore.add(value).then(function(what) {
-      ok(true, "store.add() is called");
-      ok(what > 0, "store.add() returns something");
-      return what;
-    }, cbError);
-  }
-
-  function testStorePut(value, id) {
-    return gStore.put(value, id).then(function() {
-      ok(true, "store.put() is called");
-    }, cbError);
-  }
-
-  function testStoreGetLength(number) {
-    return gStore.getLength().then(function(n) {
-      is(number, n, "store.getLength() returns the right number");
-    }, cbError);
-  }
-
-  function testStoreRemove(id) {
-    return gStore.remove(id).then(function() {
-      ok(true, "store.remove() is called");
-    }, cbError);
-  }
-
-  function testStoreClear() {
-    return gStore.clear().then(function() {
-      ok(true, "store.clear() is called");
-    }, cbError);
-  }
-
-  var tests = [
-    // Test for GetDataStore
-    testGetDataStores,
-
-    // Unknown ID
-    function() { testStoreGet(42, undefined); },
-    function() { testStoreGet(42, undefined); }, // twice
-
-    // Add + Get - number
-    function() { testStoreAdd(42).then(function(id) {
-                   gId = id; runTest(); }, cbError); },
-    function() { testStoreGet(gId, 42); },
-
-    // Add + Get - boolean
-    function() { testStoreAdd(true).then(function(id) {
-                   gId = id; runTest(); }, cbError); },
-    function() { testStoreGet(gId, true); },
-
-    // Add + Get - string
-    function() { testStoreAdd("hello world").then(function(id) {
-                   gId = id; runTest(); }, cbError); },
-    function() { testStoreGet(gId, "hello world"); },
-
-    // Put + Get - string
-    function() { testStorePut("hello world 2", gId).then(function() {
-                   runTest(); }, cbError); },
-    function() { testStoreGet(gId, "hello world 2"); },
-
-    // getLength
-    function() { testStoreGetLength(3).then(function() { runTest(); }, cbError); },
-
-    // Remove
-    function() { testStoreRemove(gId).then(function(what) {
-                   runTest(); }, cbError); },
-    function() { testStoreGet(gId, undefined); },
-
-    // Remove - wrong ID
-    function() { testStoreRemove(gId).then(function(what) {
-                   runTest(); }, cbError); },
-
-    // Clear
-    function() { testStoreClear().then(function(what) {
-                   runTest(); }, cbError); },
-  ];
-
-  function runTest() {
-    if (!tests.length) {
-      finish();
-      return;
-    }
-
-    var test = tests.shift();
-    test();
-  }
-
   runTest();
   </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_basic_common.js
@@ -0,0 +1,119 @@
+var gStore;
+
+function testGetDataStores() {
+  navigator.getDataStores('foo').then(function(stores) {
+    is(stores.length, 1, "getDataStores('foo') returns 1 element");
+    is(stores[0].name, 'foo', 'The dataStore.name is foo');
+    is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
+
+    var store = stores[0];
+    ok("get" in store, "store.get exists");
+    ok("put" in store, "store.put exists");
+    ok("add" in store, "store.add exists");
+    ok("remove" in store, "store.remove exists");
+    ok("clear" in store, "store.clear exists");
+    ok("revisionId" in store, "store.revisionId exists");
+    ok("getLength" in store, "store.getLength exists");
+    ok("sync" in store, "store.sync exists");
+
+    gStore = stores[0];
+
+    runTest();
+  }, cbError);
+}
+
+function testStoreGet(id, value) {
+  gStore.get(id).then(function(what) {
+    ok(true, "store.get() retrieves data");
+    is(what, value, "store.get(" + id + ") returns " + value);
+  }, function() {
+    ok(false, "store.get(" + id + ") retrieves data");
+  }).then(runTest, cbError);
+}
+
+function testStoreAdd(value) {
+  return gStore.add(value).then(function(what) {
+    ok(true, "store.add() is called");
+    ok(what > 0, "store.add() returns something");
+    return what;
+  }, cbError);
+}
+
+function testStorePut(value, id) {
+  return gStore.put(value, id).then(function() {
+    ok(true, "store.put() is called");
+  }, cbError);
+}
+
+function testStoreGetLength(number) {
+  return gStore.getLength().then(function(n) {
+    is(number, n, "store.getLength() returns the right number");
+  }, cbError);
+}
+
+function testStoreRemove(id) {
+  return gStore.remove(id).then(function() {
+    ok(true, "store.remove() is called");
+  }, cbError);
+}
+
+function testStoreClear() {
+  return gStore.clear().then(function() {
+    ok(true, "store.clear() is called");
+  }, cbError);
+}
+
+var tests = [
+  // Test for GetDataStore
+  testGetDataStores,
+
+  // Unknown ID
+  function() { testStoreGet(42, undefined); },
+  function() { testStoreGet(42, undefined); }, // twice
+
+  // Add + Get - number
+  function() { testStoreAdd(42).then(function(id) {
+                 gId = id; runTest(); }, cbError); },
+  function() { testStoreGet(gId, 42); },
+
+  // Add + Get - boolean
+  function() { testStoreAdd(true).then(function(id) {
+                 gId = id; runTest(); }, cbError); },
+  function() { testStoreGet(gId, true); },
+
+  // Add + Get - string
+  function() { testStoreAdd("hello world").then(function(id) {
+                 gId = id; runTest(); }, cbError); },
+  function() { testStoreGet(gId, "hello world"); },
+
+  // Put + Get - string
+  function() { testStorePut("hello world 2", gId).then(function() {
+                 runTest(); }, cbError); },
+  function() { testStoreGet(gId, "hello world 2"); },
+
+  // getLength
+  function() { testStoreGetLength(3).then(function() { runTest(); }, cbError); },
+
+  // Remove
+  function() { testStoreRemove(gId).then(function(what) {
+                 runTest(); }, cbError); },
+  function() { testStoreGet(gId, undefined); },
+
+  // Remove - wrong ID
+  function() { testStoreRemove(gId).then(function(what) {
+                 runTest(); }, cbError); },
+
+  // Clear
+  function() { testStoreClear().then(function(what) {
+                 runTest(); }, cbError); },
+];
+
+function runTest() {
+  if (!tests.length) {
+    finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
--- a/dom/datastore/tests/file_basic_worker.js
+++ b/dom/datastore/tests/file_basic_worker.js
@@ -1,137 +1,19 @@
-  var gStore;
-
-  function is(a, b, msg) {
-    postMessage((a === b ? 'OK' : 'KO') + ' ' + msg)
-  }
-
-  function ok(a, msg) {
-    postMessage((a ? 'OK' : 'KO')+ ' ' + msg)
-  }
-
-  function cbError() {
-    postMessage('KO error');
-  }
-
-  function finish() {
-    postMessage('DONE');
-  }
-
-  function testGetDataStores() {
-    navigator.getDataStores('foo').then(function(stores) {
-      is(stores.length, 1, "getDataStores('foo') returns 1 element");
-      is(stores[0].name, 'foo', 'The dataStore.name is foo');
-      is(stores[0].readOnly, false, 'The dataStore foo is not in readonly');
-
-      var store = stores[0];
-      ok("get" in store, "store.get exists");
-      ok("put" in store, "store.put exists");
-      ok("add" in store, "store.add exists");
-      ok("remove" in store, "store.remove exists");
-      ok("clear" in store, "store.clear exists");
-      ok("revisionId" in store, "store.revisionId exists");
-      ok("getLength" in store, "store.getLength exists");
-      ok("sync" in store, "store.sync exists");
+function is(a, b, msg) {
+  postMessage((a === b ? 'OK' : 'KO') + ' ' + msg)
+}
 
-      gStore = stores[0];
-
-      runTest();
-    }, cbError);
-  }
-
-  function testStoreGet(id, value) {
-    gStore.get(id).then(function(what) {
-      ok(true, "store.get() retrieves data");
-      is(what, value, "store.get(" + id + ") returns " + value);
-    }, function() {
-      ok(false, "store.get(" + id + ") retrieves data");
-    }).then(runTest, cbError);
-  }
-
-  function testStoreAdd(value) {
-    return gStore.add(value).then(function(what) {
-      ok(true, "store.add() is called");
-      ok(what > 0, "store.add() returns something");
-      return what;
-    }, cbError);
-  }
-
-  function testStorePut(value, id) {
-    return gStore.put(value, id).then(function() {
-      ok(true, "store.put() is called");
-    }, cbError);
-  }
-
-  function testStoreGetLength(number) {
-    return gStore.getLength().then(function(n) {
-      is(number, n, "store.getLength() returns the right number");
-    }, cbError);
-  }
+function ok(a, msg) {
+  postMessage((a ? 'OK' : 'KO')+ ' ' + msg)
+}
 
-  function testStoreRemove(id) {
-    return gStore.remove(id).then(function() {
-      ok(true, "store.remove() is called");
-    }, cbError);
-  }
-
-  function testStoreClear() {
-    return gStore.clear().then(function() {
-      ok(true, "store.clear() is called");
-    }, cbError);
-  }
-
-  var tests = [
-    // Test for GetDataStore
-    testGetDataStores,
-
-    // Unknown ID
-    function() { testStoreGet(42, undefined); },
-    function() { testStoreGet(42, undefined); }, // twice
-
-    // Add + Get - number
-    function() { testStoreAdd(42).then(function(id) {
-                   gId = id; runTest(); }, cbError); },
-    function() { testStoreGet(gId, 42); },
-
-    // Add + Get - boolean
-    function() { testStoreAdd(true).then(function(id) {
-                   gId = id; runTest(); }, cbError); },
-    function() { testStoreGet(gId, true); },
+function cbError() {
+  postMessage('KO error');
+}
 
-    // Add + Get - string
-    function() { testStoreAdd("hello world").then(function(id) {
-                   gId = id; runTest(); }, cbError); },
-    function() { testStoreGet(gId, "hello world"); },
-
-    // Put + Get - string
-    function() { testStorePut("hello world 2", gId).then(function() {
-                   runTest(); }, cbError); },
-    function() { testStoreGet(gId, "hello world 2"); },
-
-    // getLength
-    function() { testStoreGetLength(3).then(function() { runTest(); }, cbError); },
-
-    // Remove
-    function() { testStoreRemove(gId).then(function(what) {
-                   runTest(); }, cbError); },
-    function() { testStoreGet(gId, undefined); },
+function finish() {
+  postMessage('DONE');
+}
 
-    // Remove - wrong ID
-    function() { testStoreRemove(gId).then(function(what) {
-                   runTest(); }, cbError); },
-
-    // Clear
-    function() { testStoreClear().then(function(what) {
-                   runTest(); }, cbError); },
-  ];
+importScripts("file_basic_common.js");
 
-  function runTest() {
-    if (!tests.length) {
-      finish();
-      return;
-    }
-
-    var test = tests.shift();
-    test();
-  }
-
-  runTest();
\ No newline at end of file
+runTest();
--- a/dom/datastore/tests/file_sync.html
+++ b/dom/datastore/tests/file_sync.html
@@ -1,492 +1,31 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for DataStore - sync</title>
+  <script type="text/javascript" src="file_sync_common.js"></script>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
-  var gStore;
-  var gRevisions = [];
-  var gCursor;
-  var gExpectedEvents = true;
-
   function is(a, b, msg) {
     alert((a === b ? 'OK' : 'KO') + ' ' + msg)
   }
 
   function ok(a, msg) {
     alert((a ? 'OK' : 'KO')+ ' ' + msg)
   }
 
   function cbError() {
     alert('KO error');
   }
 
   function finish() {
     alert('DONE');
   }
 
-  function testGetDataStores() {
-    navigator.getDataStores('foo').then(function(stores) {
-      is(stores.length, 1, "getDataStores('foo') returns 1 element");
-
-      gStore = stores[0];
-      gRevisions.push(gStore.revisionId);
-
-      gStore.onchange = function(aEvent) {
-        ok(gExpectedEvents, "Events received!");
-        runTest();
-      }
-
-      runTest();
-    }, cbError);
-  }
-
-  function testBasicInterface() {
-    var cursor = gStore.sync();
-    ok(cursor, "Cursor is created");
-    is(cursor.store, gStore, "Cursor.store is the store");
-
-    ok("next" in cursor, "Cursor.next exists");
-    ok("close" in cursor, "Cursor.close exists");
-
-    cursor.close();
-
-    runTest();
-  }
-
-  function testCursor(cursor, steps) {
-    if (!steps.length) {
-      runTest();
-      return;
-    }
-
-    var step = steps.shift();
-    cursor.next().then(function(data) {
-      ok(!!data, "Cursor.next returns data");
-      is(data.operation, step.operation, "Waiting for operation: '" + step.operation + "' received '" + data.operation + "'");
-
-
-      switch (data.operation) {
-        case 'clear':
-          is (data.id, null, "'clear' operation wants a null id");
-          break;
-
-        case 'done':
-          is(/[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}/.test(data.revisionId), true, "done has a valid revisionId");
-          is (data.revisionId, gRevisions[gRevisions.length-1], "Last revision matches");
-          is (data.id, null, "'done' operation wants a null id");
-          break;
-
-        case 'add':
-        case 'update':
-          if ('id' in step) {
-            is(data.id, step.id, "next() add: id matches: " + data.id + " " + step.id);
-          }
-
-          if ('data' in step) {
-            is(data.data, step.data, "next() add: data matches: " + data.data + " " + step.data);
-          }
-
-          break;
-
-        case 'remove':
-          if ('id' in step) {
-            is(data.id, step.id, "next() add: id matches: " + data.id + " " + step.id);
-          }
-
-          break;
-      }
-
-      testCursor(cursor, steps);
-    });
-  }
-
-  var tests = [
-    // Test for GetDataStore
-    testGetDataStores,
-
-    // interface test
-    testBasicInterface,
-
-    // empty DataStore
-    function() {
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear' },
-                    { operation: 'done' },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear' },
-                    { operation: 'done' },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'done' },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // Test add from scratch
-    function() {
-      gExpectedEvents = true;
-
-      gStore.add(1).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        ok(true, "Item: " + id + " added");
-      });
-    },
-
-    function() {
-      gStore.add(2,"foobar").then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        ok(true, "Item: " + id + " added");
-      });
-    },
-
-    function() {
-      gStore.add(3,3).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        ok(true, "Item: " + id + " added");
-      });
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 1 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 1 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'add', id: 1, data: 1 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[1]);
-      var steps = [ { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[2]);
-      var steps = [ { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[3]);
-      var steps = [ { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // Test after an update
-    function() {
-      gExpectedEvents = true;
-      gStore.put(123, 1).then(function() {
-        gRevisions.push(gStore.revisionId);
-      });
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[1]);
-      var steps = [ { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[2]);
-      var steps = [ { operation: 'add', id: 3, data: 3 },
-                    { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[3]);
-      var steps = [ { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[4]);
-      var steps = [ { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // Test after a remove
-    function() {
-      gExpectedEvents = true;
-      gStore.remove(3).then(function() {
-        gRevisions.push(gStore.revisionId);
-      });
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[1]);
-      var steps = [ { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[2]);
-      var steps = [ { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[3]);
-      var steps = [ { operation: 'update', id: 1, data: 123 },
-                    { operation: 'remove', id: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[4]);
-      var steps = [ { operation: 'remove', id: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[5]);
-      var steps = [ { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // New events when the cursor is active
-    function() {
-      gCursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 } ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.add(42, 2).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 2, data: 42 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.put(43, 2).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 2, data: 43 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.remove(2).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.add(42).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 4, data: 42 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.clear().then(function() {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear' } ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.add(42).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 5, data: 42 } ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.clear().then(function() {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      gStore.add(42).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear' },
-                    { operation: 'add', id: 6, data: 42 },
-                    { operation: 'done'} ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gExpectedEvents = true;
-      gStore.add(42).then(function(id) {
-      });
-    }
-  ];
-
-  function runTest() {
-    if (!tests.length) {
-      finish();
-      return;
-    }
-
-    var test = tests.shift();
-    test();
-  }
-
   runTest();
   </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_sync_common.js
@@ -0,0 +1,461 @@
+var gStore;
+var gRevisions = [];
+var gCursor;
+var gExpectedEvents = true;
+
+function testGetDataStores() {
+  navigator.getDataStores('foo').then(function(stores) {
+    is(stores.length, 1, "getDataStores('foo') returns 1 element");
+
+    gStore = stores[0];
+    gRevisions.push(gStore.revisionId);
+
+    gStore.onchange = function(aEvent) {
+      ok(gExpectedEvents, "Events received!");
+      runTest();
+    }
+
+    runTest();
+  }, cbError);
+}
+
+function testBasicInterface() {
+  var cursor = gStore.sync();
+  ok(cursor, "Cursor is created");
+  is(cursor.store, gStore, "Cursor.store is the store");
+
+  ok("next" in cursor, "Cursor.next exists");
+  ok("close" in cursor, "Cursor.close exists");
+
+  cursor.close();
+
+  runTest();
+}
+
+function testCursor(cursor, steps) {
+  if (!steps.length) {
+    runTest();
+    return;
+  }
+
+  var step = steps.shift();
+  cursor.next().then(function(data) {
+    ok(!!data, "Cursor.next returns data");
+    is(data.operation, step.operation, "Waiting for operation: '" + step.operation + "' received '" + data.operation + "'");
+
+
+    switch (data.operation) {
+      case 'clear':
+        is (data.id, null, "'clear' operation wants a null id");
+        break;
+
+      case 'done':
+        is(/[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}/.test(data.revisionId), true, "done has a valid revisionId");
+        is (data.revisionId, gRevisions[gRevisions.length-1], "Last revision matches");
+        is (data.id, null, "'done' operation wants a null id");
+        break;
+
+      case 'add':
+      case 'update':
+        if ('id' in step) {
+          is(data.id, step.id, "next() add: id matches: " + data.id + " " + step.id);
+        }
+
+        if ('data' in step) {
+          is(data.data, step.data, "next() add: data matches: " + data.data + " " + step.data);
+        }
+
+        break;
+
+      case 'remove':
+        if ('id' in step) {
+          is(data.id, step.id, "next() add: id matches: " + data.id + " " + step.id);
+        }
+
+        break;
+    }
+
+    testCursor(cursor, steps);
+  });
+}
+
+var tests = [
+  // Test for GetDataStore
+  testGetDataStores,
+
+  // interface test
+  testBasicInterface,
+
+  // empty DataStore
+  function() {
+    var cursor = gStore.sync();
+    var steps = [ { operation: 'clear' },
+                  { operation: 'done' },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    gExpectedEvents = false;
+    var cursor = gStore.sync('wrong revision ID');
+    var steps = [ { operation: 'clear' },
+                  { operation: 'done' },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[0]);
+    var steps = [ { operation: 'done' },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  // Test add from scratch
+  function() {
+    gExpectedEvents = true;
+
+    gStore.add(1).then(function(id) {
+      gRevisions.push(gStore.revisionId);
+      ok(true, "Item: " + id + " added");
+    });
+  },
+
+  function() {
+    gStore.add(2,"foobar").then(function(id) {
+      gRevisions.push(gStore.revisionId);
+      ok(true, "Item: " + id + " added");
+    });
+  },
+
+  function() {
+    gStore.add(3,3).then(function(id) {
+      gRevisions.push(gStore.revisionId);
+      ok(true, "Item: " + id + " added");
+    });
+  },
+
+  function() {
+    gExpectedEvents = false;
+    var cursor = gStore.sync();
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 1 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync('wrong revision ID');
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 1 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[0]);
+    var steps = [ { operation: 'add', id: 1, data: 1 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[1]);
+    var steps = [ { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[2]);
+    var steps = [ { operation: 'add', id: 3, data: 3 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[3]);
+    var steps = [ { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  // Test after an update
+  function() {
+    gExpectedEvents = true;
+    gStore.put(123, 1).then(function() {
+      gRevisions.push(gStore.revisionId);
+    });
+  },
+
+  function() {
+    gExpectedEvents = false;
+    var cursor = gStore.sync();
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync('wrong revision ID');
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[0]);
+    var steps = [ { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[1]);
+    var steps = [ { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'add', id: 3, data: 3 },
+                  { operation: 'update', id: 1, data: 123 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[2]);
+    var steps = [ { operation: 'add', id: 3, data: 3 },
+                  { operation: 'update', id: 1, data: 123 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[3]);
+    var steps = [ { operation: 'update', id: 1, data: 123 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[4]);
+    var steps = [ { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  // Test after a remove
+  function() {
+    gExpectedEvents = true;
+    gStore.remove(3).then(function() {
+      gRevisions.push(gStore.revisionId);
+    });
+  },
+
+  function() {
+    gExpectedEvents = false;
+    var cursor = gStore.sync();
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync('wrong revision ID');
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[0]);
+    var steps = [ { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[1]);
+    var steps = [ { operation: 'add', id: 'foobar', data: 2 },
+                  { operation: 'update', id: 1, data: 123 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[2]);
+    var steps = [ { operation: 'update', id: 1, data: 123 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[3]);
+    var steps = [ { operation: 'update', id: 1, data: 123 },
+                  { operation: 'remove', id: 3 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[4]);
+    var steps = [ { operation: 'remove', id: 3 },
+                  { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  function() {
+    var cursor = gStore.sync(gRevisions[5]);
+    var steps = [ { operation: 'done' }];
+    testCursor(cursor, steps);
+  },
+
+  // New events when the cursor is active
+  function() {
+    gCursor = gStore.sync();
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 'foobar', data: 2 } ];
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.add(42, 2).then(function(id) {
+      ok(true, "Item: " + id + " added");
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 2, data: 42 },
+                  { operation: 'add', id: 'foobar', data: 2 } ]
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.put(43, 2).then(function(id) {
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 2, data: 43 },
+                  { operation: 'add', id: 'foobar', data: 2 } ]
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.remove(2).then(function(id) {
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 'foobar', data: 2 } ]
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.add(42).then(function(id) {
+      ok(true, "Item: " + id + " added");
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 1, data: 123 },
+                  { operation: 'add', id: 4, data: 42 },
+                  { operation: 'add', id: 'foobar', data: 2 } ]
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.clear().then(function() {
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear' } ];
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.add(42).then(function(id) {
+      ok(true, "Item: " + id + " added");
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear', },
+                  { operation: 'add', id: 5, data: 42 } ];
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gStore.clear().then(function() {
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    gStore.add(42).then(function(id) {
+      ok(true, "Item: " + id + " added");
+      gRevisions.push(gStore.revisionId);
+      runTest();
+    });
+  },
+
+  function() {
+    var steps = [ { operation: 'clear' },
+                  { operation: 'add', id: 6, data: 42 },
+                  { operation: 'done'} ];
+    testCursor(gCursor, steps);
+  },
+
+  function() {
+    gExpectedEvents = true;
+    gStore.add(42).then(function(id) {
+    });
+  }
+];
+
+function runTest() {
+  if (!tests.length) {
+    finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
--- a/dom/datastore/tests/file_sync_worker.js
+++ b/dom/datastore/tests/file_sync_worker.js
@@ -1,474 +1,19 @@
-  var gStore;
-  var gRevisions = [];
-  var gCursor;
-  var gExpectedEvents = true;
-
-  function is(a, b, msg) {
-    postMessage((a === b ? 'OK' : 'KO') + ' ' + msg)
-  }
-
-  function ok(a, msg) {
-    postMessage((a ? 'OK' : 'KO')+ ' ' + msg)
-  }
-
-  function cbError() {
-    postMessage('KO error');
-  }
-
-  function finish() {
-    postMessage('DONE');
-  }
-
-  function testGetDataStores() {
-    navigator.getDataStores('foo').then(function(stores) {
-      is(stores.length, 1, "getDataStores('foo') returns 1 element");
-
-      gStore = stores[0];
-      gRevisions.push(gStore.revisionId);
-
-      gStore.onchange = function(aEvent) {
-        ok(gExpectedEvents, "Events received!");
-        runTest();
-      }
-
-      runTest();
-    }, cbError);
-  }
-
-  function testBasicInterface() {
-    var cursor = gStore.sync();
-    ok(cursor, "Cursor is created");
-    is(cursor.store, gStore, "Cursor.store is the store");
-
-    ok("next" in cursor, "Cursor.next exists");
-    ok("close" in cursor, "Cursor.close exists");
-
-    cursor.close();
-
-    runTest();
-  }
-
-  function testCursor(cursor, steps) {
-    if (!steps.length) {
-      runTest();
-      return;
-    }
-
-    var step = steps.shift();
-    cursor.next().then(function(data) {
-      ok(!!data, "Cursor.next returns data");
-      is(data.operation, step.operation, "Waiting for operation: '" + step.operation + "' received '" + data.operation + "'");
-
-
-      switch (data.operation) {
-        case 'done':
-          is(/[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}/.test(data.revisionId), true, "done has a valid revisionId");
-          is (data.revisionId, gRevisions[gRevisions.length-1], "Last revision matches");
-          break;
-
-        case 'add':
-        case 'update':
-          if ('id' in step) {
-            is(data.id, step.id, "next() add: id matches: " + data.id + " " + step.id);
-          }
-
-          if ('data' in step) {
-            is(data.data, step.data, "next() add: data matches: " + data.data + " " + step.data);
-          }
-
-          break;
-
-        case 'remove':
-          if ('id' in step) {
-            is(data.id, step.id, "next() add: id matches: " + data.id + " " + step.id);
-          }
-
-          break;
-      }
-
-      testCursor(cursor, steps);
-    });
-  }
-
-  var tests = [
-    // Test for GetDataStore
-    testGetDataStores,
-
-    // interface test
-    testBasicInterface,
-
-    // empty DataStore
-    function() {
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear' },
-                    { operation: 'done' },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear' },
-                    { operation: 'done' },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
+function is(a, b, msg) {
+  postMessage((a === b ? 'OK' : 'KO') + ' ' + msg)
+}
 
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'done' },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // Test add from scratch
-    function() {
-      gExpectedEvents = true;
-
-      gStore.add(1).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        ok(true, "Item: " + id + " added");
-      });
-    },
-
-    function() {
-      gStore.add(2,"foobar").then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        ok(true, "Item: " + id + " added");
-      });
-    },
-
-    function() {
-      gStore.add(3,3).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        ok(true, "Item: " + id + " added");
-      });
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 1 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 1 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'add', id: 1, data: 1 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[1]);
-      var steps = [ { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[2]);
-      var steps = [ { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[3]);
-      var steps = [ { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // Test after an update
-    function() {
-      gExpectedEvents = true;
-      gStore.put(123, 1).then(function() {
-        gRevisions.push(gStore.revisionId);
-      });
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
+function ok(a, msg) {
+  postMessage((a ? 'OK' : 'KO')+ ' ' + msg)
+}
 
-    function() {
-      var cursor = gStore.sync(gRevisions[1]);
-      var steps = [ { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'add', id: 3, data: 3 },
-                    { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[2]);
-      var steps = [ { operation: 'add', id: 3, data: 3 },
-                    { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[3]);
-      var steps = [ { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[4]);
-      var steps = [ { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // Test after a remove
-    function() {
-      gExpectedEvents = true;
-      gStore.remove(3).then(function() {
-        gRevisions.push(gStore.revisionId);
-      });
-    },
-
-    function() {
-      gExpectedEvents = false;
-      var cursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync('wrong revision ID');
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[0]);
-      var steps = [ { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[1]);
-      var steps = [ { operation: 'add', id: 'foobar', data: 2 },
-                    { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[2]);
-      var steps = [ { operation: 'update', id: 1, data: 123 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[3]);
-      var steps = [ { operation: 'update', id: 1, data: 123 },
-                    { operation: 'remove', id: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[4]);
-      var steps = [ { operation: 'remove', id: 3 },
-                    { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    function() {
-      var cursor = gStore.sync(gRevisions[5]);
-      var steps = [ { operation: 'done' }];
-      testCursor(cursor, steps);
-    },
-
-    // New events when the cursor is active
-    function() {
-      gCursor = gStore.sync();
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 } ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.add(42, 2).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
+function cbError() {
+  postMessage('KO error');
+}
 
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 2, data: 42 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.put(43, 2).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 2, data: 43 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.remove(2).then(function(id) {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.add(42).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 1, data: 123 },
-                    { operation: 'add', id: 4, data: 42 },
-                    { operation: 'add', id: 'foobar', data: 2 } ]
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.clear().then(function() {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
+function finish() {
+  postMessage('DONE');
+}
 
-    function() {
-      var steps = [ { operation: 'clear' } ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.add(42).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear', },
-                    { operation: 'add', id: 5, data: 42 } ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gStore.clear().then(function() {
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
+importScripts("file_sync_common.js");
 
-    function() {
-      gStore.add(42).then(function(id) {
-        ok(true, "Item: " + id + " added");
-        gRevisions.push(gStore.revisionId);
-        runTest();
-      });
-    },
-
-    function() {
-      var steps = [ { operation: 'clear' },
-                    { operation: 'add', id: 6, data: 42 },
-                    { operation: 'done'} ];
-      testCursor(gCursor, steps);
-    },
-
-    function() {
-      gExpectedEvents = true;
-      gStore.add(42).then(function(id) {
-      });
-    }
-  ];
-
-  function runTest() {
-    if (!tests.length) {
-      finish();
-      return;
-    }
-
-    var test = tests.shift();
-    test();
-  }
-
-  runTest();
\ No newline at end of file
+runTest();
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -21,16 +21,18 @@ support-files =
   file_duplicate.html
   file_bug976311.html
   file_bug976311.template.webapp
   file_bug986056.html
   file_bug986056.template.webapp
   file_event_maker.html
   file_event_receiver.html
   file_transactions.html
+  file_basic_common.js
+  file_sync_common.js
 
 [test_app_install.html]
 [test_readonly.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; time out) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_basic.html]
 [test_basic_worker.html]
 [test_changes.html]
 skip-if = (toolkit == 'gonk' && debug) #intermittent failures, bug 961021
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -459,16 +459,20 @@ WINDOW_EVENT(beforeprint,
 BEFOREUNLOAD_EVENT(beforeunload,
                    NS_BEFORE_PAGE_UNLOAD,
                    EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
                    NS_EVENT)
 WINDOW_EVENT(hashchange,
              NS_HASHCHANGE,
              EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
              NS_EVENT)
+WINDOW_EVENT(languagechange,
+             NS_LANGUAGECHANGE,
+             EventNameType_HTMLBodyOrFramesetOnly,
+             NS_EVENT)
 // XXXbz Should the onmessage attribute on <body> really not work?  If so, do we
 // need a different macro to flag things like that (IDL, but not content
 // attributes on body/frameset), or is just using EventNameType_None enough?
 WINDOW_EVENT(message,
              NS_MESSAGE,
              EventNameType_None,
              NS_EVENT)
 WINDOW_EVENT(offline,
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -19,17 +19,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(8c115ab3-cf96-492c-850c-3b18056b45e2)]
+[scriptable, uuid(fbefa573-0ba2-4d15-befb-d60277643a0b)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -473,16 +473,17 @@ interface nsIDOMWindow : nsISupports
 
   /**
    * HTML5 event attributes that only apply to windows and <body>/<frameset>
    */
   [implicit_jscontext] attribute jsval onafterprint;
   [implicit_jscontext] attribute jsval onbeforeprint;
   [implicit_jscontext] attribute jsval onbeforeunload;
   [implicit_jscontext] attribute jsval onhashchange;
+  [implicit_jscontext] attribute jsval onlanguagechange;
   [implicit_jscontext] attribute jsval onmessage;
   [implicit_jscontext] attribute jsval onoffline;
   [implicit_jscontext] attribute jsval ononline;
   [implicit_jscontext] attribute jsval onpopstate;
   [implicit_jscontext] attribute jsval onpagehide;
   [implicit_jscontext] attribute jsval onpageshow;
   // Not supported yet
   // [implicit_jscontext] attribute jsval onredo;
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -732,17 +732,16 @@ static const dom::ConstantSpec gWinPrope
   INT_CONSTANT(INVALID_FILE_ATTRIBUTES),
 
   // GetNamedSecurityInfo and SetNamedSecurityInfo constants
   INT_CONSTANT(UNPROTECTED_DACL_SECURITY_INFORMATION),
   INT_CONSTANT(SE_FILE_OBJECT),
   INT_CONSTANT(DACL_SECURITY_INFORMATION),
 
   // Errors
-  INT_CONSTANT(ERROR_SUCCESS),
   INT_CONSTANT(ERROR_INVALID_HANDLE),
   INT_CONSTANT(ERROR_ACCESS_DENIED),
   INT_CONSTANT(ERROR_DIR_NOT_EMPTY),
   INT_CONSTANT(ERROR_FILE_EXISTS),
   INT_CONSTANT(ERROR_ALREADY_EXISTS),
   INT_CONSTANT(ERROR_FILE_NOT_FOUND),
   INT_CONSTANT(ERROR_NO_MORE_FILES),
   INT_CONSTANT(ERROR_PATH_NOT_FOUND),
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -118,16 +118,17 @@ interface GlobalEventHandlers {
 };
 
 [NoInterfaceObject]
 interface WindowEventHandlers {
            attribute EventHandler onafterprint;
            attribute EventHandler onbeforeprint;
            attribute OnBeforeUnloadEventHandler onbeforeunload;
            attribute EventHandler onhashchange;
+           attribute EventHandler onlanguagechange;
            attribute EventHandler onmessage;
            attribute EventHandler onoffline;
            attribute EventHandler ononline;
            attribute EventHandler onpagehide;
            attribute EventHandler onpageshow;
            attribute EventHandler onpopstate;
            attribute EventHandler onresize;
            //(Not implemented)attribute EventHandler onstorage;
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -23,16 +23,17 @@
 interface Navigator {
   // objects implementing this interface also implement the interfaces given below
 };
 Navigator implements NavigatorID;
 Navigator implements NavigatorLanguage;
 Navigator implements NavigatorOnLine;
 Navigator implements NavigatorContentUtils;
 Navigator implements NavigatorStorageUtils;
+Navigator implements NavigatorFeatures;
 
 [NoInterfaceObject]
 interface NavigatorID {
   // WebKit/Blink/Trident/Presto support this (hardcoded "Mozilla").
   [Constant]
   readonly attribute DOMString appCodeName; // constant "Mozilla"
   [Constant]
   readonly attribute DOMString appName;
@@ -47,16 +48,17 @@ interface NavigatorID {
 
   // Everyone but WebKit/Blink supports this.  See bug 679971.
   boolean taintEnabled(); // constant false
 };
 
 [NoInterfaceObject]
 interface NavigatorLanguage {
   readonly attribute DOMString? language;
+  [Pure, Cached, Frozen] readonly attribute sequence<DOMString> languages;
 };
 
 [NoInterfaceObject]
 interface NavigatorOnLine {
   readonly attribute boolean onLine;
 };
 
 [NoInterfaceObject]
@@ -74,16 +76,22 @@ interface NavigatorContentUtils {
 };
 
 [NoInterfaceObject]
 interface NavigatorStorageUtils {
   // NOT IMPLEMENTED
   //void yieldForStorageUpdates();
 };
 
+[NoInterfaceObject]
+interface NavigatorFeatures {
+  [Func="Navigator::HasFeatureDetectionSupport"]
+  Promise getFeature(DOMString name);
+};
+
 // Things that definitely need to be in the spec and and are not for some
 // reason.  See https://www.w3.org/Bugs/Public/show_bug.cgi?id=22406
 partial interface Navigator {
   [Throws]
   readonly attribute MimeTypeArray mimeTypes;
   [Throws]
   readonly attribute PluginArray plugins;
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -492,17 +492,17 @@ LoadJSGCMemoryOptions(const char* aPrefN
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark");
     if (memPrefName == matchName || (!rts && index == 1)) {
       int32_t prefValue = GetWorkerPref(matchName, 128);
       UpdatOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES,
                                  uint32_t(prefValue) * 1024 * 1024);
       continue;
     }
 
-    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX 
+    matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
                             "gc_high_frequency_time_limit_ms");
     if (memPrefName == matchName || (!rts && index == 2)) {
       UpdateCommonJSGCMemoryOption(rts, matchName,
                                    JSGC_HIGH_FREQUENCY_TIME_LIMIT);
       continue;
     }
 
     matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX
@@ -1271,22 +1271,21 @@ RuntimeService::RegisterWorker(JSContext
     AssertIsOnMainThread();
 
     if (mShuttingDown) {
       JS_ReportError(aCx, "Cannot create worker during shutdown!");
       return false;
     }
   }
 
-  bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
-
-  const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
   nsCString sharedWorkerScriptSpec;
 
-  if (isSharedWorker) {
+  bool isSharedOrServiceWorker = aWorkerPrivate->IsSharedWorker() ||
+                                 aWorkerPrivate->IsServiceWorker();
+  if (isSharedOrServiceWorker) {
     AssertIsOnMainThread();
 
     nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
     NS_ASSERTION(scriptURI, "Null script URI!");
 
     nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
     if (NS_FAILED(rv)) {
       NS_WARNING("GetSpec failed?!");
@@ -1321,17 +1320,19 @@ RuntimeService::RegisterWorker(JSContext
     }
     else if (parent) {
       domainInfo->mChildWorkerCount++;
     }
     else {
       domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
     }
 
-    if (isSharedWorker) {
+    if (isSharedOrServiceWorker) {
+      const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
+
       nsAutoCString key;
       GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, key);
       MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key));
 
       SharedWorkerInfo* sharedWorkerInfo =
         new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
                              sharedWorkerName);
       domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo);
@@ -1461,17 +1462,17 @@ RuntimeService::UnregisterWorker(JSConte
       MOZ_ASSERT(sharedWorkersToNotify[index]);
       sharedWorkersToNotify[index]->NoteDeadWorker(aCx);
     }
   }
 
   if (parent) {
     parent->RemoveChildWorker(aCx, aWorkerPrivate);
   }
-  else if (aWorkerPrivate->IsSharedWorker()) {
+  else if (aWorkerPrivate->IsSharedWorker() || aWorkerPrivate->IsServiceWorker()) {
     mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
   }
   else {
     // May be null.
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
     nsTArray<WorkerPrivate*>* windowArray;
     MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray));
@@ -1939,17 +1940,17 @@ RuntimeService::RemoveSharedWorkerFromWi
                                   void* aUserArg)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aData.get());
   MOZ_ASSERT(aUserArg);
 
   auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
 
-  MOZ_ASSERT(workerPrivate->IsSharedWorker());
+  MOZ_ASSERT(workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker());
 
   if (aData->RemoveElement(workerPrivate)) {
     MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
 
     if (aData->IsEmpty()) {
       return PL_DHASH_REMOVE;
     }
   }
@@ -2114,17 +2115,17 @@ RuntimeService::CreateSharedWorker(const
   }
 
   bool created = false;
 
   if (!workerPrivate) {
     ErrorResult rv;
     workerPrivate =
       WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
-                                 WorkerPrivate::WorkerTypeShared, aName,
+                                 WorkerTypeShared, aName,
                                  &loadInfo, rv);
     NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode());
 
     created = true;
   }
 
   MOZ_ASSERT(workerPrivate->IsSharedWorker());
 
@@ -2155,17 +2156,18 @@ RuntimeService::CreateSharedWorker(const
   return NS_OK;
 }
 
 void
 RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aWorkerPrivate);
-  MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
+  MOZ_ASSERT(aWorkerPrivate->IsSharedWorker() ||
+             aWorkerPrivate->IsServiceWorker());
 
   MutexAutoLock lock(mMutex);
 
   WorkerDomainInfo* domainInfo;
   if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) {
     MatchSharedWorkerInfo match(aWorkerPrivate);
     domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
                                                  &match);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1310,17 +1310,17 @@ private:
     else {
       AssertIsOnMainThread();
 
       if (aWorkerPrivate->IsSuspended()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
-      if (aWorkerPrivate->IsSharedWorker()) {
+      if (aWorkerPrivate->IsSharedWorker() || aWorkerPrivate->IsServiceWorker()) {
         aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
                                                       mLine, mLineNumber,
                                                       mColumnNumber, mFlags);
         return true;
       }
 
       aWorkerPrivate->AssertInnerWindowIsCorrect();
 
@@ -2111,19 +2111,19 @@ WorkerPrivateParent<Derived>::WorkerPriv
   mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
   mParentStatus(Pending), mParentSuspended(false),
   mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
   mWorkerType(aWorkerType),
   mCreationTimeStamp(TimeStamp::Now())
 {
   SetIsDOMBinding();
 
-  MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid() &&
-                                  NS_IsMainThread());
-  MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
+  MOZ_ASSERT_IF(!IsDedicatedWorker(),
+                !aSharedWorkerName.IsVoid() && NS_IsMainThread());
+  MOZ_ASSERT_IF(IsDedicatedWorker(), aSharedWorkerName.IsEmpty());
 
   if (aLoadInfo.mWindow) {
     AssertIsOnMainThread();
     MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
                "Should have inner window here!");
     BindToOwner(aLoadInfo.mWindow);
   }
 
@@ -2342,17 +2342,17 @@ WorkerPrivateParent<Derived>::NotifyPriv
     if (mParentStatus >= aStatus) {
       return true;
     }
 
     pending = mParentStatus == Pending;
     mParentStatus = aStatus;
   }
 
-  if (IsSharedWorker()) {
+  if (IsSharedWorker() || IsServiceWorker()) {
     RuntimeService* runtime = RuntimeService::GetService();
     MOZ_ASSERT(runtime);
 
     runtime->ForgetSharedWorker(ParentAsWorkerPrivate());
   }
 
   if (pending) {
     WorkerPrivate* self = ParentAsWorkerPrivate();
@@ -2394,17 +2394,17 @@ template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(aCx);
 
   // Shared workers are only suspended if all of their owning documents are
   // suspended.
-  if (IsSharedWorker()) {
+  if (IsSharedWorker() || IsServiceWorker()) {
     AssertIsOnMainThread();
     MOZ_ASSERT(mSharedWorkers.Count());
 
     struct Closure
     {
       nsPIDOMWindow* mWindow;
       bool mAllSuspended;
 
@@ -2475,20 +2475,20 @@ WorkerPrivateParent<Derived>::Suspend(JS
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(aCx);
-  MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended);
+  MOZ_ASSERT_IF(IsDedicatedWorker(), mParentSuspended);
 
   // Shared workers are resumed if any of their owning documents are resumed.
-  if (IsSharedWorker()) {
+  if (IsSharedWorker() || IsServiceWorker()) {
     AssertIsOnMainThread();
     MOZ_ASSERT(mSharedWorkers.Count());
 
     struct Closure
     {
       nsPIDOMWindow* mWindow;
       bool mAnyRunning;
 
@@ -3029,24 +3029,26 @@ WorkerPrivate::OfflineStatusChangeEventI
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
                                                    SharedWorker* aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
-  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
   MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial()));
 
-  nsRefPtr<MessagePortRunnable> runnable =
-    new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
-                            true);
-  if (!runnable->Dispatch(aCx)) {
-    return false;
+  if (IsSharedWorker()) {
+    nsRefPtr<MessagePortRunnable> runnable =
+      new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
+                              true);
+    if (!runnable->Dispatch(aCx)) {
+      return false;
+    }
   }
 
   mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker);
 
   // If there were other SharedWorker objects attached to this worker then they
   // may all have been suspended and this worker would need to be resumed.
   if (mSharedWorkers.Count() > 1 && !Resume(aCx, nullptr)) {
     return false;
@@ -3058,17 +3060,17 @@ WorkerPrivateParent<Derived>::RegisterSh
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UnregisterSharedWorker(
                                                     JSContext* aCx,
                                                     SharedWorker* aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aSharedWorker);
-  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
   MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial()));
 
   nsRefPtr<MessagePortRunnable> runnable =
     new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
                             false);
   if (!runnable->Dispatch(aCx)) {
     JS_ReportPendingException(aCx);
   }
@@ -3236,17 +3238,17 @@ WorkerPrivateParent<Derived>::BroadcastE
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::GetAllSharedWorkers(
                                nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
 
   struct Helper
   {
     static PLDHashOperator
     Collect(const uint64_t& aKey,
             SharedWorker* aSharedWorker,
             void* aClosure)
     {
@@ -3269,17 +3271,17 @@ WorkerPrivateParent<Derived>::GetAllShar
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
                                                          nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(IsSharedWorker() || IsServiceWorker());
   MOZ_ASSERT(aWindow);
 
   struct Closure
   {
     nsPIDOMWindow* mWindow;
     nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers;
 
     Closure(nsPIDOMWindow* aWindow)
@@ -3322,17 +3324,17 @@ WorkerPrivateParent<Derived>::CloseShare
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::WorkerScriptLoaded()
 {
   AssertIsOnMainThread();
 
-  if (IsSharedWorker()) {
+  if (IsSharedWorker() || IsServiceWorker()) {
     // No longer need to hold references to the window or document we came from.
     mLoadInfo.mWindow = nullptr;
     mLoadInfo.mScriptContext = nullptr;
   }
 }
 
 template <class Derived>
 void
@@ -3556,18 +3558,18 @@ WorkerPrivate::WorkerPrivate(JSContext* 
   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
   mCloseHandlerFinished(false), mMemoryReporterRunning(false),
   mBlockedForMemoryReporter(false), mCancelAllPendingRunnables(false),
   mPeriodicGCTimerRunning(false), mIdleGCTimerRunning(false)
 #ifdef DEBUG
   , mPRThread(nullptr)
 #endif
 {
-  MOZ_ASSERT_IF(IsSharedWorker(), !aSharedWorkerName.IsVoid());
-  MOZ_ASSERT_IF(!IsSharedWorker(), aSharedWorkerName.IsEmpty());
+  MOZ_ASSERT_IF(!IsDedicatedWorker(), !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(IsDedicatedWorker(), aSharedWorkerName.IsEmpty());
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
     aParent->GetAllPreferences(mPreferences);
     mOnLine = aParent->OnLine();
   }
   else {
     AssertIsOnMainThread();
@@ -3644,19 +3646,19 @@ WorkerPrivate::Constructor(const GlobalO
   if (parent) {
     parent->AssertIsOnWorkerThread();
   } else {
     AssertIsOnMainThread();
   }
 
   JSContext* cx = aGlobal.GetContext();
 
-  MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared,
+  MOZ_ASSERT_IF(aWorkerType != WorkerTypeDedicated,
                 !aSharedWorkerName.IsVoid());
-  MOZ_ASSERT_IF(aWorkerType != WorkerTypeShared,
+  MOZ_ASSERT_IF(aWorkerType == WorkerTypeDedicated,
                 aSharedWorkerName.IsEmpty());
 
   Maybe<LoadInfo> stackLoadInfo;
   if (!aLoadInfo) {
     stackLoadInfo.construct();
 
     nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL,
                               aIsChromeWorker, stackLoadInfo.addr());
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -57,16 +57,23 @@ BEGIN_WORKERS_NAMESPACE
 class AutoSyncLoopHolder;
 class MessagePort;
 class SharedWorker;
 class WorkerControlRunnable;
 class WorkerGlobalScope;
 class WorkerPrivate;
 class WorkerRunnable;
 
+enum WorkerType
+{
+  WorkerTypeDedicated,
+  WorkerTypeShared,
+  WorkerTypeService
+};
+
 // SharedMutex is a small wrapper around an (internal) reference-counted Mutex
 // object. It exists to avoid changing a lot of code to use Mutex* instead of
 // Mutex&.
 class SharedMutex
 {
   typedef mozilla::Mutex Mutex;
 
   class RefCountedMutex MOZ_FINAL : public Mutex
@@ -189,22 +196,16 @@ public:
       mReportCSPViolations = aOther.mReportCSPViolations;
       mXHRParamsAllowed = aOther.mXHRParamsAllowed;
       mPrincipalIsSystem = aOther.mPrincipalIsSystem;
       mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
       mIsInCertifiedApp = aOther.mIsInCertifiedApp;
     }
   };
 
-  enum WorkerType
-  {
-    WorkerTypeDedicated,
-    WorkerTypeShared
-  };
-
 protected:
   typedef mozilla::ErrorResult ErrorResult;
 
   SharedMutex mMutex;
   mozilla::CondVar mCondVar;
   mozilla::CondVar mMemoryReportCondVar;
 
   // Protected by mMutex.
@@ -633,17 +634,17 @@ public:
   CopyJSCompartmentOptions(JS::CompartmentOptions& aOptions)
   {
     mozilla::MutexAutoLock lock(mMutex);
     aOptions = IsChromeWorker() ? mJSSettings.chrome.compartmentOptions
                                 : mJSSettings.content.compartmentOptions;
   }
 
   // The ability to be a chrome worker is orthogonal to the type of
-  // worker [Dedicated|Shared].
+  // worker [Dedicated|Shared|Service].
   bool
   IsChromeWorker() const
   {
     return mIsChromeWorker;
   }
 
   bool
   IsDedicatedWorker() const
@@ -652,16 +653,22 @@ public:
   }
 
   bool
   IsSharedWorker() const
   {
     return mWorkerType == WorkerTypeShared;
   }
 
+  bool
+  IsServiceWorker() const
+  {
+    return mWorkerType == WorkerTypeService;
+  }
+
   const nsCString&
   SharedWorkerName() const
   {
     return mSharedWorkerName;
   }
 
   uint64_t
   NextMessagePortSerial()
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -280,17 +280,17 @@ GLContext::GLContext(const SurfaceCaps& 
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
     mMaxTextureImageSize(0),
     mMaxRenderbufferSize(0),
     mNeedsTextureSizeChecks(false),
     mWorkAroundDriverBugs(true)
 {
-    mOwningThread = NS_GetCurrentThread();
+    mOwningThreadId = PlatformThread::CurrentId();
 }
 
 GLContext::~GLContext() {
     NS_ASSERTION(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
 #ifdef DEBUG
     if (mSharedContext) {
         GLContext *tip = mSharedContext;
         while (tip->mSharedContext)
@@ -2103,30 +2103,17 @@ GLContext::IsOffscreenSizeAllowed(const 
   int32_t biggerDimension = std::max(aSize.width, aSize.height);
   int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize);
   return biggerDimension <= maxAllowed;
 }
 
 bool
 GLContext::IsOwningThreadCurrent()
 {
-  return NS_GetCurrentThread() == mOwningThread;
-}
-
-void
-GLContext::DispatchToOwningThread(nsIRunnable *event)
-{
-    // Before dispatching, we need to ensure we're not in the middle of
-    // shutting down. Dispatching runnables in the middle of shutdown
-    // (that is, when the main thread is no longer get-able) can cause them
-    // to leak. See Bug 741319, and Bug 744115.
-    nsCOMPtr<nsIThread> mainThread;
-    if (NS_SUCCEEDED(NS_GetMainThread(getter_AddRefs(mainThread)))) {
-        mOwningThread->Dispatch(event, NS_DISPATCH_NORMAL);
-    }
+  return PlatformThread::CurrentId() == mOwningThreadId;
 }
 
 GLBlitHelper*
 GLContext::BlitHelper()
 {
     if (!mBlitHelper) {
         mBlitHelper = new GLBlitHelper(this);
     }
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -33,16 +33,17 @@
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsAutoPtr.h"
 #include "GLContextTypes.h"
 #include "GLTextureImage.h"
 #include "SurfaceTypes.h"
 #include "GLScreenBuffer.h"
 #include "GLContextSymbols.h"
+#include "base/platform_thread.h"       // for PlatformThreadId
 #include "mozilla/GenericRefCounted.h"
 #include "mozilla/Scoped.h"
 #include "gfx2DGlue.h"
 
 class nsIntRegion;
 class nsIRunnable;
 class nsIThread;
 
@@ -2564,17 +2565,16 @@ public:
 
     GLContext *GetSharedContext() { return mSharedContext; }
 
     /**
      * Returns true if the thread on which this context was created is the currently
      * executing thread.
      */
     bool IsOwningThreadCurrent();
-    void DispatchToOwningThread(nsIRunnable *event);
 
     static void PlatformStartup();
 
 public:
     /**
      * If this context wraps a double-buffered target, swap the back
      * and front buffers.  It should be assumed that after a swap, the
      * contents of the new back buffer are undefined.
@@ -2728,18 +2728,18 @@ public:
 #else
         return 0;
 #endif
     }
 
 protected:
     nsRefPtr<GLContext> mSharedContext;
 
-    // The thread on which this context was created.
-    nsCOMPtr<nsIThread> mOwningThread;
+    // The thread id which this context was created.
+    PlatformThreadId mOwningThreadId;
 
     GLContextSymbols mSymbols;
 
 #ifdef DEBUG
     // GLDebugMode will check that we don't send call
     // to a GLContext that isn't current on the current
     // thread.
     // Store the current context when binding to thread local
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -1010,16 +1010,19 @@ EraseLayerState(uint64_t aId)
 {
   sIndirectLayerTrees.erase(aId);
 }
 
 /*static*/ void
 CompositorParent::DeallocateLayerTreeId(uint64_t aId)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  // Here main thread notifies compositor to remove an element from
+  // sIndirectLayerTrees. This removed element might be queried soon.
+  // Checking the elements of sIndirectLayerTrees exist or not before using.
   CompositorLoop()->PostTask(FROM_HERE,
                              NewRunnableFunction(&EraseLayerState, aId));
 }
 
 static void
 UpdateControllerForLayersId(uint64_t aLayersId,
                             GeckoContentController* aController)
 {
@@ -1231,19 +1234,25 @@ CrossProcessCompositorParent::ActorDestr
 PLayerTransactionParent*
 CrossProcessCompositorParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>&,
                                                            const uint64_t& aId,
                                                            TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                            bool *aSuccess)
 {
   MOZ_ASSERT(aId != 0);
 
-  if (sIndirectLayerTrees[aId].mLayerManager) {
-    sIndirectLayerTrees[aId].mCrossProcessParent = this;
-    LayerManagerComposite* lm = sIndirectLayerTrees[aId].mLayerManager;
+  CompositorParent::LayerTreeState* state = nullptr;
+  LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
+  if (sIndirectLayerTrees.end() != itr) {
+    state = &itr->second;
+  }
+
+  if (state && state->mLayerManager) {
+    state->mCrossProcessParent = this;
+    LayerManagerComposite* lm = state->mLayerManager;
     *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
     *aSuccess = true;
     LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
     p->AddIPDLReference();
     return p;
   }
 
   NS_WARNING("Created child without a matching parent?");
@@ -1262,73 +1271,101 @@ CrossProcessCompositorParent::DeallocPLa
   RemoveIndirectTree(slp->GetId());
   static_cast<LayerTransactionParent*>(aLayers)->ReleaseIPDLReference();
   return true;
 }
 
 bool
 CrossProcessCompositorParent::RecvNotifyChildCreated(const uint64_t& child)
 {
-  sIndirectLayerTrees[child].mParent->NotifyChildCreated(child);
+  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(child);
+  if (!state) {
+    return false;
+  }
+
+  MOZ_ASSERT(state->mParent);
+  state->mParent->NotifyChildCreated(child);
   return true;
 }
 
 void
 CrossProcessCompositorParent::ShadowLayersUpdated(
   LayerTransactionParent* aLayerTree,
   const TargetConfig& aTargetConfig,
   bool aIsFirstPaint,
   bool aScheduleComposite)
 {
   uint64_t id = aLayerTree->GetId();
 
   MOZ_ASSERT(id != 0);
-  MOZ_ASSERT(sIndirectLayerTrees[id].mParent);
 
-  sIndirectLayerTrees[id].mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
+  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
+  if (!state) {
+    return;
+  }
+  MOZ_ASSERT(state->mParent);
+  state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
 
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
     SetShadowProperties(shadowRoot);
   }
   UpdateIndirectTree(id, shadowRoot, aTargetConfig);
 
-  sIndirectLayerTrees[id].mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite);
+  state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite);
 }
 
 void
 CrossProcessCompositorParent::ForceComposite(LayerTransactionParent* aLayerTree)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
   sIndirectLayerTrees[id].mParent->ForceComposite(aLayerTree);
 }
 
 bool
 CrossProcessCompositorParent::SetTestSampleTime(
   LayerTransactionParent* aLayerTree, const TimeStamp& aTime)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
-  return sIndirectLayerTrees[id].mParent->SetTestSampleTime(aLayerTree, aTime);
+  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
+  if (!state) {
+    return false;
+  }
+
+  MOZ_ASSERT(state->mParent);
+  return state->mParent->SetTestSampleTime(aLayerTree, aTime);
 }
 
 void
 CrossProcessCompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
-  sIndirectLayerTrees[id].mParent->LeaveTestMode(aLayerTree);
+  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
+  if (!state) {
+    return;
+  }
+
+  MOZ_ASSERT(state->mParent);
+  state->mParent->LeaveTestMode(aLayerTree);
 }
 
 AsyncCompositionManager*
 CrossProcessCompositorParent::GetCompositionManager(LayerTransactionParent* aLayerTree)
 {
   uint64_t id = aLayerTree->GetId();
-  return sIndirectLayerTrees[id].mParent->GetCompositionManager(aLayerTree);
+  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(id);
+  if (!state) {
+    return nullptr;
+  }
+
+  MOZ_ASSERT(state->mParent);
+  return state->mParent->GetCompositionManager(aLayerTree);
 }
 
 void
 CrossProcessCompositorParent::DeferredDestroy()
 {
   CrossProcessCompositorParent* self;
   mSelfRef.forget(&self);
 
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -1217,17 +1217,17 @@ const char* gfxFontUtils::gISOFontNameCh
 const char* gfxFontUtils::gMSFontNameCharsets[] =
 {
     /* [0] ENCODING_ID_MICROSOFT_SYMBOL */      ""          ,
     /* [1] ENCODING_ID_MICROSOFT_UNICODEBMP */  ""          ,
     /* [2] ENCODING_ID_MICROSOFT_SHIFTJIS */    "Shift_JIS" ,
     /* [3] ENCODING_ID_MICROSOFT_PRC */         nullptr      ,
     /* [4] ENCODING_ID_MICROSOFT_BIG5 */        "Big5"      ,
     /* [5] ENCODING_ID_MICROSOFT_WANSUNG */     nullptr      ,
-    /* [6] ENCODING_ID_MICROSOFT_JOHAB */       "x-johab"   ,
+    /* [6] ENCODING_ID_MICROSOFT_JOHAB */       nullptr      ,
     /* [7] reserved */                          nullptr      ,
     /* [8] reserved */                          nullptr      ,
     /* [9] reserved */                          nullptr      ,
     /*[10] ENCODING_ID_MICROSOFT_UNICODEFULL */ ""
 };
 
 // Return the name of the charset we should use to decode a font name
 // given the name table attributes.
--- a/hal/windows/WindowsGamepad.cpp
+++ b/hal/windows/WindowsGamepad.cpp
@@ -7,17 +7,17 @@
 #include <cstddef>
 
 #ifndef UNICODE
 #define UNICODE
 #endif
 #include <windows.h>
 #include <hidsdi.h>
 #include <stdio.h>
-#include <Xinput.h>
+#include <xinput.h>
 
 #include "nsIComponentManager.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsITimer.h"
 #include "nsTArray.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/GamepadService.h"
@@ -740,17 +740,17 @@ WindowsGamepadService::GetRawGamepad(HAN
       gamepad.dpadCaps = valueCaps[i];
       // Expose d-pad as 4 additional buttons.
       gamepad.numButtons += 4;
     } else {
       axes.InsertElementSorted(valueCaps[i], comparator);
     }
   }
 
-  gamepad.numAxes = std::min(axes.Length(), kMaxAxes);
+  gamepad.numAxes = std::min<size_t>(axes.Length(), kMaxAxes);
   for (unsigned i = 0; i < gamepad.numAxes; i++) {
     if (i >= kMaxAxes) {
       break;
     }
     gamepad.axes[i].caps = axes[i];
   }
   gamepad.type = kRawInputGamepad;
   gamepad.handle = handle;
--- a/intl/uconv/idl/nsIScriptableUConv.idl
+++ b/intl/uconv/idl/nsIScriptableUConv.idl
@@ -68,13 +68,16 @@ interface nsIScriptableUnicodeConverter 
    * @throw NS_ERROR_UCONV_NOCONV
    *        The requested charset is not supported.
    */
   attribute string charset;
 
   /**
    * Internal use
    *
-   * When this attribute is set, all charsets may be accessed but only 
-   * by Gecko-canonical name.
+   * When this attribute is set, all encodings may be accessed. Alias
+   * resolution is not performed for non-Encoding Standard encodings.
+   * MailNews callers should perform alias resolution first (e.g. using
+   * nsICharsetConverterManager::getCharsetAlias()) and use the result
+   * with this API.
    */
   attribute boolean isInternal;
 };
--- a/intl/uconv/src/moz.build
+++ b/intl/uconv/src/moz.build
@@ -53,19 +53,17 @@ UNIFIED_SOURCES += [
     '../ucvja/nsUnicodeToISO2022JP.cpp',
     '../ucvja/nsUnicodeToJISx0201.cpp',
     '../ucvja/nsUnicodeToSJIS.cpp',
 ]
 
 UNIFIED_SOURCES += [
     '../ucvko/nsCP949ToUnicode.cpp',
     '../ucvko/nsISO2022KRToUnicode.cpp',
-    '../ucvko/nsJohabToUnicode.cpp',
     '../ucvko/nsUnicodeToCP949.cpp',
-    '../ucvko/nsUnicodeToJohab.cpp',
 ]
 
 UNIFIED_SOURCES += [
     '../ucvlatin/nsARMSCII8ToUnicode.cpp',
     '../ucvlatin/nsAsciiToUnicode.cpp',
     '../ucvlatin/nsCP1250ToUnicode.cpp',
     '../ucvlatin/nsCP1251ToUnicode.cpp',
     '../ucvlatin/nsCP1253ToUnicode.cpp',
@@ -105,17 +103,16 @@ UNIFIED_SOURCES += [
     '../ucvlatin/nsMacFarsiToUnicode.cpp',
     '../ucvlatin/nsMacGreekToUnicode.cpp',
     '../ucvlatin/nsMacGujaratiToUnicode.cpp',
     '../ucvlatin/nsMacGurmukhiToUnicode.cpp',
     '../ucvlatin/nsMacHebrewToUnicode.cpp',
     '../ucvlatin/nsMacIcelandicToUnicode.cpp',
     '../ucvlatin/nsMacRomanianToUnicode.cpp',
     '../ucvlatin/nsMacTurkishToUnicode.cpp',
-    '../ucvlatin/nsT61ToUnicode.cpp',
     '../ucvlatin/nsTCVN5712ToUnicode.cpp',
     '../ucvlatin/nsTIS620ToUnicode.cpp',
     '../ucvlatin/nsUnicodeToARMSCII8.cpp',
     '../ucvlatin/nsUnicodeToAscii.cpp',
     '../ucvlatin/nsUnicodeToCP1250.cpp',
     '../ucvlatin/nsUnicodeToCP1251.cpp',
     '../ucvlatin/nsUnicodeToCP1253.cpp',
     '../ucvlatin/nsUnicodeToCP1254.cpp',
@@ -154,17 +151,16 @@ UNIFIED_SOURCES += [
     '../ucvlatin/nsUnicodeToMacFarsi.cpp',
     '../ucvlatin/nsUnicodeToMacGreek.cpp',
     '../ucvlatin/nsUnicodeToMacGujarati.cpp',
     '../ucvlatin/nsUnicodeToMacGurmukhi.cpp',
     '../ucvlatin/nsUnicodeToMacHebrew.cpp',
     '../ucvlatin/nsUnicodeToMacIcelandic.cpp',
     '../ucvlatin/nsUnicodeToMacRomanian.cpp',
     '../ucvlatin/nsUnicodeToMacTurkish.cpp',
-    '../ucvlatin/nsUnicodeToT61.cpp',
     '../ucvlatin/nsUnicodeToTCVN5712.cpp',
     '../ucvlatin/nsUnicodeToTIS620.cpp',
     '../ucvlatin/nsUnicodeToUserDefined.cpp',
     '../ucvlatin/nsUnicodeToUTF16.cpp',
     '../ucvlatin/nsUnicodeToVISCII.cpp',
     '../ucvlatin/nsUnicodeToVPS.cpp',
     '../ucvlatin/nsUserDefinedToUnicode.cpp',
     '../ucvlatin/nsUTF16ToUnicode.cpp',
--- a/intl/uconv/src/nsScriptableUConv.cpp
+++ b/intl/uconv/src/nsScriptableUConv.cpp
@@ -251,41 +251,72 @@ nsScriptableUnicodeConverter::SetIsInter
   mIsInternal = aIsInternal;
   return NS_OK;
 }
 
 nsresult
 nsScriptableUnicodeConverter::InitConverter()
 {
   mEncoder = nullptr;
+  mDecoder = nullptr;
 
   nsAutoCString encoding;
   if (mIsInternal) {
-    encoding.Assign(mCharset);
-    // Better have a valid encoding name at this point! Otherwise, we'll
-    // crash with MOZ_ASSERT in debug builds. However, since this code
-    // might be called by severely misguided extensions in opt builds, the
-    // error condition is tested for below.
-    mEncoder = EncodingUtils::EncoderForEncoding(mCharset);
-    mDecoder = EncodingUtils::DecoderForEncoding(mCharset);
-    if (!mEncoder || !mDecoder) {
+    // For compatibility with legacy extensions, let's try to see if the label
+    // happens to be ASCII-case-insensitively an encoding. This should allow
+    // for things like "utf-7" and "x-Mac-Hebrew".
+    nsAutoCString contractId;
+    nsAutoCString label(mCharset);
+    EncodingUtils::TrimSpaceCharacters(label);
+    // Let's try in lower case if we didn't get an decoder. E.g. x-mac-ce
+    // and x-imap4-modified-utf7 are all lower case.
+    ToLowerCase(label);
+    if (label.EqualsLiteral("replacement")) {
+      // reject "replacement"
       return NS_ERROR_UCONV_NOCONV;
     }
-  } else {
+    contractId.AssignLiteral(NS_UNICODEENCODER_CONTRACTID_BASE);
+    contractId.Append(label);
+    mEncoder = do_CreateInstance(contractId.get());
+    contractId.AssignLiteral(NS_UNICODEDECODER_CONTRACTID_BASE);
+    contractId.Append(label);
+    mDecoder = do_CreateInstance(contractId.get());
+    if (!mDecoder) {
+      // The old code seemed to want both a decoder and an encoder. Since some
+      // internal encodings will be decoder-only in the future, let's relax
+      // this. Note that the other methods check mEncoder for null anyway.
+      // Let's try the upper case. E.g. UTF-7 and ISO-2022-CN have upper
+      // case Gecko-canonical names.
+      ToUpperCase(label);
+      contractId.AssignLiteral(NS_UNICODEENCODER_CONTRACTID_BASE);
+      contractId.Append(label);
+      mEncoder = do_CreateInstance(contractId.get());
+      contractId.AssignLiteral(NS_UNICODEDECODER_CONTRACTID_BASE);
+      contractId.Append(label);
+      mDecoder = do_CreateInstance(contractId.get());
+      // If still no decoder, use the normal non-internal case below.
+    }
+  }
+
+  if (!mDecoder) {
     if (!EncodingUtils::FindEncodingForLabelNoReplacement(mCharset, encoding)) {
       return NS_ERROR_UCONV_NOCONV;
     }
     mEncoder = EncodingUtils::EncoderForEncoding(encoding);
     mDecoder = EncodingUtils::DecoderForEncoding(encoding);
   }
 
   // The UTF-8 decoder used to throw regardless of the error behavior.
   // Simulating the old behavior for compatibility with legacy callers
   // (including addons). If callers want a control over the behavior,
   // they should switch to TextDecoder.
   if (encoding.EqualsLiteral("UTF-8")) {
     mDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
   }
 
+  if (!mEncoder) {
+    return NS_OK;
+  }
+
   return mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
                                           nullptr,
                                           (char16_t)'?');
 }
--- a/intl/uconv/src/nsUConvModule.cpp
+++ b/intl/uconv/src/nsUConvModule.cpp
@@ -68,17 +68,16 @@
 #include "nsMacRomanianToUnicode.h"
 #include "nsMacCyrillicToUnicode.h"
 #include "nsMacIcelandicToUnicode.h"
 #include "nsARMSCII8ToUnicode.h"
 #include "nsTCVN5712ToUnicode.h"
 #include "nsVISCIIToUnicode.h"
 #include "nsVPSToUnicode.h"
 #include "nsUTF16ToUnicode.h"
-#include "nsT61ToUnicode.h"
 #include "nsUserDefinedToUnicode.h"
 #include "nsUnicodeToAscii.h"
 #include "nsUnicodeToISO88592.h"
 #include "nsUnicodeToISO88593.h"
 #include "nsUnicodeToISO88594.h"
 #include "nsUnicodeToISO88595.h"
 #include "nsUnicodeToISO88596.h"
 #include "nsUnicodeToISO88596E.h"
@@ -115,17 +114,16 @@
 #include "nsUnicodeToMacRomanian.h"
 #include "nsUnicodeToMacCyrillic.h"
 #include "nsUnicodeToMacIcelandic.h"
 #include "nsUnicodeToARMSCII8.h"
 #include "nsUnicodeToTCVN5712.h"
 #include "nsUnicodeToVISCII.h"
 #include "nsUnicodeToVPS.h"
 #include "nsUnicodeToUTF16.h"
-#include "nsUnicodeToT61.h"
 #include "nsUnicodeToUserDefined.h"
 #include "nsMacArabicToUnicode.h"
 #include "nsMacDevanagariToUnicode.h"
 #include "nsMacFarsiToUnicode.h"
 #include "nsMacGujaratiToUnicode.h"
 #include "nsMacGurmukhiToUnicode.h"
 #include "nsMacHebrewToUnicode.h"
 #include "nsUnicodeToMacArabic.h"
@@ -173,18 +171,16 @@
 #include "nsUnicodeToBIG5.h"
 #include "nsBIG5HKSCSToUnicode.h"
 #include "nsUnicodeToBIG5HKSCS.h"
 #include "nsUnicodeToHKSCS.h"
 
 // ucvko
 #include "nsUCvKOCID.h"
 #include "nsUCvKODll.h"
-#include "nsJohabToUnicode.h"
-#include "nsUnicodeToJohab.h"
 #include "nsCP949ToUnicode.h"
 #include "nsUnicodeToCP949.h"
 #include "nsISO2022KRToUnicode.h"
 
 // ucvcn
 #include "nsUCvCnCID.h"
 #include "nsHZToUnicode.h"
 #include "nsUnicodeToHZ.h"
@@ -245,17 +241,16 @@ NS_UCONV_REG_UNREG("x-mac-cyrillic", NS_
 NS_UCONV_REG_UNREG("x-mac-icelandic", NS_MACICELANDICTOUNICODE_CID, NS_UNICODETOMACICELANDIC_CID)
 NS_UCONV_REG_UNREG("armscii-8", NS_ARMSCII8TOUNICODE_CID, NS_UNICODETOARMSCII8_CID)
 NS_UCONV_REG_UNREG("x-viet-tcvn5712", NS_TCVN5712TOUNICODE_CID, NS_UNICODETOTCVN5712_CID)
 NS_UCONV_REG_UNREG("VISCII", NS_VISCIITOUNICODE_CID, NS_UNICODETOVISCII_CID)
 NS_UCONV_REG_UNREG("x-viet-vps", NS_VPSTOUNICODE_CID, NS_UNICODETOVPS_CID)
 NS_UCONV_REG_UNREG("UTF-16", NS_UTF16TOUNICODE_CID, NS_UNICODETOUTF16_CID)
 NS_UCONV_REG_UNREG("UTF-16BE", NS_UTF16BETOUNICODE_CID, NS_UNICODETOUTF16BE_CID)
 NS_UCONV_REG_UNREG("UTF-16LE", NS_UTF16LETOUNICODE_CID, NS_UNICODETOUTF16LE_CID)
-NS_UCONV_REG_UNREG("T.61-8bit", NS_T61TOUNICODE_CID, NS_UNICODETOT61_CID)
 NS_UCONV_REG_UNREG("x-user-defined", NS_USERDEFINEDTOUNICODE_CID, NS_UNICODETOUSERDEFINED_CID)
 NS_UCONV_REG_UNREG("x-mac-arabic" , NS_MACARABICTOUNICODE_CID, NS_UNICODETOMACARABIC_CID)
 NS_UCONV_REG_UNREG("x-mac-devanagari" , NS_MACDEVANAGARITOUNICODE_CID, NS_UNICODETOMACDEVANAGARI_CID)
 NS_UCONV_REG_UNREG("x-mac-farsi" , NS_MACFARSITOUNICODE_CID, NS_UNICODETOMACFARSI_CID)
 NS_UCONV_REG_UNREG("x-mac-gurmukhi" , NS_MACGURMUKHITOUNICODE_CID, NS_UNICODETOMACGURMUKHI_CID)
 NS_UCONV_REG_UNREG("x-mac-gujarati" , NS_MACGUJARATITOUNICODE_CID, NS_UNICODETOMACGUJARATI_CID)
 NS_UCONV_REG_UNREG("x-mac-hebrew" , NS_MACHEBREWTOUNICODE_CID, NS_UNICODETOMACHEBREW_CID)
 
@@ -280,17 +275,16 @@ NS_UCONV_REG_UNREG("x-euc-tw", NS_EUCTWT
     // ucvtw
 NS_UCONV_REG_UNREG("Big5", NS_BIG5TOUNICODE_CID, NS_UNICODETOBIG5_CID)
 NS_UCONV_REG_UNREG("Big5-HKSCS", NS_BIG5HKSCSTOUNICODE_CID, NS_UNICODETOBIG5HKSCS_CID)
   
 NS_UCONV_REG_UNREG_ENCODER("hkscs-1" , NS_UNICODETOHKSCS_CID)
 
     // ucvko
 NS_UCONV_REG_UNREG("EUC-KR", NS_EUCKRTOUNICODE_CID, NS_UNICODETOEUCKR_CID)
-NS_UCONV_REG_UNREG("x-johab", NS_JOHABTOUNICODE_CID, NS_UNICODETOJOHAB_CID)
 NS_UCONV_REG_UNREG_DECODER("ISO-2022-KR", NS_ISO2022KRTOUNICODE_CID)
 
 // ucvcn
 NS_UCONV_REG_UNREG("GB2312", NS_GB2312TOUNICODE_CID, NS_UNICODETOGB2312_CID)
 NS_UCONV_REG_UNREG("gbk", NS_GBKTOUNICODE_CID, NS_UNICODETOGBK_CID)
 NS_UCONV_REG_UNREG("HZ-GB-2312", NS_HZTOUNICODE_CID, NS_UNICODETOHZ_CID)
 NS_UCONV_REG_UNREG("gb18030", NS_GB18030TOUNICODE_CID, NS_UNICODETOGB18030_CID)
 NS_UCONV_REG_UNREG_DECODER("ISO-2022-CN", NS_ISO2022CNTOUNICODE_CID)
@@ -440,20 +434,16 @@ const uint16_t g_utKSC5601Mapping[] = {
 const uint16_t g_ufKSC5601Mapping[] = {
 #include "u20kscgl.uf"
 };
 
 const uint16_t g_HangulNullMapping[] ={
   0x0001, 0x0004, 0x0005, 0x0008, 0x0000, 0xAC00, 0xD7A3, 0xAC00
 };
 
-const uint16_t g_ufJohabJamoMapping[] ={   
-#include "johabjamo.uf"
-};
-
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTextToSubURI)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUTF8ConverterService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsConverterInputStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsConverterOutputStream)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableUnicodeConverter)
 
 NS_DEFINE_NAMED_CID(NS_TEXTTOSUBURI_CID);
 NS_DEFINE_NAMED_CID(NS_CONVERTERINPUTSTREAM_CID);
@@ -511,17 +501,16 @@ NS_DEFINE_NAMED_CID(NS_MACCYRILLICTOUNIC
 NS_DEFINE_NAMED_CID(NS_MACICELANDICTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_ARMSCII8TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_TCVN5712TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_VISCIITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_VPSTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UTF16TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UTF16BETOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UTF16LETOUNICODE_CID);
-NS_DEFINE_NAMED_CID(NS_T61TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_USERDEFINEDTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACARABICTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACDEVANAGARITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACFARSITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACGURMUKHITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACGUJARATITOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_MACHEBREWTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOASCII_CID);
@@ -566,17 +555,16 @@ NS_DEFINE_NAMED_CID(NS_UNICODETOMACCYRIL
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACICELANDIC_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOARMSCII8_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOTCVN5712_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOVISCII_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOVPS_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16BE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16LE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUTF16_CID);
-NS_DEFINE_NAMED_CID(NS_UNICODETOT61_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOUSERDEFINED_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACARABIC_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACDEVANAGARI_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACFARSI_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACGURMUKHI_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACGUJARATI_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOMACHEBREW_CID);
 NS_DEFINE_NAMED_CID(NS_CP850TOUNICODE_CID);
@@ -602,18 +590,16 @@ NS_DEFINE_NAMED_CID(NS_EUCTWTOUNICODE_CI
 NS_DEFINE_NAMED_CID(NS_UNICODETOEUCTW_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOBIG5_CID);
 NS_DEFINE_NAMED_CID(NS_BIG5TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOBIG5HKSCS_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOHKSCS_CID);
 NS_DEFINE_NAMED_CID(NS_BIG5HKSCSTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_EUCKRTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOEUCKR_CID);
-NS_DEFINE_NAMED_CID(NS_JOHABTOUNICODE_CID);
-NS_DEFINE_NAMED_CID(NS_UNICODETOJOHAB_CID);
 NS_DEFINE_NAMED_CID(NS_ISO2022KRTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_GB2312TOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOGB2312_CID);
 NS_DEFINE_NAMED_CID(NS_GBKTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOGBK_CID);
 NS_DEFINE_NAMED_CID(NS_HZTOUNICODE_CID);
 NS_DEFINE_NAMED_CID(NS_UNICODETOHZ_CID);
 NS_DEFINE_NAMED_CID(NS_GB18030TOUNICODE_CID);
@@ -677,17 +663,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_MACICELANDICTOUNICODE_CID, false, nullptr, nsMacIcelandicToUnicodeConstructor },
   { &kNS_ARMSCII8TOUNICODE_CID, false, nullptr, nsARMSCII8ToUnicodeConstructor },
   { &kNS_TCVN5712TOUNICODE_CID, false, nullptr, nsTCVN5712ToUnicodeConstructor },
   { &kNS_VISCIITOUNICODE_CID, false, nullptr, nsVISCIIToUnicodeConstructor },
   { &kNS_VPSTOUNICODE_CID, false, nullptr, nsVPSToUnicodeConstructor },
   { &kNS_UTF16TOUNICODE_CID, false, nullptr, nsUTF16ToUnicodeConstructor },
   { &kNS_UTF16BETOUNICODE_CID, false, nullptr, nsUTF16BEToUnicodeConstructor },
   { &kNS_UTF16LETOUNICODE_CID, false, nullptr, nsUTF16LEToUnicodeConstructor },
-  { &kNS_T61TOUNICODE_CID, false, nullptr, nsT61ToUnicodeConstructor },
   { &kNS_USERDEFINEDTOUNICODE_CID, false, nullptr, nsUserDefinedToUnicodeConstructor },
   { &kNS_MACARABICTOUNICODE_CID, false, nullptr, nsMacArabicToUnicodeConstructor },
   { &kNS_MACDEVANAGARITOUNICODE_CID, false, nullptr, nsMacDevanagariToUnicodeConstructor },
   { &kNS_MACFARSITOUNICODE_CID, false, nullptr, nsMacFarsiToUnicodeConstructor },
   { &kNS_MACGURMUKHITOUNICODE_CID, false, nullptr, nsMacGurmukhiToUnicodeConstructor },
   { &kNS_MACGUJARATITOUNICODE_CID, false, nullptr, nsMacGujaratiToUnicodeConstructor },
   { &kNS_MACHEBREWTOUNICODE_CID, false, nullptr, nsMacHebrewToUnicodeConstructor },
   { &kNS_UNICODETOASCII_CID, false, nullptr, nsUnicodeToAsciiConstructor },
@@ -732,17 +717,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_UNICODETOMACICELANDIC_CID, false, nullptr, nsUnicodeToMacIcelandicConstructor },
   { &kNS_UNICODETOARMSCII8_CID, false, nullptr, nsUnicodeToARMSCII8Constructor },
   { &kNS_UNICODETOTCVN5712_CID, false, nullptr, nsUnicodeToTCVN5712Constructor },
   { &kNS_UNICODETOVISCII_CID, false, nullptr, nsUnicodeToVISCIIConstructor },
   { &kNS_UNICODETOVPS_CID, false, nullptr, nsUnicodeToVPSConstructor },
   { &kNS_UNICODETOUTF16BE_CID, false, nullptr, nsUnicodeToUTF16BEConstructor },
   { &kNS_UNICODETOUTF16LE_CID, false, nullptr, nsUnicodeToUTF16LEConstructor },
   { &kNS_UNICODETOUTF16_CID, false, nullptr, nsUnicodeToUTF16Constructor },
-  { &kNS_UNICODETOT61_CID, false, nullptr, nsUnicodeToT61Constructor },
   { &kNS_UNICODETOUSERDEFINED_CID, false, nullptr, nsUnicodeToUserDefinedConstructor },
   { &kNS_UNICODETOMACARABIC_CID, false, nullptr, nsUnicodeToMacArabicConstructor },
   { &kNS_UNICODETOMACDEVANAGARI_CID, false, nullptr, nsUnicodeToMacDevanagariConstructor },
   { &kNS_UNICODETOMACFARSI_CID, false, nullptr, nsUnicodeToMacFarsiConstructor },
   { &kNS_UNICODETOMACGURMUKHI_CID, false, nullptr, nsUnicodeToMacGurmukhiConstructor },
   { &kNS_UNICODETOMACGUJARATI_CID, false, nullptr, nsUnicodeToMacGujaratiConstructor },
   { &kNS_UNICODETOMACHEBREW_CID, false, nullptr, nsUnicodeToMacHebrewConstructor },
   { &kNS_CP850TOUNICODE_CID, false, nullptr, nsCP850ToUnicodeConstructor },
@@ -768,18 +752,16 @@ static const mozilla::Module::CIDEntry k
   { &kNS_UNICODETOEUCTW_CID, false, nullptr, nsUnicodeToEUCTWConstructor },
   { &kNS_UNICODETOBIG5_CID, false, nullptr, nsUnicodeToBIG5Constructor },
   { &kNS_BIG5TOUNICODE_CID, false, nullptr, nsBIG5ToUnicodeConstructor },
   { &kNS_UNICODETOBIG5HKSCS_CID, false, nullptr, nsUnicodeToBIG5HKSCSConstructor },
   { &kNS_UNICODETOHKSCS_CID, false, nullptr, nsUnicodeToHKSCSConstructor },
   { &kNS_BIG5HKSCSTOUNICODE_CID, false, nullptr, nsBIG5HKSCSToUnicodeConstructor },
   { &kNS_EUCKRTOUNICODE_CID, false, nullptr, nsCP949ToUnicodeConstructor },
   { &kNS_UNICODETOEUCKR_CID, false, nullptr, nsUnicodeToCP949Constructor },
-  { &kNS_JOHABTOUNICODE_CID, false, nullptr, nsJohabToUnicodeConstructor },
-  { &kNS_UNICODETOJOHAB_CID, false, nullptr, nsUnicodeToJohabConstructor },
   { &kNS_ISO2022KRTOUNICODE_CID, false, nullptr, nsISO2022KRToUnicodeConstructor },
   { &kNS_GB2312TOUNICODE_CID, false, nullptr, nsGB18030ToUnicodeConstructor },
   { &kNS_UNICODETOGB2312_CID, false, nullptr, nsUnicodeToGB2312V2Constructor },
   { &kNS_GBKTOUNICODE_CID, false, nullptr, nsGB18030ToUnicodeConstructor },
   { &kNS_UNICODETOGBK_CID, false, nullptr, nsUnicodeToGBKConstructor },
   { &kNS_HZTOUNICODE_CID, false, nullptr, nsHZToUnicodeConstructor },
   { &kNS_UNICODETOHZ_CID, false, nullptr, nsUnicodeToHZConstructor },
   { &kNS_GB18030TOUNICODE_CID, false, nullptr, nsGB18030ToUnicodeConstructor },
@@ -845,17 +827,16 @@ static const mozilla::Module::ContractID
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-icelandic", &kNS_MACICELANDICTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "armscii-8", &kNS_ARMSCII8TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-viet-tcvn5712", &kNS_TCVN5712TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "VISCII", &kNS_VISCIITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-viet-vps", &kNS_VPSTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16", &kNS_UTF16TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16BE", &kNS_UTF16BETOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "UTF-16LE", &kNS_UTF16LETOUNICODE_CID },
-  { NS_UNICODEDECODER_CONTRACTID_BASE "T.61-8bit", &kNS_T61TOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-user-defined", &kNS_USERDEFINEDTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-arabic", &kNS_MACARABICTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-devanagari", &kNS_MACDEVANAGARITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-farsi", &kNS_MACFARSITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-gurmukhi", &kNS_MACGURMUKHITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-gujarati", &kNS_MACGUJARATITOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "x-mac-hebrew", &kNS_MACHEBREWTOUNICODE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "us-ascii", &kNS_UNICODETOASCII_CID },
@@ -900,17 +881,16 @@ static const mozilla::Module::ContractID
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-icelandic", &kNS_UNICODETOMACICELANDIC_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "armscii-8", &kNS_UNICODETOARMSCII8_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-viet-tcvn5712", &kNS_UNICODETOTCVN5712_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "VISCII", &kNS_UNICODETOVISCII_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-viet-vps", &kNS_UNICODETOVPS_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16BE", &kNS_UNICODETOUTF16BE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16LE", &kNS_UNICODETOUTF16LE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "UTF-16", &kNS_UNICODETOUTF16_CID },
-  { NS_UNICODEENCODER_CONTRACTID_BASE "T.61-8bit", &kNS_UNICODETOT61_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-user-defined", &kNS_UNICODETOUSERDEFINED_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-arabic", &kNS_UNICODETOMACARABIC_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-devanagari", &kNS_UNICODETOMACDEVANAGARI_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-farsi", &kNS_UNICODETOMACFARSI_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-gurmukhi", &kNS_UNICODETOMACGURMUKHI_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-gujarati", &kNS_UNICODETOMACGUJARATI_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-mac-hebrew", &kNS_UNICODETOMACHEBREW_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "IBM850", &kNS_CP850TOUNICODE_CID },
@@ -936,18 +916,16 @@ static const mozilla::Module::ContractID
   { NS_UNICODEENCODER_CONTRACTID_BASE "x-euc-tw", &kNS_UNICODETOEUCTW_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "Big5", &kNS_UNICODETOBIG5_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "Big5", &kNS_BIG5TOUNICODE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "Big5-HKSCS", &kNS_UNICODETOBIG5HKSCS_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "hkscs-1", &kNS_UNICODETOHKSCS_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "Big5-HKSCS", &kNS_BIG5HKSCSTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "EUC-KR", &kNS_EUCKRTOUNICODE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "EUC-KR", &kNS_UNICODETOEUCKR_CID },
-  { NS_UNICODEDECODER_CONTRACTID_BASE "x-johab", &kNS_JOHABTOUNICODE_CID },
-  { NS_UNICODEENCODER_CONTRACTID_BASE "x-johab", &kNS_UNICODETOJOHAB_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "ISO-2022-KR", &kNS_ISO2022KRTOUNICODE_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "GB2312", &kNS_GB2312TOUNICODE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "GB2312", &kNS_UNICODETOGB2312_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "gbk", &kNS_GBKTOUNICODE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "gbk", &kNS_UNICODETOGBK_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "HZ-GB-2312", &kNS_HZTOUNICODE_CID },
   { NS_UNICODEENCODER_CONTRACTID_BASE "HZ-GB-2312", &kNS_UNICODETOHZ_CID },
   { NS_UNICODEDECODER_CONTRACTID_BASE "gb18030", &kNS_GB18030TOUNICODE_CID },
copy from intl/uconv/tests/unit/test_decode_x_mac_hebrew_internal.js
copy to intl/uconv/tests/unit/test_bug1008832.js
--- a/intl/uconv/tests/unit/test_decode_x_mac_hebrew_internal.js
+++ b/intl/uconv/tests/unit/test_bug1008832.js
@@ -1,13 +1,13 @@
-// Tests conversion from x-mac-hebrew to Unicode
+// Test case-insensitive internal encoding handling in nsIScriptableUConv
 	
 load('CharsetConversionTests.js');
 	
 const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";
     
 const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u00c4\ufb1f\u00c7\u00c9\u00d1\u00d6\u00dc\u00e1\u00e0\u00e2\u00e4\u00e3\u00e5\u00e7\u00e9\u00e8\u00ea\u00eb\u00ed\u00ec\u00ee\u00ef\u00f1\u00f3\u00f2\u00f4\u00f6\u00f5\u00fa\u00f9\u00fb\u00fc !\"#$%\u20aa'()*+,-./0123456789:;<=>?\ufffd\u201e\uf89b\uf89c\uf89d\uf89e\u05bc\ufb4b\ufb35\u2026\u00a0\u05b8\u05b7\u05b5\u05b6\u05b4\u2013\u2014\u201c\u201d\u2018\u2019\ufb2a\ufb2b\u05bf\u05b0\u05b2\u05b1\u05bb\u05b9\ufffd\u05b3\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea}]{[|";
 
-const aliases = [ "x-mac-hebrew" ];
+const aliases = [ "x-Mac-Hebrew" ];
 
 function run_test() {
   testDecodeAliasesInternal();
 }
--- a/intl/uconv/tests/unit/xpcshell.ini
+++ b/intl/uconv/tests/unit/xpcshell.ini
@@ -140,8 +140,9 @@ support-files =
 [test_encode_x_mac_hebrew_internal.js]
 [test_encode_x_mac_icelandic.js]
 [test_encode_macintosh.js]
 [test_encode_x_mac_romanian.js]
 [test_encode_x_mac_turkish.js]
 [test_encode_x_mac_ukrainian.js]
 [test_utf8_illegals.js]
 [test_input_stream.js]
+[test_bug1008832.js]
deleted file mode 100644
--- a/intl/uconv/ucvko/johabjamo.uf
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/*========================================================
-  This is a Generated file. Please don't edit it.
-
-  The tool which used to generate this file is called umaptable.
-  You can find this tool under mozilla/intl/uconv/tools/umaptable.c.
-  If you have any problem of this file. Please contact 
-  Netscape Client International Team or 
-  ftang@netscape <Frank Tang> 
-
-  Note manually added by Jungshik Shin <jshin@mailaps.org>
-
-  The table was generated by filtering JOHAB.TXT at
-  http://jshin.net/faq/JOHAB.TXT.gz
-
-  gunzip -c JOHAB.TXT.gz | egrep '^0x.... +0x31(3[1-F]|[45][0-F]|6[0-3])' \ 
-  | umaptable -uf
-
-              Table in Debug form 
-Begin of Item 0000
- Format 0
-  srcBegin = 313A
-  srcEnd = 3140
-  destBegin = 844A
-End of Item 0000 
-
-Begin of Item 0001
- Format 1
-  srcBegin = 3131
-  srcEnd = 3163
-  mappingOffset = 0000
- Mapping  = 
-  8841 8C41 8444 9041 8446 8447 9441 9841 
-  9C41 FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  A041 A441 A841 8454 AC41 B041 B441 B841 
-  BC41 C041 C441 C841 CC41 D041 8461 8481 
-  84A1 84C1 84E1 8541 8561 8581 85A1 85C1 
-  85E1 8641 8661 8681 86A1 86C1 86E1 8741 
-  8761 8781 87A1 
-End of Item 0001 
-
-========================================================*/
-/* Offset=0x0000  ItemOfList */
-  0x0002,
-/*-------------------------------------------------------*/
-/* Offset=0x0001  offsetToFormatArray */
-  0x0004,
-/*-------------------------------------------------------*/
-/* Offset=0x0002  offsetToMapCellArray */ 
-  0x0005,
-/*-------------------------------------------------------*/
-/* Offset=0x0003  offsetToMappingTable */ 
-  0x000B,
-/*-------------------------------------------------------*/
-/*       Offset=0x0004   Start of Format Array */ 
-/*	Total of Format 0 : 0x0001			 */
-/*	Total of Format 1 : 0x0001			 */
-/*	Total of Format 2 : 0x0000			 */
-/*	Total of Format 3 : 0x0000			 */
-
-0x0010, 
-/*-------------------------------------------------------*/
-/*       Offset=0x0005   Start of MapCell Array */ 
-/* 0000 */    0x313A, 0x3140, 0x844A, 
-/* 0001 */    0x3131, 0x3163, 0x0000, 
-/*-------------------------------------------------------*/
-/*       Offset=0x000B   Start of MappingTable */ 
-
-/* 0000 */    0x8841, 0x8C41, 0x8444, 0x9041, 0x8446, 0x8447, 0x9441, 0x9841, 
-/* 0008 */    0x9C41, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0010 */    0xA041, 0xA441, 0xA841, 0x8454, 0xAC41, 0xB041, 0xB441, 0xB841, 
-/* 0018 */    0xBC41, 0xC041, 0xC441, 0xC841, 0xCC41, 0xD041, 0x8461, 0x8481, 
-/* 0020 */    0x84A1, 0x84C1, 0x84E1, 0x8541, 0x8561, 0x8581, 0x85A1, 0x85C1, 
-/* 0028 */    0x85E1, 0x8641, 0x8661, 0x8681, 0x86A1, 0x86C1, 0x86E1, 0x8741, 
-/* 0030 */    0x8761, 0x8781, 0x87A1, 
-/*	End of table Total Length = 0x003E * 2 */
deleted file mode 100644
--- a/intl/uconv/ucvko/johabjamo.ut
+++ /dev/null
@@ -1,355 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/*========================================================
-  This is a Generated file. Please don't edit it.
-
-  The tool which used to generate this file is called umaptable.
-  You can find this tool under mozilla/intl/uconv/tools/umaptable.c.
-  If you have any problem of this file. Please contact 
-  Netscape Client International Team or 
-  ftang@netscape <Frank Tang> 
-
-  Note manually added by Jungshik Shin <jshin@mailaps.org>
-
-  The table was generated by filtering JOHAB.TXT at
-  http://jshin.net/faq/JOHAB.TXT.gz 
-
-  gunzip -c JOHAB.TXT.gz | egrep '^0x.... +0x31(3[1-F]|[45][0-F]|6[0-3])' \
-  | umaptable -ut
-
-              Table in Debug form 
-Begin of Item 0000
- Format 0
-  srcBegin = 844A
-  srcEnd = 8450
-  destBegin = 313A
-End of Item 0000 
-
-Begin of Item 0001
- Format 1
-  srcBegin = 8444
-  srcEnd = 8447
-  mappingOffset = 0000
- Mapping  = 
-  3133 FFFD 3135 3136 
-End of Item 0001 
-
-Begin of Item 0002
- Format 2
-  srcBegin = 8454
-  destBegin = 3144
-End of Item 0002 
-
-Begin of Item 0003
- Format 2
-  srcBegin = 8461
-  destBegin = 314F
-End of Item 0003 
-
-Begin of Item 0004
- Format 2
-  srcBegin = 8481
-  destBegin = 3150
-End of Item 0004 
-
-Begin of Item 0005
- Format 2
-  srcBegin = 84A1
-  destBegin = 3151
-End of Item 0005 
-
-Begin of Item 0006
- Format 2
-  srcBegin = 84C1
-  destBegin = 3152
-End of Item 0006 
-
-Begin of Item 0007
- Format 2
-  srcBegin = 84E1
-  destBegin = 3153
-End of Item 0007 
-
-Begin of Item 0008
- Format 2
-  srcBegin = 8541
-  destBegin = 3154
-End of Item 0008 
-
-Begin of Item 0009
- Format 2
-  srcBegin = 8561
-  destBegin = 3155
-End of Item 0009 
-
-Begin of Item 000A
- Format 2
-  srcBegin = 8581
-  destBegin = 3156
-End of Item 000A 
-
-Begin of Item 000B
- Format 2
-  srcBegin = 85A1
-  destBegin = 3157
-End of Item 000B 
-
-Begin of Item 000C
- Format 2
-  srcBegin = 85C1
-  destBegin = 3158
-End of Item 000C 
-
-Begin of Item 000D
- Format 2
-  srcBegin = 85E1
-  destBegin = 3159
-End of Item 000D 
-
-Begin of Item 000E
- Format 2
-  srcBegin = 8641
-  destBegin = 315A
-End of Item 000E 
-
-Begin of Item 000F
- Format 2
-  srcBegin = 8661
-  destBegin = 315B
-End of Item 000F 
-
-Begin of Item 0010
- Format 2
-  srcBegin = 8681
-  destBegin = 315C
-End of Item 0010 
-
-Begin of Item 0011
- Format 2
-  srcBegin = 86A1
-  destBegin = 315D
-End of Item 0011 
-
-Begin of Item 0012
- Format 2
-  srcBegin = 86C1
-  destBegin = 315E
-End of Item 0012 
-
-Begin of Item 0013
- Format 2
-  srcBegin = 86E1
-  destBegin = 315F
-End of Item 0013 
-
-Begin of Item 0014
- Format 2
-  srcBegin = 8741
-  destBegin = 3160
-End of Item 0014 
-
-Begin of Item 0015
- Format 2
-  srcBegin = 8761
-  destBegin = 3161
-End of Item 0015 
-
-Begin of Item 0016
- Format 2
-  srcBegin = 8781
-  destBegin = 3162
-End of Item 0016 
-
-Begin of Item 0017
- Format 2
-  srcBegin = 87A1
-  destBegin = 3163
-End of Item 0017 
-
-Begin of Item 0018
- Format 2
-  srcBegin = 8841
-  destBegin = 3131
-End of Item 0018 
-
-Begin of Item 0019
- Format 2
-  srcBegin = 8C41
-  destBegin = 3132
-End of Item 0019 
-
-Begin of Item 001A
- Format 2
-  srcBegin = 9041
-  destBegin = 3134
-End of Item 001A 
-
-Begin of Item 001B
- Format 2
-  srcBegin = 9441
-  destBegin = 3137
-End of Item 001B 
-
-Begin of Item 001C
- Format 2
-  srcBegin = 9841
-  destBegin = 3138
-End of Item 001C 
-
-Begin of Item 001D
- Format 2
-  srcBegin = 9C41
-  destBegin = 3139
-End of Item 001D 
-
-Begin of Item 001E
- Format 2
-  srcBegin = A041
-  destBegin = 3141
-End of Item 001E 
-
-Begin of Item 001F
- Format 2
-  srcBegin = A441
-  destBegin = 3142
-End of Item 001F 
-
-Begin of Item 0020
- Format 2
-  srcBegin = A841
-  destBegin = 3143
-End of Item 0020 
-
-Begin of Item 0021
- Format 2
-  srcBegin = AC41
-  destBegin = 3145
-End of Item 0021 
-
-Begin of Item 0022
- Format 2
-  srcBegin = B041
-  destBegin = 3146
-End of Item 0022 
-
-Begin of Item 0023
- Format 2
-  srcBegin = B441
-  destBegin = 3147
-End of Item 0023 
-
-Begin of Item 0024
- Format 2
-  srcBegin = B841
-  destBegin = 3148
-End of Item 0024 
-
-Begin of Item 0025
- Format 2
-  srcBegin = BC41
-  destBegin = 3149
-End of Item 0025 
-
-Begin of Item 0026
- Format 2
-  srcBegin = C041
-  destBegin = 314A
-End of Item 0026 
-
-Begin of Item 0027
- Format 2
-  srcBegin = C441
-  destBegin = 314B
-End of Item 0027 
-
-Begin of Item 0028
- Format 2
-  srcBegin = C841
-  destBegin = 314C
-End of Item 0028 
-
-Begin of Item 0029
- Format 2
-  srcBegin = CC41
-  destBegin = 314D
-End of Item 0029 
-
-Begin of Item 002A
- Format 2
-  srcBegin = D041
-  destBegin = 314E
-End of Item 002A 
-
-========================================================*/
-/* Offset=0x0000  ItemOfList */
-  0x002B,
-/*-------------------------------------------------------*/
-/* Offset=0x0001  offsetToFormatArray */
-  0x0004,
-/*-------------------------------------------------------*/
-/* Offset=0x0002  offsetToMapCellArray */ 
-  0x000F,
-/*-------------------------------------------------------*/
-/* Offset=0x0003  offsetToMappingTable */ 
-  0x0090,
-/*-------------------------------------------------------*/
-/*       Offset=0x0004   Start of Format Array */ 
-/*	Total of Format 0 : 0x0001			 */
-/*	Total of Format 1 : 0x0001			 */
-/*	Total of Format 2 : 0x0029			 */
-/*	Total of Format 3 : 0x0000			 */
-
-0x2210, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 0x2222, 
-0x2222, 0x2222, 0x0222, 
-/*-------------------------------------------------------*/
-/*       Offset=0x000F   Start of MapCell Array */ 
-/* 0000 */    0x844A, 0x8450, 0x313A, 
-/* 0001 */    0x8444, 0x8447, 0x0000, 
-/* 0002 */    0x8454, 0x0000, 0x3144, 
-/* 0003 */    0x8461, 0x0000, 0x314F, 
-/* 0004 */    0x8481, 0x0000, 0x3150, 
-/* 0005 */    0x84A1, 0x0000, 0x3151, 
-/* 0006 */    0x84C1, 0x0000, 0x3152, 
-/* 0007 */    0x84E1, 0x0000, 0x3153, 
-/* 0008 */    0x8541, 0x0000, 0x3154, 
-/* 0009 */    0x8561, 0x0000, 0x3155, 
-/* 000A */    0x8581, 0x0000, 0x3156, 
-/* 000B */    0x85A1, 0x0000, 0x3157, 
-/* 000C */    0x85C1, 0x0000, 0x3158, 
-/* 000D */    0x85E1, 0x0000, 0x3159, 
-/* 000E */    0x8641, 0x0000, 0x315A, 
-/* 000F */    0x8661, 0x0000, 0x315B, 
-/* 0010 */    0x8681, 0x0000, 0x315C, 
-/* 0011 */    0x86A1, 0x0000, 0x315D, 
-/* 0012 */    0x86C1, 0x0000, 0x315E, 
-/* 0013 */    0x86E1, 0x0000, 0x315F, 
-/* 0014 */    0x8741, 0x0000, 0x3160, 
-/* 0015 */    0x8761, 0x0000, 0x3161, 
-/* 0016 */    0x8781, 0x0000, 0x3162, 
-/* 0017 */    0x87A1, 0x0000, 0x3163, 
-/* 0018 */    0x8841, 0x0000, 0x3131, 
-/* 0019 */    0x8C41, 0x0000, 0x3132, 
-/* 001A */    0x9041, 0x0000, 0x3134, 
-/* 001B */    0x9441, 0x0000, 0x3137, 
-/* 001C */    0x9841, 0x0000, 0x3138, 
-/* 001D */    0x9C41, 0x0000, 0x3139, 
-/* 001E */    0xA041, 0x0000, 0x3141, 
-/* 001F */    0xA441, 0x0000, 0x3142, 
-/* 0020 */    0xA841, 0x0000, 0x3143, 
-/* 0021 */    0xAC41, 0x0000, 0x3145, 
-/* 0022 */    0xB041, 0x0000, 0x3146, 
-/* 0023 */    0xB441, 0x0000, 0x3147, 
-/* 0024 */    0xB841, 0x0000, 0x3148, 
-/* 0025 */    0xBC41, 0x0000, 0x3149, 
-/* 0026 */    0xC041, 0x0000, 0x314A, 
-/* 0027 */    0xC441, 0x0000, 0x314B, 
-/* 0028 */    0xC841, 0x0000, 0x314C, 
-/* 0029 */    0xCC41, 0x0000, 0x314D, 
-/* 002A */    0xD041, 0x0000, 0x314E, 
-/*-------------------------------------------------------*/
-/*       Offset=0x0090   Start of MappingTable */ 
-
-/* 0000 */    0x3133, 0xFFFD, 0x3135, 0x3136, 
-/*	End of table Total Length = 0x0094 * 2 */
deleted file mode 100644
--- a/intl/uconv/ucvko/nsJohabToUnicode.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsJohabToUnicode.h"
-#include "nsUCvKODll.h"
-#include "nsUCConstructors.h"
-#include "mozilla/Telemetry.h"
-
-using namespace mozilla;
-
-//----------------------------------------------------------------------
-// Global functions and data [declaration]
-
-static const uScanClassID g_JOHABScanClassIDs[] = {
-  u1ByteCharset,
-  uJohabHangulCharset,
-  u2BytesCharset,
-  uJohabSymbolCharset,
-  uJohabSymbolCharset
-};
-
-static const uRange g_JOHABRanges[] = {
-  { 0x00, 0x7E },
-  { 0x84, 0xD3 },
-  { 0x84, 0xD3 },
-  { 0xD8, 0xDE },
-  { 0xE0, 0xF9 }
-};
-
-static const uint16_t g_utJohabJamoMapping[] ={   
-#include "johabjamo.ut"
-};
-
-static const uint16_t *g_JOHABMappingTableSet [] ={
-  g_ASCIIMappingTable,
-  g_HangulNullMapping,
-  g_utJohabJamoMapping,
-  g_utKSC5601Mapping,
-  g_utKSC5601Mapping
-};
-
-
-//----------------------------------------------------------------------
-// Class nsJohabToUnicode [implementation]
-
-nsresult
-nsJohabToUnicodeConstructor(nsISupports *aOuter, REFNSIID aIID,
-                            void **aResult)
-{
-  Telemetry::Accumulate(Telemetry::DECODER_INSTANTIATED_JOHAB, true);
-  return CreateMultiTableDecoder(sizeof(g_JOHABRanges) / sizeof(g_JOHABRanges[0]),
-                                 (const uRange*) &g_JOHABRanges,
-                                 (uScanClassID*) &g_JOHABScanClassIDs,
-                                 (uMappingTable**) &g_JOHABMappingTableSet, 1,
-                                 aOuter, aIID, aResult);
-}
-
deleted file mode 100644
--- a/intl/uconv/ucvko/nsJohabToUnicode.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsJohabToUnicode_h___
-#define nsJohabToUnicode_h___
-
-#include "nsID.h"
-
-class nsISupports;
-
-/**
- * A character set converter from Johab to Unicode.
- *
- * @created         06/Apr/1999
- * @author  Catalin Rotaru [CATA]
- */
-nsresult
-nsJohabToUnicodeConstructor(nsISupports *aOuter, REFNSIID aIID,
-                            void **aResult);
-
-#endif /* nsJohabToUnicode_h___ */
--- a/intl/uconv/ucvko/nsUCvKOCID.h
+++ b/intl/uconv/ucvko/nsUCvKOCID.h
@@ -23,19 +23,9 @@
 #define NS_UNICODETOEUCKR_CID \
   { 0x379c2778, 0xec77, 0x11d2, {0x8a, 0xac, 0x0, 0x60, 0x8, 0x11, 0xa8, 0x36}}
 
 // Class ID for our UnicodeToISO2022KR charset converter
 // {BA6151A0-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_UNICODETOISO2022KR_CID \
   { 0xba6151a0, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
-// Class ID for our UnicodeToJohab charset converter
-// {D9B1F97E-CFA0-80b6-FB92-9972E48E3DCC}
-#define NS_UNICODETOJOHAB_CID \
-  { 0xd9b1f97e, 0xcfa0, 0x80b6,  {0xfb, 0x92, 0x99, 0x72, 0xe4, 0x8e, 0x3d, 0xcc}} 
-
-// Class ID for our JohabToUnicode charset converter
-// {D9B1F97F-CFA0-80b6-FB92-9972E48E3DCC}
-#define NS_JOHABTOUNICODE_CID \
-  { 0xd9b1f97f, 0xcfa0, 0x80b6,  {0xfb, 0x92, 0x99, 0x72, 0xe4, 0x8e, 0x3d, 0xcc}} 
-
 #endif /* nsUCvKOCID_h___ */
--- a/intl/uconv/ucvko/nsUCvKODll.h
+++ b/intl/uconv/ucvko/nsUCvKODll.h
@@ -5,11 +5,10 @@
 
 #ifndef nsUCvKODll_h_
 #define nsUCvKODll_h_
 
 extern const uint16_t g_utKSC5601Mapping[];
 extern const uint16_t g_ufKSC5601Mapping[];
 extern const uint16_t g_ASCIIMappingTable[];
 extern const uint16_t g_HangulNullMapping[];
-extern const uint16_t g_ufJohabJamoMapping[];
 
 #endif /* nsUCvKODll_h_ */
deleted file mode 100644
--- a/intl/uconv/ucvko/nsUnicodeToJohab.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsUnicodeToJohab.h"
-#include "nsUCvKODll.h"
-#include "nsUCConstructors.h"
-
-//----------------------------------------------------------------------
-// Global functions and data [declaration]
-
-static const uint16_t *g_JohabMappingTable[4] = {
-  g_ASCIIMappingTable,
-  g_HangulNullMapping,
-  g_ufJohabJamoMapping,
-  g_ufKSC5601Mapping
-};
-
-static const uScanClassID g_JohabScanClassTable[4] =  {
-  u1ByteCharset,
-  uJohabHangulCharset,
-  u2BytesCharset,
-  uJohabSymbolCharset
-};
-
-//----------------------------------------------------------------------
-// Class nsUnicodeToJohab [implementation]
-
-nsresult
-nsUnicodeToJohabConstructor(nsISupports *aOuter, REFNSIID aIID,
-                            void **aResult) {
-
-  return CreateMultiTableEncoder(sizeof(g_JohabScanClassTable) / sizeof(g_JohabScanClassTable[0]),
-                                 (uScanClassID*) g_JohabScanClassTable, 
-                                 (uMappingTable**) g_JohabMappingTable,
-                                 2 /* max length = src * 2*/,
-                                 aOuter, aIID, aResult);
-}
deleted file mode 100644
--- a/intl/uconv/ucvko/nsUnicodeToJohab.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsUnicodeToJohab_h___
-#define nsUnicodeToJohab_h___
-
-#include "nsID.h"
-
-class nsISupports;
-
-/**
- * A character set converter from Unicode to Johab.
- *
- */
-nsresult
-nsUnicodeToJohabConstructor(nsISupports *aOuter, REFNSIID aIID,
-                            void **aResult);
-
-#endif /* nsUnicodeToJohab_h___ */
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsT61ToUnicode.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsUCConstructors.h"
-#include "nsT61ToUnicode.h"
-#include "mozilla/Telemetry.h"
-
-using namespace mozilla;
-
-//----------------------------------------------------------------------
-// Global functions and data [declaration]
-
-nsresult
-nsT61ToUnicodeConstructor(nsISupports *aOuter, REFNSIID aIID,
-                          void **aResult) 
-{
-  static const uint16_t g_T61MappingTable[] = {
-#include "t61.ut"
-  };
-
-  static const int16_t g_T61ShiftInTable[] =  {
-      3,  
-      ShiftInCell(u1ByteChar,   1, 0x00, 0xBF),
-      ShiftInCell(u1ByteChar,   1, 0xD0, 0xFF),
-      ShiftInCell(u2BytesChar,  2, 0xC0, 0xCF)
-  };
-
-  Telemetry::Accumulate(Telemetry::DECODER_INSTANTIATED_T61, true);
-  return CreateTableDecoder(uMultibytesCharset,
-                            (uShiftInTable*) &g_T61ShiftInTable, 
-                            (uMappingTable*) &g_T61MappingTable, 1,
-                            aOuter, aIID, aResult);
-}
-
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsT61ToUnicode.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsT61ToUnicode_h___
-#define nsT61ToUnicode_h___
-
-#include "nsISupports.h"
-
-/**
- * A character set converter from T61 to Unicode.
- *
- * @created         18/Mar/1998
- * @author  Catalin Rotaru [CATA]
- */
-nsresult
-nsT61ToUnicodeConstructor(nsISupports *aOuter, REFNSIID aIID,
-                          void **aResult);
-
-#endif /* nsT61ToUnicode_h___ */
--- a/intl/uconv/ucvlatin/nsUCvLatinCID.h
+++ b/intl/uconv/ucvlatin/nsUCvLatinCID.h
@@ -415,21 +415,16 @@
 #define NS_UNICODETOISO885913_CID \
   { 0xba6151ac, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our UnicodeToUTF16BE charset converter
 // {BA6151AD-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_UNICODETOUTF16BE_CID \
   { 0xba6151ad, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
-// Class ID for our UnicodeToT61 charset converter
-// {BA6151AF-1DFA-11d3-B3BF-00805F8A6670}
-#define NS_UNICODETOT61_CID \
-  { 0xba6151af, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
-
 // Class ID for our ISO885910ToUnicode charset converter
 // {BA6151B0-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_ISO885910TOUNICODE_CID \
   { 0xba6151b0, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our ISO885911ToUnicode charset converter
 // {776588a6-86d5-47e2-b6b3-992810078202}
 #define NS_ISO885911TOUNICODE_CID \
@@ -440,21 +435,16 @@
 #define NS_ISO885913TOUNICODE_CID \
   { 0xba6151b1, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our UTF16BEToUnicode charset converter
 // {BA6151B2-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_UTF16BETOUNICODE_CID \
   { 0xba6151b2, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
-// Class ID for our T61ToUnicode charset converter
-// {BA6151B4-1DFA-11d3-B3BF-00805F8A6670}
-#define NS_T61TOUNICODE_CID \
-  { 0xba6151b4, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
-
 // Class ID for our UnicodeToUTF16LE charset converter
 // {BA6151B5-1DFA-11d3-B3BF-00805F8A6670}
 #define NS_UNICODETOUTF16LE_CID \
   { 0xba6151b5, 0x1dfa, 0x11d3, {0xb3, 0xbf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70}}
 
 // Class ID for our UTF16ToUnicode charset converter
 // {d673255d-1184-400a-b0b5-ee9d1295bd85}
 #define NS_UTF16TOUNICODE_CID \
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUnicodeToT61.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsUCConstructors.h"
-#include "nsUnicodeToT61.h"
-
-//----------------------------------------------------------------------
-// Global functions and data [declaration]
-
-nsresult
-nsUnicodeToT61Constructor(nsISupports *aOuter, REFNSIID aIID,
-                          void **aResult) 
-{
-  static const uint16_t g_T61MappingTable[] = {
-#include "t61.uf"
-  };
-
-  static const int16_t g_T61ShiftOutTable[] =  {
-      3,  
-      ShiftOutCell(u1ByteChar,   1, 0x00, 0x00, 0x00, 0xBF),
-      ShiftOutCell(u1ByteChar,   1, 0x00, 0xD0, 0x00, 0xFF),
-      ShiftOutCell(u2BytesChar,  2, 0xC0, 0x41, 0xCF, 0x7A)
-  };
-
-  return CreateTableEncoder(uMultibytesCharset,
-                            (uShiftOutTable*) &g_T61ShiftOutTable, 
-                            (uMappingTable*) &g_T61MappingTable, 2,
-                            aOuter, aIID, aResult);
-}
deleted file mode 100644
--- a/intl/uconv/ucvlatin/nsUnicodeToT61.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsUnicodeToT61_h___
-#define nsUnicodeToT61_h___
-
-#include "nsISupports.h"
-
-/**
- * A character set converter from Unicode to T61.
- *
- * @created         05/Apr/1999
- * @author  Catalin Rotaru [CATA]
- */
-nsresult
-nsUnicodeToT61Constructor(nsISupports *aOuter, REFNSIID aIID,
-                          void **aResult);
-
-#endif /* nsUnicodeToT61_h___ */
deleted file mode 100644
--- a/intl/uconv/ucvlatin/t61.uf
+++ /dev/null
@@ -1,231 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/*========================================================
-  This is a Generated file. Please don't edit it.
-
-  The tool which used to generate this file is called fromu.
-  If you have any problem of this file. Please contact 
-  Netscape Client International Team or 
-  ftang@netscape <Frank Tang> 
-
-              Table in Debug form 
-Begin of Item 0000
- Format 0
-  srcBegin = 0000
-  srcEnd = 0023
-  destBegin = 0000
-End of Item 0000 
-
-Begin of Item 0001
- Format 0
-  srcBegin = 0025
-  srcEnd = 005D
-  destBegin = 0025
-End of Item 0001 
-
-Begin of Item 0002
- Format 0
-  srcBegin = 0061
-  srcEnd = 007D
-  destBegin = 0061
-End of Item 0002 
-
-Begin of Item 0003
- Format 2
-  srcBegin = 0024
-  destBegin = 00A4
-End of Item 0003 
-
-Begin of Item 0004
- Format 1
-  srcBegin = 005E
-  srcEnd = 0060
-  mappingOffset = 0000
- Mapping  = 
-  C320 005F C120 
-End of Item 0004 
-
-Begin of Item 0005
- Format 1
-  srcBegin = 007E
-  srcEnd = 007F
-  mappingOffset = 0003
- Mapping  = 
-  C420 007F 
-End of Item 0005 
-
-Begin of Item 0006
- Format 1
-  srcBegin = 00A0
-  srcEnd = 017E
-  mappingOffset = 0005
- Mapping  = 
-  00A0 00A1 00A2 00A3 FFFD 00A5 00D7 00A7 
-  C820 00D3 00E3 00AB 00D6 00FF 00D2 C520 
-  00B0 00B1 00B2 00B3 C220 00B5 00B6 00B7 
-  CB20 00D1 00EB 00BB 00BC 00BD 00BE 00BF 
-  C141 C241 C341 C441 C841 CA41 00E1 CB43 
-  C145 C245 C345 C845 C149 C249 C349 C849 
-  FFFD C44E C14F C24F C34F C44F C84F 00B4 
-  00E9 C155 C255 C355 C855 C259 00EC 00FB 
-  C161 C261 C361 C461 C861 CA61 00F1 CB63 
-  C165 C265 C365 C865 C169 C269 C369 C869 
-  00F3 C46E C16F C26F C36F C46F C86F 00B8 
-  00F9 C175 C275 C375 C875 C279 00FC C879 
-  C541 C561 C641 C661 CE41 CE61 C243 C263 
-  C343 C363 C743 C763 CF43 CF63 CF44 CF64 
-  00E2 00F2 C545 C565 FFFD FFFD C745 C765 
-  CE45 CE65 CF45 CF65 C347 C367 C647 C667 
-  C747 C767 CB47 FFFD C348 C368 00E4 00F4 
-  C449 C469 C549 C569 FFFD FFFD CE49 CE69 
-  C749 00F5 00E6 00F6 C34A C36A CB4B CB6B 
-  00F0 C24C C26C CB4C CB6C CF4C CF6C 00E7 
-  00F7 00E8 00F8 C24E C26E FFFD CB6E CF4E 
-  CF6E 00EF 00EE 00FE C54F C56F FFFD FFFD 
-  CD4F CD6F 00EA 00FA C252 C272 CB52 CB72 
-  CF52 CF72 C253 C273 C353 C373 CB53 CB73 
-  CF53 CF73 CB54 CB74 CF54 CF74 00ED 00FD 
-  C455 C475 C555 C575 C655 C675 CA55 CA75 
-  CD55 CD75 CE55 CE75 C357 C377 C359 C379 
-  C859 FFFD C27A C75A C77A CF5A CF7A 
-End of Item 0006 
-
-Begin of Item 0007
- Format 2
-  srcBegin = 01F5
-  destBegin = C267
-End of Item 0007 
-
-Begin of Item 0008
- Format 2
-  srcBegin = 02C7
-  destBegin = CF20
-End of Item 0008 
-
-Begin of Item 0009
- Format 1
-  srcBegin = 02D8
-  srcEnd = 02DD
-  mappingOffset = 00E4
- Mapping  = 
-  C620 C720 CA20 CE20 FFFD CD20 
-End of Item 0009 
-
-Begin of Item 000A
- Format 1
-  srcBegin = 2015
-  srcEnd = 201D
-  mappingOffset = 00EA
- Mapping  = 
-  00D0 FFFD FFFD 00A9 00B9 FFFD FFFD 00AA 
-  00BA 
-End of Item 000A 
-
-Begin of Item 000B
- Format 1
-  srcBegin = 2122
-  srcEnd = 2126
-  mappingOffset = 00F3
- Mapping  = 
-  00D4 FFFD FFFD FFFD 00E0 
-End of Item 000B 
-
-Begin of Item 000C
- Format 1
-  srcBegin = 215B
-  srcEnd = 215E
-  mappingOffset = 00F8
- Mapping  = 
-  00DC 00DD 00DE 00DF 
-End of Item 000C 
-
-Begin of Item 000D
- Format 1
-  srcBegin = 2190
-  srcEnd = 2193
-  mappingOffset = 00FC
- Mapping  = 
-  00AC 00AD 00AE 00AF 
-End of Item 000D 
-
-Begin of Item 000E
- Format 2
-  srcBegin = 266A
-  destBegin = 00D5
-End of Item 000E 
-
-========================================================*/
-/* Offset=0x0000  ItemOfList */
-  0x000F,
-/*-------------------------------------------------------*/
-/* Offset=0x0001  offsetToFormatArray */
-  0x0004,
-/*-------------------------------------------------------*/
-/* Offset=0x0002  offsetToMapCellArray */ 
-  0x0008,
-/*-------------------------------------------------------*/
-/* Offset=0x0003  offsetToMappingTable */ 
-  0x0035,
-/*-------------------------------------------------------*/
-/*       Offset=0x0004   Start of Format Array */ 
-/*	Total of Format 0 : 0x0003			 */
-/*	Total of Format 1 : 0x0008			 */
-/*	Total of Format 2 : 0x0004			 */
-/*	Total of Format 3 : 0x0000			 */
-
-0x2000, 0x2111, 0x1112, 0x0211, 
-/*-------------------------------------------------------*/
-/*       Offset=0x0008   Start of MapCell Array */ 
-/* 0000 */    0x0000, 0x0023, 0x0000, 
-/* 0001 */    0x0025, 0x005D, 0x0025, 
-/* 0002 */    0x0061, 0x007D, 0x0061, 
-/* 0003 */    0x0024, 0x0000, 0x00A4, 
-/* 0004 */    0x005E, 0x0060, 0x0000, 
-/* 0005 */    0x007E, 0x007F, 0x0003, 
-/* 0006 */    0x00A0, 0x017E, 0x0005, 
-/* 0007 */    0x01F5, 0x0000, 0xC267, 
-/* 0008 */    0x02C7, 0x0000, 0xCF20, 
-/* 0009 */    0x02D8, 0x02DD, 0x00E4, 
-/* 000A */    0x2015, 0x201D, 0x00EA, 
-/* 000B */    0x2122, 0x2126, 0x00F3, 
-/* 000C */    0x215B, 0x215E, 0x00F8, 
-/* 000D */    0x2190, 0x2193, 0x00FC, 
-/* 000E */    0x266A, 0x0000, 0x00D5, 
-/*-------------------------------------------------------*/
-/*       Offset=0x0035   Start of MappingTable */ 
-
-/* 0000 */    0xC320, 0x005F, 0xC120, 0xC420, 0x007F, 0x00A0, 0x00A1, 0x00A2, 
-/* 0008 */    0x00A3, 0xFFFD, 0x00A5, 0x00D7, 0x00A7, 0xC820, 0x00D3, 0x00E3, 
-/* 0010 */    0x00AB, 0x00D6, 0x00FF, 0x00D2, 0xC520, 0x00B0, 0x00B1, 0x00B2, 
-/* 0018 */    0x00B3, 0xC220, 0x00B5, 0x00B6, 0x00B7, 0xCB20, 0x00D1, 0x00EB, 
-/* 0020 */    0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0xC141, 0xC241, 0xC341, 
-/* 0028 */    0xC441, 0xC841, 0xCA41, 0x00E1, 0xCB43, 0xC145, 0xC245, 0xC345, 
-/* 0030 */    0xC845, 0xC149, 0xC249, 0xC349, 0xC849, 0xFFFD, 0xC44E, 0xC14F, 
-/* 0038 */    0xC24F, 0xC34F, 0xC44F, 0xC84F, 0x00B4, 0x00E9, 0xC155, 0xC255, 
-/* 0040 */    0xC355, 0xC855, 0xC259, 0x00EC, 0x00FB, 0xC161, 0xC261, 0xC361, 
-/* 0048 */    0xC461, 0xC861, 0xCA61, 0x00F1, 0xCB63, 0xC165, 0xC265, 0xC365, 
-/* 0050 */    0xC865, 0xC169, 0xC269, 0xC369, 0xC869, 0x00F3, 0xC46E, 0xC16F, 
-/* 0058 */    0xC26F, 0xC36F, 0xC46F, 0xC86F, 0x00B8, 0x00F9, 0xC175, 0xC275, 
-/* 0060 */    0xC375, 0xC875, 0xC279, 0x00FC, 0xC879, 0xC541, 0xC561, 0xC641, 
-/* 0068 */    0xC661, 0xCE41, 0xCE61, 0xC243, 0xC263, 0xC343, 0xC363, 0xC743, 
-/* 0070 */    0xC763, 0xCF43, 0xCF63, 0xCF44, 0xCF64, 0x00E2, 0x00F2, 0xC545, 
-/* 0078 */    0xC565, 0xFFFD, 0xFFFD, 0xC745, 0xC765, 0xCE45, 0xCE65, 0xCF45, 
-/* 0080 */    0xCF65, 0xC347, 0xC367, 0xC647, 0xC667, 0xC747, 0xC767, 0xCB47, 
-/* 0088 */    0xFFFD, 0xC348, 0xC368, 0x00E4, 0x00F4, 0xC449, 0xC469, 0xC549, 
-/* 0090 */    0xC569, 0xFFFD, 0xFFFD, 0xCE49, 0xCE69, 0xC749, 0x00F5, 0x00E6, 
-/* 0098 */    0x00F6, 0xC34A, 0xC36A, 0xCB4B, 0xCB6B, 0x00F0, 0xC24C, 0xC26C, 
-/* 00A0 */    0xCB4C, 0xCB6C, 0xCF4C, 0xCF6C, 0x00E7, 0x00F7, 0x00E8, 0x00F8, 
-/* 00A8 */    0xC24E, 0xC26E, 0xFFFD, 0xCB6E, 0xCF4E, 0xCF6E, 0x00EF, 0x00EE, 
-/* 00B0 */    0x00FE, 0xC54F, 0xC56F, 0xFFFD, 0xFFFD, 0xCD4F, 0xCD6F, 0x00EA, 
-/* 00B8 */    0x00FA, 0xC252, 0xC272, 0xCB52, 0xCB72, 0xCF52, 0xCF72, 0xC253, 
-/* 00C0 */    0xC273, 0xC353, 0xC373, 0xCB53, 0xCB73, 0xCF53, 0xCF73, 0xCB54, 
-/* 00C8 */    0xCB74, 0xCF54, 0xCF74, 0x00ED, 0x00FD, 0xC455, 0xC475, 0xC555, 
-/* 00D0 */    0xC575, 0xC655, 0xC675, 0xCA55, 0xCA75, 0xCD55, 0xCD75, 0xCE55, 
-/* 00D8 */    0xCE75, 0xC357, 0xC377, 0xC359, 0xC379, 0xC859, 0xFFFD, 0xC27A, 
-/* 00E0 */    0xC75A, 0xC77A, 0xCF5A, 0xCF7A, 0xC620, 0xC720, 0xCA20, 0xCE20, 
-/* 00E8 */    0xFFFD, 0xCD20, 0x00D0, 0xFFFD, 0xFFFD, 0x00A9, 0x00B9, 0xFFFD, 
-/* 00F0 */    0xFFFD, 0x00AA, 0x00BA, 0x00D4, 0xFFFD, 0xFFFD, 0xFFFD, 0x00E0, 
-/* 00F8 */    0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 
-/*	End of table Total Length = 0x0135 * 2 */
deleted file mode 100644
--- a/intl/uconv/ucvlatin/t61.ut
+++ /dev/null
@@ -1,544 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/*========================================================
-  This is a Generated file. Please don't edit it.
-
-  The tool which used to generate this file is called fromu.
-  If you have any problem of this file. Please contact 
-  Netscape Client International Team or 
-  ftang@netscape <Frank Tang> 
-
-              Table in Debug form 
-Begin of Item 0000
- Format 0
-  srcBegin = 0000
-  srcEnd = 007F
-  destBegin = 0000
-End of Item 0000 
-
-Begin of Item 0001
- Format 1
-  srcBegin = 00A0
-  srcEnd = 00BF
-  mappingOffset = 0000
- Mapping  = 
-  00A0 00A1 00A2 00A3 0024 00A5 FFFD 00A7 
-  FFFD 2018 201C 00AB 2190 2191 2192 2193 
-  00B0 00B1 00B2 00B3 00D7 00B5 00B6 00B7 
-  00F7 2019 201D 00BB 00BC 00BD 00BE 00BF 
-End of Item 0001 
-
-Begin of Item 0002
- Format 1
-  srcBegin = 00D0
-  srcEnd = 00FF
-  mappingOffset = 0020
- Mapping  = 
-  2015 00B9 00AE 00A9 2122 266A 00AC 00A6 
-  FFFD FFFD FFFD FFFD 215B 215C 215D 215E 
-  2126 00C6 0110 00AA 0126 FFFD 0132 013F 
-  0141 00D8 0152 00BA 00DE 0166 014A 0149 
-  0138 00E6 0111 00F0 0127 0131 0133 0140 
-  0142 00F8 0153 00DF 00FE 0167 014B 00AD 
-End of Item 0002 
-
-Begin of Item 0003
- Format 2
-  srcBegin = C120
-  destBegin = 0060
-End of Item 0003 
-
-Begin of Item 0004
- Format 1
-  srcBegin = C141
-  srcEnd = C155
-  mappingOffset = 0050
- Mapping  = 
-  00C0 FFFD FFFD FFFD 00C8 FFFD FFFD FFFD 
-  00CC FFFD FFFD FFFD FFFD FFFD 00D2 FFFD 
-  FFFD FFFD FFFD FFFD 00D9 
-End of Item 0004 
-
-Begin of Item 0005
- Format 1
-  srcBegin = C161
-  srcEnd = C175
-  mappingOffset = 0065
- Mapping  = 
-  00E0 FFFD FFFD FFFD 00E8 FFFD FFFD FFFD 
-  00EC FFFD FFFD FFFD FFFD FFFD 00F2 FFFD 
-  FFFD FFFD FFFD FFFD 00F9 
-End of Item 0005 
-
-Begin of Item 0006
- Format 2
-  srcBegin = C220
-  destBegin = 00B4
-End of Item 0006 
-
-Begin of Item 0007
- Format 1
-  srcBegin = C241
-  srcEnd = C27A
-  mappingOffset = 007A
- Mapping  = 
-  00C1 FFFD 0106 FFFD 00C9 FFFD FFFD FFFD 
-  00CD FFFD FFFD 0139 FFFD 0143 00D3 FFFD 
-  FFFD 0154 015A FFFD 00DA FFFD FFFD FFFD 
-  00DD 017B FFFD FFFD FFFD FFFD FFFD FFFD 
-  00E1 FFFD 0107 FFFD 00E9 FFFD 01F5 FFFD 
-  00ED FFFD FFFD 013A FFFD 0144 00F3 FFFD 
-  FFFD 0155 015B FFFD 00FA FFFD FFFD FFFD 
-  00FD 017A 
-End of Item 0007 
-
-Begin of Item 0008
- Format 2
-  srcBegin = C320
-  destBegin = 005E
-End of Item 0008 
-
-Begin of Item 0009
- Format 1
-  srcBegin = C341
-  srcEnd = C379
-  mappingOffset = 00B4
- Mapping  = 
-  00C2 FFFD 0108 FFFD 00CA FFFD 011C 0124 
-  00CE 0134 FFFD FFFD FFFD FFFD 00D4 FFFD 
-  FFFD FFFD 015C FFFD 00DB FFFD 0174 FFFD 
-  0176 FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  00E2 FFFD 0109 FFFD 00EA FFFD 011D 0125 
-  00EE 0135 FFFD FFFD FFFD FFFD 00F4 FFFD 
-  FFFD FFFD 015D FFFD 00FB FFFD 0175 FFFD 
-  0177 
-End of Item 0009 
-
-Begin of Item 000A
- Format 2
-  srcBegin = C420
-  destBegin = 007E
-End of Item 000A 
-
-Begin of Item 000B
- Format 1
-  srcBegin = C441
-  srcEnd = C455
-  mappingOffset = 00ED
- Mapping  = 
-  00C3 FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  0128 FFFD FFFD FFFD FFFD 00D1 00D5 FFFD 
-  FFFD FFFD FFFD FFFD 0168 
-End of Item 000B 
-
-Begin of Item 000C
- Format 1
-  srcBegin = C461
-  srcEnd = C475
-  mappingOffset = 0102
- Mapping  = 
-  00E3 FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  0129 FFFD FFFD FFFD FFFD 00F1 00F5 FFFD 
-  FFFD FFFD FFFD FFFD 0169 
-End of Item 000C 
-
-Begin of Item 000D
- Format 2
-  srcBegin = C520
-  destBegin = 00AF
-End of Item 000D 
-
-Begin of Item 000E
- Format 1
-  srcBegin = C541
-  srcEnd = C555
-  mappingOffset = 0117
- Mapping  = 
-  0100 FFFD FFFD FFFD 0112 FFFD FFFD FFFD 
-  012A FFFD FFFD FFFD FFFD FFFD 014C FFFD 
-  FFFD FFFD FFFD FFFD 016A 
-End of Item 000E 
-
-Begin of Item 000F
- Format 1
-  srcBegin = C561
-  srcEnd = C575
-  mappingOffset = 012C
- Mapping  = 
-  0101 FFFD FFFD FFFD 0113 FFFD FFFD FFFD 
-  012B FFFD FFFD FFFD FFFD FFFD 014D FFFD 
-  FFFD FFFD FFFD FFFD 016B 
-End of Item 000F 
-
-Begin of Item 0010
- Format 2
-  srcBegin = C620
-  destBegin = 02D8
-End of Item 0010 
-
-Begin of Item 0011
- Format 1
-  srcBegin = C641
-  srcEnd = C647
-  mappingOffset = 0141
- Mapping  = 
-  0102 FFFD FFFD FFFD FFFD FFFD 011E 
-End of Item 0011 
-
-Begin of Item 0012
- Format 2
-  srcBegin = C655
-  destBegin = 016C
-End of Item 0012 
-
-Begin of Item 0013
- Format 1
-  srcBegin = C661
-  srcEnd = C667
-  mappingOffset = 0148
- Mapping  = 
-  0103 FFFD FFFD FFFD FFFD FFFD 011F 
-End of Item 0013 
-
-Begin of Item 0014
- Format 2
-  srcBegin = C675
-  destBegin = 016D
-End of Item 0014 
-
-Begin of Item 0015
- Format 2
-  srcBegin = C720
-  destBegin = 02D9
-End of Item 0015 
-
-Begin of Item 0016
- Format 1
-  srcBegin = C743
-  srcEnd = C749
-  mappingOffset = 014F
- Mapping  = 
-  010A FFFD 0116 FFFD 0120 FFFD 0130 
-End of Item 0016 
-
-Begin of Item 0017
- Format 1
-  srcBegin = C75A
-  srcEnd = C767
-  mappingOffset = 0156
- Mapping  = 
-  017B FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  FFFD 010B FFFD 0117 FFFD 0121 
-End of Item 0017 
-
-Begin of Item 0018
- Format 2
-  srcBegin = C77A
-  destBegin = 017C
-End of Item 0018 
-
-Begin of Item 0019
- Format 2
-  srcBegin = C820
-  destBegin = 00A8
-End of Item 0019 
-
-Begin of Item 001A
- Format 1
-  srcBegin = C841
-  srcEnd = C879
-  mappingOffset = 0164
- Mapping  = 
-  00C4 FFFD FFFD FFFD 00CB FFFD FFFD FFFD 
-  00CF FFFD FFFD FFFD FFFD FFFD 00D6 FFFD 
-  FFFD FFFD FFFD FFFD 00DC FFFD FFFD FFFD 
-  0178 FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  00E4 FFFD FFFD FFFD 00EB FFFD FFFD FFFD 
-  00EF FFFD FFFD FFFD FFFD FFFD 00F6 FFFD 
-  FFFD FFFD FFFD FFFD 00FC FFFD FFFD FFFD 
-  00FF 
-End of Item 001A 
-
-Begin of Item 001B
- Format 2
-  srcBegin = CA20
-  destBegin = 02DA
-End of Item 001B 
-
-Begin of Item 001C
- Format 2
-  srcBegin = CA41
-  destBegin = 00C5
-End of Item 001C 
-
-Begin of Item 001D
- Format 2
-  srcBegin = CA55
-  destBegin = 016E
-End of Item 001D 
-
-Begin of Item 001E
- Format 2
-  srcBegin = CA61
-  destBegin = 00E5
-End of Item 001E 
-
-Begin of Item 001F
- Format 2
-  srcBegin = CA75
-  destBegin = 016F
-End of Item 001F 
-
-Begin of Item 0020
- Format 2
-  srcBegin = CB20
-  destBegin = 00B8
-End of Item 0020 
-
-Begin of Item 0021
- Format 1
-  srcBegin = CB43
-  srcEnd = CB54
-  mappingOffset = 019D
- Mapping  = 
-  00C7 FFFD FFFD FFFD 0122 FFFD FFFD FFFD 
-  0136 013B FFFD FFFD FFFD FFFD FFFD 0156 
-  015E 0162 
-End of Item 0021 
-
-Begin of Item 0022
- Format 1
-  srcBegin = CB63
-  srcEnd = CB74
-  mappingOffset = 01AF
- Mapping  = 
-  00E7 FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  0137 013C FFFD 0146 FFFD FFFD FFFD 0157 
-  015F 0163 
-End of Item 0022 
-
-Begin of Item 0023
- Format 2
-  srcBegin = CD20
-  destBegin = 02DD
-End of Item 0023 
-
-Begin of Item 0024
- Format 1
-  srcBegin = CD4F
-  srcEnd = CD55
-  mappingOffset = 01C1
- Mapping  = 
-  0150 FFFD FFFD FFFD FFFD FFFD 0170 
-End of Item 0024 
-
-Begin of Item 0025
- Format 1
-  srcBegin = CD6F
-  srcEnd = CD75
-  mappingOffset = 01C8
- Mapping  = 
-  0151 FFFD FFFD FFFD FFFD FFFD 0171 
-End of Item 0025 
-
-Begin of Item 0026
- Format 2
-  srcBegin = CE20
-  destBegin = 02DB
-End of Item 0026 
-
-Begin of Item 0027
- Format 1
-  srcBegin = CE41
-  srcEnd = CE49
-  mappingOffset = 01CF
- Mapping  = 
-  0104 FFFD FFFD FFFD 0118 FFFD FFFD FFFD 
-  012E 
-End of Item 0027 
-
-Begin of Item 0028
- Format 2
-  srcBegin = CE55
-  destBegin = 0172
-End of Item 0028 
-
-Begin of Item 0029
- Format 1
-  srcBegin = CE61
-  srcEnd = CE69
-  mappingOffset = 01D8
- Mapping  = 
-  0105 FFFD FFFD FFFD 0119 FFFD FFFD FFFD 
-  012F 
-End of Item 0029 
-
-Begin of Item 002A
- Format 2
-  srcBegin = CE75
-  destBegin = 0173
-End of Item 002A 
-
-Begin of Item 002B
- Format 2
-  srcBegin = CF20
-  destBegin = 02C7
-End of Item 002B 
-
-Begin of Item 002C
- Format 1
-  srcBegin = CF43
-  srcEnd = CF7A
-  mappingOffset = 01E1
- Mapping  = 
-  010C 010E 011A FFFD FFFD FFFD FFFD FFFD 
-  FFFD 013D FFFD 0147 FFFD FFFD FFFD 0158 
-  0160 0164 FFFD FFFD FFFD FFFD FFFD 017D 
-  FFFD FFFD FFFD FFFD FFFD FFFD FFFD FFFD 
-  010D 010F 011B FFFD FFFD FFFD FFFD FFFD 
-  FFFD 013E FFFD 0148 FFFD FFFD FFFD 0159 
-  0161 0165 FFFD FFFD FFFD FFFD FFFD 017E 
-End of Item 002C 
-
-========================================================*/
-/* Offset=0x0000  ItemOfList */
-  0x002D,
-/*-------------------------------------------------------*/
-/* Offset=0x0001  offsetToFormatArray */
-  0x0004,
-/*-------------------------------------------------------*/
-/* Offset=0x0002  offsetToMapCellArray */ 
-  0x0010,
-/*-------------------------------------------------------*/
-/* Offset=0x0003  offsetToMappingTable */ 
-  0x0097,
-/*-------------------------------------------------------*/
-/*       Offset=0x0004   Start of Format Array */ 
-/*	Total of Format 0 : 0x0001			 */
-/*	Total of Format 1 : 0x0016			 */
-/*	Total of Format 2 : 0x0016			 */
-/*	Total of Format 3 : 0x0000			 */
-
-0x2110, 0x1211, 0x1212, 0x1121, 0x1212, 0x1122, 0x2122, 0x2222, 
-0x2112, 0x1211, 0x2212, 0x0001, 
-/*-------------------------------------------------------*/
-/*       Offset=0x0010   Start of MapCell Array */ 
-/* 0000 */    0x0000, 0x007F, 0x0000, 
-/* 0001 */    0x00A0, 0x00BF, 0x0000, 
-/* 0002 */    0x00D0, 0x00FF, 0x0020, 
-/* 0003 */    0xC120, 0x0000, 0x0060, 
-/* 0004 */    0xC141, 0xC155, 0x0050, 
-/* 0005 */    0xC161, 0xC175, 0x0065, 
-/* 0006 */    0xC220, 0x0000, 0x00B4, 
-/* 0007 */    0xC241, 0xC27A, 0x007A, 
-/* 0008 */    0xC320, 0x0000, 0x005E, 
-/* 0009 */    0xC341, 0xC379, 0x00B4, 
-/* 000A */    0xC420, 0x0000, 0x007E, 
-/* 000B */    0xC441, 0xC455, 0x00ED, 
-/* 000C */    0xC461, 0xC475, 0x0102, 
-/* 000D */    0xC520, 0x0000, 0x00AF, 
-/* 000E */    0xC541, 0xC555, 0x0117, 
-/* 000F */    0xC561, 0xC575, 0x012C, 
-/* 0010 */    0xC620, 0x0000, 0x02D8, 
-/* 0011 */    0xC641, 0xC647, 0x0141, 
-/* 0012 */    0xC655, 0x0000, 0x016C, 
-/* 0013 */    0xC661, 0xC667, 0x0148, 
-/* 0014 */    0xC675, 0x0000, 0x016D, 
-/* 0015 */    0xC720, 0x0000, 0x02D9, 
-/* 0016 */    0xC743, 0xC749, 0x014F, 
-/* 0017 */    0xC75A, 0xC767, 0x0156, 
-/* 0018 */    0xC77A, 0x0000, 0x017C, 
-/* 0019 */    0xC820, 0x0000, 0x00A8, 
-/* 001A */    0xC841, 0xC879, 0x0164, 
-/* 001B */    0xCA20, 0x0000, 0x02DA, 
-/* 001C */    0xCA41, 0x0000, 0x00C5, 
-/* 001D */    0xCA55, 0x0000, 0x016E, 
-/* 001E */    0xCA61, 0x0000, 0x00E5, 
-/* 001F */    0xCA75, 0x0000, 0x016F, 
-/* 0020 */    0xCB20, 0x0000, 0x00B8, 
-/* 0021 */    0xCB43, 0xCB54, 0x019D, 
-/* 0022 */    0xCB63, 0xCB74, 0x01AF, 
-/* 0023 */    0xCD20, 0x0000, 0x02DD, 
-/* 0024 */    0xCD4F, 0xCD55, 0x01C1, 
-/* 0025 */    0xCD6F, 0xCD75, 0x01C8, 
-/* 0026 */    0xCE20, 0x0000, 0x02DB, 
-/* 0027 */    0xCE41, 0xCE49, 0x01CF, 
-/* 0028 */    0xCE55, 0x0000, 0x0172, 
-/* 0029 */    0xCE61, 0xCE69, 0x01D8, 
-/* 002A */    0xCE75, 0x0000, 0x0173, 
-/* 002B */    0xCF20, 0x0000, 0x02C7, 
-/* 002C */    0xCF43, 0xCF7A, 0x01E1, 
-/*-------------------------------------------------------*/
-/*       Offset=0x0097   Start of MappingTable */ 
-
-/* 0000 */    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x0024, 0x00A5, 0xFFFD, 0x00A7, 
-/* 0008 */    0xFFFD, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193, 
-/* 0010 */    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 
-/* 0018 */    0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 
-/* 0020 */    0x2015, 0x00B9, 0x00AE, 0x00A9, 0x2122, 0x266A, 0x00AC, 0x00A6, 
-/* 0028 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x215B, 0x215C, 0x215D, 0x215E, 
-/* 0030 */    0x2126, 0x00C6, 0x0110, 0x00AA, 0x0126, 0xFFFD, 0x0132, 0x013F, 
-/* 0038 */    0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149, 
-/* 0040 */    0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 
-/* 0048 */    0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x00AD, 
-/* 0050 */    0x00C0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00C8, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0058 */    0x00CC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00D2, 0xFFFD, 
-/* 0060 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00D9, 0x00E0, 0xFFFD, 0xFFFD, 
-/* 0068 */    0xFFFD, 0x00E8, 0xFFFD, 0xFFFD, 0xFFFD, 0x00EC, 0xFFFD, 0xFFFD, 
-/* 0070 */    0xFFFD, 0xFFFD, 0xFFFD, 0x00F2, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0078 */    0xFFFD, 0x00F9, 0x00C1, 0xFFFD, 0x0106, 0xFFFD, 0x00C9, 0xFFFD, 
-/* 0080 */    0xFFFD, 0xFFFD, 0x00CD, 0xFFFD, 0xFFFD, 0x0139, 0xFFFD, 0x0143, 
-/* 0088 */    0x00D3, 0xFFFD, 0xFFFD, 0x0154, 0x015A, 0xFFFD, 0x00DA, 0xFFFD, 
-/* 0090 */    0xFFFD, 0xFFFD, 0x00DD, 0x017B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0098 */    0xFFFD, 0xFFFD, 0x00E1, 0xFFFD, 0x0107, 0xFFFD, 0x00E9, 0xFFFD, 
-/* 00A0 */    0x01F5, 0xFFFD, 0x00ED, 0xFFFD, 0xFFFD, 0x013A, 0xFFFD, 0x0144, 
-/* 00A8 */    0x00F3, 0xFFFD, 0xFFFD, 0x0155, 0x015B, 0xFFFD, 0x00FA, 0xFFFD, 
-/* 00B0 */    0xFFFD, 0xFFFD, 0x00FD, 0x017A, 0x00C2, 0xFFFD, 0x0108, 0xFFFD, 
-/* 00B8 */    0x00CA, 0xFFFD, 0x011C, 0x0124, 0x00CE, 0x0134, 0xFFFD, 0xFFFD, 
-/* 00C0 */    0xFFFD, 0xFFFD, 0x00D4, 0xFFFD, 0xFFFD, 0xFFFD, 0x015C, 0xFFFD, 
-/* 00C8 */    0x00DB, 0xFFFD, 0x0174, 0xFFFD, 0x0176, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 00D0 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00E2, 0xFFFD, 0x0109, 0xFFFD, 
-/* 00D8 */    0x00EA, 0xFFFD, 0x011D, 0x0125, 0x00EE, 0x0135, 0xFFFD, 0xFFFD, 
-/* 00E0 */    0xFFFD, 0xFFFD, 0x00F4, 0xFFFD, 0xFFFD, 0xFFFD, 0x015D, 0xFFFD, 
-/* 00E8 */    0x00FB, 0xFFFD, 0x0175, 0xFFFD, 0x0177, 0x00C3, 0xFFFD, 0xFFFD, 
-/* 00F0 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0128, 0xFFFD, 0xFFFD, 
-/* 00F8 */    0xFFFD, 0xFFFD, 0x00D1, 0x00D5, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0100 */    0xFFFD, 0x0168, 0x00E3, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0108 */    0xFFFD, 0xFFFD, 0x0129, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00F1, 
-/* 0110 */    0x00F5, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0169, 0x0100, 
-/* 0118 */    0xFFFD, 0xFFFD, 0xFFFD, 0x0112, 0xFFFD, 0xFFFD, 0xFFFD, 0x012A, 
-/* 0120 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x014C, 0xFFFD, 0xFFFD, 
-/* 0128 */    0xFFFD, 0xFFFD, 0xFFFD, 0x016A, 0x0101, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0130 */    0x0113, 0xFFFD, 0xFFFD, 0xFFFD, 0x012B, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0138 */    0xFFFD, 0xFFFD, 0x014D, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0140 */    0x016B, 0x0102, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x011E, 
-/* 0148 */    0x0103, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x011F, 0x010A, 
-/* 0150 */    0xFFFD, 0x0116, 0xFFFD, 0x0120, 0xFFFD, 0x0130, 0x017B, 0xFFFD, 
-/* 0158 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x010B, 
-/* 0160 */    0xFFFD, 0x0117, 0xFFFD, 0x0121, 0x00C4, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0168 */    0x00CB, 0xFFFD, 0xFFFD, 0xFFFD, 0x00CF, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0170 */    0xFFFD, 0xFFFD, 0x00D6, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0178 */    0x00DC, 0xFFFD, 0xFFFD, 0xFFFD, 0x0178, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0180 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00E4, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0188 */    0x00EB, 0xFFFD, 0xFFFD, 0xFFFD, 0x00EF, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0190 */    0xFFFD, 0xFFFD, 0x00F6, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0198 */    0x00FC, 0xFFFD, 0xFFFD, 0xFFFD, 0x00FF, 0x00C7, 0xFFFD, 0xFFFD, 
-/* 01A0 */    0xFFFD, 0x0122, 0xFFFD, 0xFFFD, 0xFFFD, 0x0136, 0x013B, 0xFFFD, 
-/* 01A8 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0156, 0x015E, 0x0162, 0x00E7, 
-/* 01B0 */    0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0137, 
-/* 01B8 */    0x013C, 0xFFFD, 0x0146, 0xFFFD, 0xFFFD, 0xFFFD, 0x0157, 0x015F, 
-/* 01C0 */    0x0163, 0x0150, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0170, 
-/* 01C8 */    0x0151, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0171, 0x0104, 
-/* 01D0 */    0xFFFD, 0xFFFD, 0xFFFD, 0x0118, 0xFFFD, 0xFFFD, 0xFFFD, 0x012E, 
-/* 01D8 */    0x0105, 0xFFFD, 0xFFFD, 0xFFFD, 0x0119, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 01E0 */    0x012F, 0x010C, 0x010E, 0x011A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 01E8 */    0xFFFD, 0xFFFD, 0x013D, 0xFFFD, 0x0147, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 01F0 */    0x0158, 0x0160, 0x0164, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 01F8 */    0x017D, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0200 */    0xFFFD, 0x010D, 0x010F, 0x011B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0208 */    0xFFFD, 0xFFFD, 0x013E, 0xFFFD, 0x0148, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0210 */    0x0159, 0x0161, 0x0165, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 
-/* 0218 */    0x017E, 
-/*	End of table Total Length = 0x02B0 * 2 */
--- a/intl/uconv/util/nsUCConstructors.cpp
+++ b/intl/uconv/util/nsUCConstructors.cpp
@@ -114,37 +114,16 @@ CreateTableEncoder(uScanClassID aScanCla
                    void** aResult)
 {
     return CreateTableEncoder(aScanClass, nullptr,
                               aMappingTable, aMaxLengthFactor,
                               aOuter, aIID, aResult);
 }
 
 NS_METHOD
-CreateTableDecoder(uScanClassID aScanClass,
-                   uShiftInTable * aShiftInTable,
-                   uMappingTable  * aMappingTable,
-                   uint32_t aMaxLengthFactor,
-                   nsISupports* aOuter,
-                   REFNSIID aIID,
-                   void** aResult)
-{
-  if (aOuter)
-    return NS_ERROR_NO_AGGREGATION;
-  
-  nsTableDecoderSupport* decoder =
-      new nsTableDecoderSupport(aScanClass, aShiftInTable, aMappingTable,
-                                aMaxLengthFactor);
-  if (!decoder)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  return StabilizedQueryInterface(decoder, aIID, aResult);
-}
-
-NS_METHOD
 CreateOneByteDecoder(uMappingTable * aMappingTable,
                      
                      nsISupports* aOuter,
                      REFNSIID aIID,
                      void** aResult)
 {
     if (aOuter) return NS_ERROR_NO_AGGREGATION;
     
--- a/intl/uconv/util/nsUCConstructors.h
+++ b/intl/uconv/util/nsUCConstructors.h
@@ -57,24 +57,15 @@ NS_METHOD
 CreateTableEncoder(uScanClassID aScanClass,
                    uMappingTable  * aMappingTable,
                    uint32_t aMaxLengthFactor,
                    nsISupports* aOuter,
                    REFNSIID aIID,
                    void** aResult);
 
 NS_METHOD
-CreateTableDecoder(uScanClassID aScanClass,
-                   uShiftInTable * aShiftInTable,
-                   uMappingTable * aMappingTable,
-                   uint32_t aMaxLengthFactor,
-                   nsISupports* aOuter,
-                   REFNSIID aIID,
-                   void** aResult);
-
-NS_METHOD
 CreateOneByteDecoder(uMappingTable * aMappingTable,
                      nsISupports* aOuter,
                      REFNSIID aIID,
                      void** aResult);
 
                    
 #endif
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -379,16 +379,17 @@ selfhosting_srcs := \
   $(srcdir)/builtin/TypedObject.js \
   $(NULL)
 
 selfhosted_out_h_deps := \
   $(selfhosting_srcs) \
   $(srcdir)/js.msg \
   $(srcdir)/builtin/embedjs.py \
   $(srcdir)/builtin/TypedObjectConstants.h \
+  $(srcdir)/builtin/SelfHostingDefines.h \
   $(NULL)
 
 SELFHOSTED_DEFINES += $(DEFINES) $(ACDEFINES)
 ifdef MOZ_DEBUG
 SELFHOSTED_DEFINES += $(MOZ_DEBUG_ENABLE_DEFS)
 else
 SELFHOSTED_DEFINES += $(MOZ_DEBUG_DISABLE_DEFS)
 endif
--- a/js/src/devtools/rootAnalysis/loadCallgraph.js
+++ b/js/src/devtools/rootAnalysis/loadCallgraph.js
@@ -155,17 +155,17 @@ function loadCallgraph(file)
         if (name in suppressedFunctions)
             delete gcFunctions[name];
     }
 
     for (var name in suppressedFieldCalls) {
         suppressedFunctions[name] = true;
     }
 
-    for (var gcName of [ 'jsgc.cpp:void Collect(JSRuntime*, uint8, int64, uint32, uint32)',
+    for (var gcName of [ 'void js::gc::GCRuntime::collect(uint8, int64, uint32, uint32)',
                          'void js::MinorGC(JSRuntime*, uint32)' ])
     {
         assert(gcName in mangledName);
         addGCFunction(mangledName[gcName], "GC");
     }
 
     // Initialize the worklist to all known gcFunctions.
     var worklist = [];
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -38,16 +38,17 @@ struct ScriptAndCounts
 
 typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
 
 namespace gc {
 
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 class MarkingValidator;
+class AutoPrepareForTracing;
 
 struct ConservativeGCData
 {
     /*
      * The GC scans conservatively between ThreadData::nativeStackBase and
      * nativeStackTop unless the latter is nullptr.
      */
     uintptr_t           *nativeStackTop;
@@ -83,18 +84,87 @@ struct ConservativeGCData
         return !!nativeStackTop;
     }
 };
 
 class GCRuntime
 {
   public:
     GCRuntime(JSRuntime *rt);
+    bool init(uint32_t maxbytes);
+    void finish();
+
+    void setGCZeal(uint8_t zeal, uint32_t frequency);
+    template <typename T> bool addRoot(T *rp, const char *name, JSGCRootType rootType);
+    void removeRoot(void *rp);
+    void setMarkStackLimit(size_t limit);
+
+    bool isHeapBusy() { return heapState != js::Idle; }
+    bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; }
+    bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; }
+    bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); }
+
+    bool triggerGC(JS::gcreason::Reason reason);
+    bool triggerZoneGC(Zone *zone, JS::gcreason::Reason reason);
+    void maybeGC(Zone *zone);
+    void minorGC(JS::gcreason::Reason reason);
+    void minorGC(JSContext *cx, JS::gcreason::Reason reason);
+    void gcIfNeeded(JSContext *cx);
+    void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
+                 JS::gcreason::Reason reason);
+    void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis);
+    void runDebugGC();
+
+  private:
+    // For ArenaLists::allocateFromArenaInline()
+    friend class ArenaLists;
+    Chunk *pickChunk(Zone *zone);
+
+    inline bool wantBackgroundAllocation() const;
+
+    bool initGCZeal();
+    void recordNativeStackTopForGC();
+    void requestInterrupt(JS::gcreason::Reason reason);
+    bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
+                 JS::gcreason::Reason reason);
+    void budgetIncrementalGC(int64_t *budget);
+    void resetIncrementalGC(const char *reason);
+    void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason,
+                                 JSGCInvocationKind gckind);
+    void pushZealSelectedObjects();
+    bool beginMarkPhase();
+    bool shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime);
+    bool drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase);
+    template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
+    void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
+    template <class ZoneIterT, class CompartmentIterT> void markGrayReferences();
+    void markGrayReferencesInCurrentGroup();
+    void beginSweepPhase(bool lastGC);
+    void findZoneGroups();
+    void getNextZoneGroup();
+    void endMarkingZoneGroup();
+    void beginSweepingZoneGroup();
+    bool releaseObservedTypes();
+    void endSweepingZoneGroup();
+    bool sweepPhase(SliceBudget &sliceBudget);
+    void endSweepPhase(JSGCInvocationKind gckind, bool lastGC);
+    void sweepZones(FreeOp *fop, bool lastGC);
+
+    void computeNonIncrementalMarkingForValidation();
+    void validateIncrementalMarking();
+    void finishMarkingValidation();
+
+#ifdef DEBUG
+    void checkForCompartmentMismatches();
+    void markAllWeakReferences(gcstats::Phase phase);
+    void markAllGrayReferences();
+#endif
 
   public:  // Internal state, public for now
+    JSRuntime             *rt;
 
     /* Embedders can use this zone however they wish. */
     JS::Zone              *systemZone;
 
     /* List of compartments and zones (protected by the GC lock). */
     js::gc::ZoneVector    zones;
 
     js::gc::SystemPageAllocator pageAllocator;
@@ -223,17 +293,17 @@ class GCRuntime
     /* Index of current zone group (for stats). */
     unsigned              zoneGroupIndex;
 
     /*
      * Incremental sweep state.
      */
     JS::Zone              *zoneGroups;
     JS::Zone              *currentZoneGroup;
-    int                   sweepPhase;
+    int                   finalizePhase;
     JS::Zone              *sweepZone;
     int                   sweepKindIndex;
     bool                  abortSweepAfterCurrentGroup;
 
     /*
      * List head of arenas allocated during the sweep phase.
      */
     js::gc::ArenaHeader   *arenasAllocatedDuringSweep;
@@ -321,21 +391,21 @@ class GCRuntime
     int                   incrementalLimit;
 
     js::Vector<JSObject *, 0, js::SystemAllocPolicy>   selectedForMarking;
 #endif
 
     bool                  validate;
     bool                  fullCompartmentChecks;
 
-    JSGCCallback          callback;
+    JSGCCallback          gcCallback;
     JS::GCSliceCallback   sliceCallback;
     JSFinalizeCallback    finalizeCallback;
 
-    void                  *callbackData;
+    void                  *gcCallbackData;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from   maxMallocBytes down to zero.
      */
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire>   mallocBytes;
 
     /*
@@ -372,19 +442,21 @@ class GCRuntime
 #ifdef DEBUG
     size_t                noGCOrAllocationCheck;
 #endif
 
     /* Synchronize GC heap access between main thread and GCHelperThread. */
     PRLock   *lock;
     mozilla::DebugOnly<PRThread *>   lockOwner;
 
-    friend class js::GCHelperThread;
+    js::GCHelperThread helperThread;
+
+    ConservativeGCData conservativeGC;
 
-    js::GCHelperThread    helperThread;
-
-    ConservativeGCData    conservativeGC;
+    friend class js::GCHelperThread;
+    friend class js::gc::AutoPrepareForTracing; /* For recordNativeStackTopForGC(). */
+    friend class js::gc::MarkingValidator;
 };
 
 } /* namespace gc */
 } /* namespace js */
 
 #endif
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -29,16 +29,17 @@ struct Zone;
 namespace js {
 
 class ObjectElements;
 class HeapSlot;
 void SetGCZeal(JSRuntime *, uint8_t, uint32_t);
 
 namespace gc {
 class Cell;
+class Collector;
 class MinorCollectionTracer;
 } /* namespace gc */
 
 namespace types {
 struct TypeObject;
 }
 
 namespace jit {
@@ -145,16 +146,37 @@ class Nursery
         return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
     }
 
     MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
         JS_ASSERT(runtime_);
         return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
     }
 
+#ifdef JS_GC_ZEAL
+    /*
+     * In debug and zeal builds, these bytes indicate the state of an unused
+     * segment of nursery-allocated memory.
+     */
+    void enterZealMode() {
+        if (isEnabled())
+            numActiveChunks_ = NumNurseryChunks;
+    }
+    void leaveZealMode() {
+        if (isEnabled()) {
+            JS_ASSERT(isEmpty());
+            setCurrentChunk(0);
+            currentStart_ = start();
+        }
+    }
+#else
+    void enterZealMode() {}
+    void leaveZealMode() {}
+#endif
+
   private:
     /*
      * The start and end pointers are stored under the runtime so that we can
      * inline the isInsideNursery check into embedder code. Use the start()
      * and heapEnd() functions to access these values.
      */
     JSRuntime *runtime_;
 
@@ -284,38 +306,16 @@ class Nursery
     void sweep(JSRuntime *rt);
 
     /* Change the allocable space provided by the nursery. */
     void growAllocableSpace();
     void shrinkAllocableSpace();
 
     static void MinorGCCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind);
 
-#ifdef JS_GC_ZEAL
-    /*
-     * In debug and zeal builds, these bytes indicate the state of an unused
-     * segment of nursery-allocated memory.
-     */
-    void enterZealMode() {
-        if (isEnabled())
-            numActiveChunks_ = NumNurseryChunks;
-    }
-    void leaveZealMode() {
-        if (isEnabled()) {
-            JS_ASSERT(isEmpty());
-            setCurrentChunk(0);
-            currentStart_ = start();
-        }
-    }
-#else
-    void enterZealMode() {}
-    void leaveZealMode() {}
-#endif
-
     friend class gc::MinorCollectionTracer;
     friend class jit::MacroAssembler;
-    friend void SetGCZeal(JSRuntime *, uint8_t, uint32_t);
 };
 
 } /* namespace js */
 
 #endif /* JSGC_GENERATIONAL */
 #endif /* gc_Nursery_h */
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -97,16 +97,17 @@ namespace JS {
  */
 
 struct Zone : public JS::shadow::Zone,
               public js::gc::GraphNodeBase<JS::Zone>,
               public js::MallocProvider<JS::Zone>
 {
   private:
     friend bool js::CurrentThreadCanAccessZone(Zone *zone);
+    friend class js::gc::GCRuntime;
 
   public:
     js::Allocator                allocator;
 
     js::CompartmentVector        compartments;
 
   private:
     bool                         ionUsingBarriers_;
@@ -316,16 +317,17 @@ struct Zone : public JS::shadow::Zone,
     js::types::TypeZone types;
 
     void sweep(js::FreeOp *fop, bool releaseTypes, bool *oom);
 
     bool hasMarkedCompartments();
 
   private:
     void sweepBreakpoints(js::FreeOp *fop);
+    void sweepCompartments(js::FreeOp *fop, bool keepAtleastOne, bool lastGC);
 
 #ifdef JS_ION
     js::jit::JitZone *jitZone_;
     js::jit::JitZone *createJitZone(JSContext *cx);
 
   public:
     js::jit::JitZone *getJitZone(JSContext *cx) {
         return jitZone_ ? jitZone_ : createJitZone(cx);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6332,19 +6332,16 @@ IonBuilder::pushDOMTypeBarrier(MInstruct
     // unbox as a double.  Instead, go ahead and barrier on having an int type,
     // since we know we need a barrier anyway due to the type mismatch.  This is
     // the only situation in which TI actually has more information about the
     // JSValueType than codegen can, short of jitinfo->returnType just being
     // JSVAL_TYPE_UNKNOWN.
     MDefinition* replace = ins;
     if (jitinfo->returnType() != JSVAL_TYPE_DOUBLE ||
         observed->getKnownMIRType() != MIRType_Int32) {
-        JS_ASSERT(jitinfo->returnType() == JSVAL_TYPE_UNKNOWN ||
-                  observed->getKnownMIRType() == MIRType_Value ||
-                  MIRTypeFromValueType(jitinfo->returnType()) == observed->getKnownMIRType());
         replace = ensureDefiniteType(ins, MIRTypeFromValueType(jitinfo->returnType()));
         if (replace != ins) {
             current->pop();
             current->push(replace);
         }
     } else {
         JS_ASSERT(barrier);
     }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1902,18 +1902,18 @@ JS_MaybeGC(JSContext *cx)
 {
     MaybeGC(cx);
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCCallback(JSRuntime *rt, JSGCCallback cb, void *data)
 {
     AssertHeapIsIdle(rt);
-    rt->gc.callback = cb;
-    rt->gc.callbackData = data;
+    rt->gc.gcCallback = cb;
+    rt->gc.gcCallbackData = data;
 }
 
 JS_PUBLIC_API(void)
 JS_SetFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb)
 {
     AssertHeapIsIdle(rt);
     rt->gc.finalizeCallback = cb;
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2250,17 +2250,17 @@ class AutoIdArray : private AutoGCRooter
       : AutoGCRooter(cx, IDARRAY), context(cx), idArray(ida)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
     ~AutoIdArray() {
         if (idArray)
             JS_DestroyIdArray(context, idArray);
     }
-    bool operator!() {
+    bool operator!() const {
         return !idArray;
     }
     jsid operator[](size_t i) const {
         JS_ASSERT(idArray);
         return JS_IdArrayGet(context, idArray, i);
     }
     size_t length() const {
         return JS_IdArrayLength(context, idArray);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -616,53 +616,29 @@ AllocChunk(JSRuntime *rt)
 }
 
 static inline void
 FreeChunk(JSRuntime *rt, Chunk *p)
 {
     rt->gc.pageAllocator.unmapPages(static_cast<void *>(p), ChunkSize);
 }
 
-inline bool
-ChunkPool::wantBackgroundAllocation(JSRuntime *rt) const
-{
-    /*
-     * To minimize memory waste we do not want to run the background chunk
-     * allocation if we have empty chunks or when the runtime needs just few
-     * of them.
-     */
-    return rt->gc.helperThread.canBackgroundAllocate() &&
-           emptyCount == 0 &&
-           rt->gc.chunkSet.count() >= 4;
-}
-
 /* Must be called with the GC lock taken. */
 inline Chunk *
 ChunkPool::get(JSRuntime *rt)
 {
-    JS_ASSERT(this == &rt->gc.chunkPool);
-
     Chunk *chunk = emptyChunkListHead;
-    if (chunk) {
-        JS_ASSERT(emptyCount);
-        emptyChunkListHead = chunk->info.next;
-        --emptyCount;
-    } else {
+    if (!chunk) {
         JS_ASSERT(!emptyCount);
-        chunk = Chunk::allocate(rt);
-        if (!chunk)
-            return nullptr;
-        JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
+        return nullptr;
     }
-    JS_ASSERT(chunk->unused());
-    JS_ASSERT(!rt->gc.chunkSet.has(chunk));
-
-    if (wantBackgroundAllocation(rt))
-        rt->gc.helperThread.startBackgroundAllocationIfIdle();
-
+
+    JS_ASSERT(emptyCount);
+    emptyChunkListHead = chunk->info.next;
+    --emptyCount;
     return chunk;
 }
 
 /* Must be called either during the GC or with the GC lock taken. */
 inline void
 ChunkPool::put(Chunk *chunk)
 {
     chunk->info.age = 0;
@@ -971,51 +947,74 @@ Chunk::releaseArena(ArenaHeader *aheader
         rt->gc.chunkSet.remove(this);
         removeFromAvailableList();
         JS_ASSERT(info.numArenasFree == ArenasPerChunk);
         decommitAllArenas(rt);
         rt->gc.chunkPool.put(this);
     }
 }
 
+inline bool
+GCRuntime::wantBackgroundAllocation() const
+{
+    /*
+     * To minimize memory waste we do not want to run the background chunk
+     * allocation if we have empty chunks or when the runtime needs just few
+     * of them.
+     */
+    return helperThread.canBackgroundAllocate() &&
+           chunkPool.getEmptyCount() == 0 &&
+           chunkSet.count() >= 4;
+}
+
 /* The caller must hold the GC lock. */
-static Chunk *
-PickChunk(Zone *zone)
-{
-    JSRuntime *rt = zone->runtimeFromAnyThread();
+Chunk *
+GCRuntime::pickChunk(Zone *zone)
+{
     Chunk **listHeadp = GetAvailableChunkList(zone);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
-    chunk = rt->gc.chunkPool.get(rt);
-    if (!chunk)
-        return nullptr;
-
-    rt->gc.chunkAllocationSinceLastGC = true;
+    chunk = chunkPool.get(rt);
+    if (!chunk) {
+        chunk = Chunk::allocate(rt);
+        if (!chunk)
+            return nullptr;
+        JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
+    }
+
+    JS_ASSERT(chunk->unused());
+    JS_ASSERT(!chunkSet.has(chunk));
+
+    if (wantBackgroundAllocation())
+        helperThread.startBackgroundAllocationIfIdle();
+
+    chunkAllocationSinceLastGC = true;
 
     /*
      * FIXME bug 583732 - chunk is newly allocated and cannot be present in
      * the table so using ordinary lookupForAdd is suboptimal here.
      */
-    GCChunkSet::AddPtr p = rt->gc.chunkSet.lookupForAdd(chunk);
+    GCChunkSet::AddPtr p = chunkSet.lookupForAdd(chunk);
     JS_ASSERT(!p);
-    if (!rt->gc.chunkSet.add(p, chunk)) {
+    if (!chunkSet.add(p, chunk)) {
         Chunk::release(rt, chunk);
         return nullptr;
     }
 
     chunk->info.prevp = nullptr;
     chunk->info.next = nullptr;
     chunk->addToAvailableList(zone);
 
     return chunk;
 }
 
-js::gc::GCRuntime::GCRuntime(JSRuntime *rt) :
+GCRuntime::GCRuntime(JSRuntime *rt) :
+    rt(rt),
     systemZone(nullptr),
     systemAvailableChunkListHead(nullptr),
     userAvailableChunkListHead(nullptr),
     bytes(0),
     maxBytes(0),
     maxMallocBytes(0),
     numArenasFreeCommitted(0),
     marker(rt),
@@ -1051,17 +1050,16 @@ js::gc::GCRuntime::GCRuntime(JSRuntime *
     incrementalState(gc::NO_INCREMENTAL),
     lastMarkSlice(false),
     sweepOnBackgroundThread(false),
     foundBlackGrayEdges(false),
     sweepingZones(nullptr),
     zoneGroupIndex(0),
     zoneGroups(nullptr),
     currentZoneGroup(nullptr),
-    sweepPhase(0),
     sweepZone(nullptr),
     sweepKindIndex(0),
     abortSweepAfterCurrentGroup(false),
     arenasAllocatedDuringSweep(nullptr),
 #ifdef DEBUG
     markingValidator(nullptr),
 #endif
     interFrameGC(0),
@@ -1080,17 +1078,17 @@ js::gc::GCRuntime::GCRuntime(JSRuntime *
     zealMode(0),
     zealFrequency(0),
     nextScheduled(0),
     deterministicOnly(false),
     incrementalLimit(0),
 #endif
     validate(true),
     fullCompartmentChecks(false),
-    callback(nullptr),
+    gcCallback(nullptr),
     sliceCallback(nullptr),
     finalizeCallback(nullptr),
     mallocBytes(0),
     mallocGCTriggered(false),
     scriptAndCountsVector(nullptr),
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
@@ -1101,39 +1099,45 @@ js::gc::GCRuntime::GCRuntime(JSRuntime *
 {
 }
 
 #ifdef JS_GC_ZEAL
 
 extern void
 js::SetGCZeal(JSRuntime *rt, uint8_t zeal, uint32_t frequency)
 {
-    if (rt->gc.verifyPreData)
+    rt->gc.setGCZeal(zeal, frequency);
+}
+
+void
+GCRuntime::setGCZeal(uint8_t zeal, uint32_t frequency)
+{
+    if (verifyPreData)
         VerifyBarriers(rt, PreBarrierVerifier);
-    if (rt->gc.verifyPostData)
+    if (verifyPostData)
         VerifyBarriers(rt, PostBarrierVerifier);
 
 #ifdef JSGC_GENERATIONAL
-    if (rt->gc.zealMode == ZealGenerationalGCValue) {
-        MinorGC(rt, JS::gcreason::DEBUG_GC);
-        rt->gc.nursery.leaveZealMode();
+    if (zealMode == ZealGenerationalGCValue) {
+        minorGC(JS::gcreason::DEBUG_GC);
+        nursery.leaveZealMode();
     }
 
     if (zeal == ZealGenerationalGCValue)
-        rt->gc.nursery.enterZealMode();
+        nursery.enterZealMode();
 #endif
 
     bool schedule = zeal >= js::gc::ZealAllocValue;
-    rt->gc.zealMode = zeal;
-    rt->gc.zealFrequency = frequency;
-    rt->gc.nextScheduled = schedule ? frequency : 0;
-}
-
-static bool
-InitGCZeal(JSRuntime *rt)
+    zealMode = zeal;
+    zealFrequency = frequency;
+    nextScheduled = schedule ? frequency : 0;
+}
+
+bool
+GCRuntime::initGCZeal()
 {
     const char *env = getenv("JS_GC_ZEAL");
     if (!env)
         return true;
 
     int zeal = -1;
     int frequency = JS_DEFAULT_ZEAL_FREQ;
     if (strcmp(env, "help") != 0) {
@@ -1159,148 +1163,170 @@ InitGCZeal(JSRuntime *rt)
                 "  9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
                 " 10: Incremental GC in multiple slices\n"
                 " 11: Verify post write barriers between instructions\n"
                 " 12: Verify post write barriers between paints\n"
                 " 13: Purge analysis state every F allocations (default: 100)\n");
         return false;
     }
 
-    SetGCZeal(rt, zeal, frequency);
+    setGCZeal(zeal, frequency);
     return true;
 }
 
 #endif
 
 /* Lifetime for type sets attached to scripts containing observed types. */
 static const int64_t JIT_SCRIPT_RELEASE_TYPES_INTERVAL = 60 * 1000 * 1000;
 
 bool
-js_InitGC(JSRuntime *rt, uint32_t maxbytes)
-{
-    if (!rt->gc.chunkSet.init(INITIAL_CHUNK_CAPACITY))
+GCRuntime::init(uint32_t maxbytes)
+{
+    if (!chunkSet.init(INITIAL_CHUNK_CAPACITY))
         return false;
 
-    if (!rt->gc.rootsHash.init(256))
+    if (!rootsHash.init(256))
         return false;
 
-    if (!rt->gc.helperThread.init())
+    if (!helperThread.init())
         return false;
 
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
-    rt->gc.maxBytes = maxbytes;
+    maxBytes = maxbytes;
     rt->setGCMaxMallocBytes(maxbytes);
 
 #ifndef JS_MORE_DETERMINISTIC
-    rt->gc.jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
+    jitReleaseTime = PRMJ_Now() + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
 #endif
 
 #ifdef JSGC_GENERATIONAL
-    if (!rt->gc.nursery.init())
+    if (!nursery.init())
         return false;
 
-    if (!rt->gc.storeBuffer.enable())
+    if (!storeBuffer.enable())
         return false;
 #endif
 
 #ifdef JS_GC_ZEAL
-    if (!InitGCZeal(rt))
+    if (!initGCZeal())
         return false;
 #endif
 
+    if (!marker.init(mode))
+        return false;
+
     return true;
 }
 
-static void
-RecordNativeStackTopForGC(JSRuntime *rt)
-{
-    ConservativeGCData *cgcd = &rt->gc.conservativeGC;
-
+void
+GCRuntime::recordNativeStackTopForGC()
+{
 #ifdef JS_THREADSAFE
     /* Record the stack top here only if we are called from a request. */
     if (!rt->requestDepth)
         return;
 #endif
-    cgcd->recordStackTop();
+    conservativeGC.recordStackTop();
 }
 
 void
-js_FinishGC(JSRuntime *rt)
+GCRuntime::finish()
 {
     /*
      * Wait until the background finalization stops and the helper thread
      * shuts down before we forcefully release any remaining GC memory.
      */
-    rt->gc.helperThread.finish();
+    helperThread.finish();
 
 #ifdef JS_GC_ZEAL
     /* Free memory associated with GC verification. */
     FinishVerifier(rt);
 #endif
 
     /* Delete all remaining zones. */
     if (rt->gcInitialized) {
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
                 js_delete(comp.get());
             js_delete(zone.get());
         }
     }
 
-    rt->gc.zones.clear();
-
-    rt->gc.systemAvailableChunkListHead = nullptr;
-    rt->gc.userAvailableChunkListHead = nullptr;
-    if (rt->gc.chunkSet.initialized()) {
-        for (GCChunkSet::Range r(rt->gc.chunkSet.all()); !r.empty(); r.popFront())
+    zones.clear();
+
+    systemAvailableChunkListHead = nullptr;
+    userAvailableChunkListHead = nullptr;
+    if (chunkSet.initialized()) {
+        for (GCChunkSet::Range r(chunkSet.all()); !r.empty(); r.popFront())
             Chunk::release(rt, r.front());
-        rt->gc.chunkSet.clear();
+        chunkSet.clear();
     }
 
-    rt->gc.chunkPool.expireAndFree(rt, true);
-
-    if (rt->gc.rootsHash.initialized())
-        rt->gc.rootsHash.clear();
-
+    chunkPool.expireAndFree(rt, true);
+
+    if (rootsHash.initialized())
+        rootsHash.clear();
+
+    FinishPersistentRootedChains(rt);
+}
+
+void
+js::gc::FinishPersistentRootedChains(JSRuntime *rt)
+{
+    /* The lists of persistent roots are stored on the shadow runtime. */
     rt->functionPersistentRooteds.clear();
     rt->idPersistentRooteds.clear();
     rt->objectPersistentRooteds.clear();
     rt->scriptPersistentRooteds.clear();
     rt->stringPersistentRooteds.clear();
     rt->valuePersistentRooteds.clear();
 }
 
 template <typename T> struct BarrierOwner {};
 template <typename T> struct BarrierOwner<T *> { typedef T result; };
 template <> struct BarrierOwner<Value> { typedef HeapValue result; };
 
 template <typename T>
-static bool
-AddRoot(JSRuntime *rt, T *rp, const char *name, JSGCRootType rootType)
+bool
+GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType)
 {
     /*
      * Sometimes Firefox will hold weak references to objects and then convert
      * them to strong references by calling AddRoot (e.g., via PreserveWrapper,
      * or ModifyBusyCount in workers). We need a read barrier to cover these
      * cases.
      */
     if (rt->gc.incrementalState != NO_INCREMENTAL)
         BarrierOwner<T>::result::writeBarrierPre(*rp);
 
     return rt->gc.rootsHash.put((void *)rp, RootInfo(name, rootType));
 }
 
+void
+GCRuntime::removeRoot(void *rp)
+{
+    rootsHash.remove(rp);
+    poke = true;
+}
+
+template <typename T>
+static bool
+AddRoot(JSRuntime *rt, T *rp, const char *name, JSGCRootType rootType)
+{
+    return rt->gc.addRoot(rp, name, rootType);
+}
+
 template <typename T>
 static bool
 AddRoot(JSContext *cx, T *rp, const char *name, JSGCRootType rootType)
 {
-    bool ok = AddRoot(cx->runtime(), rp, name, rootType);
+    bool ok = cx->runtime()->gc.addRoot(rp, name, rootType);
     if (!ok)
         JS_ReportOutOfMemory(cx);
     return ok;
 }
 
 bool
 js::AddValueRoot(JSContext *cx, Value *vp, const char *name)
 {
@@ -1347,18 +1373,17 @@ extern JS_FRIEND_API(void)
 js::RemoveRawValueRoot(JSContext *cx, Value *vp)
 {
     RemoveRoot(cx->runtime(), vp);
 }
 
 void
 js::RemoveRoot(JSRuntime *rt, void *rp)
 {
-    rt->gc.rootsHash.remove(rp);
-    rt->gc.poke = true;
+    rt->gc.removeRoot(rp);
 }
 
 typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
 static size_t
 ComputeTriggerBytes(Zone *zone, size_t lastBytes, size_t maxBytes, JSGCInvocationKind gckind)
@@ -1468,37 +1493,38 @@ inline void *
 ArenaLists::allocateFromArenaInline(Zone *zone, AllocKind thingKind)
 {
     /*
      * Parallel JS Note:
      *
      * This function can be called from parallel threads all of which
      * are associated with the same compartment. In that case, each
      * thread will have a distinct ArenaLists.  Therefore, whenever we
-     * fall through to PickChunk() we must be sure that we are holding
+     * fall through to pickChunk() we must be sure that we are holding
      * a lock.
      */
 
     Chunk *chunk = nullptr;
 
     ArenaList *al = &arenaLists[thingKind];
     AutoLockGC maybeLock;
 
 #ifdef JS_THREADSAFE
-    volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
+    ArenaLists::BackgroundFinalizeState *bfs = &backgroundFinalizeState[thingKind];
     if (*bfs != BFS_DONE) {
         /*
          * We cannot search the arena list for free things while background
          * finalization runs and can modify it at any moment. So we always
          * allocate a new arena in that case.
          */
-        maybeLock.lock(zone->runtimeFromAnyThread());
+        JSRuntime *rt = zone->runtimeFromAnyThread();
+        maybeLock.lock(rt);
         if (*bfs == BFS_RUN) {
             JS_ASSERT(al->isCursorAtEnd());
-            chunk = PickChunk(zone);
+            chunk = rt->gc.pickChunk(zone);
             if (!chunk) {
                 /*
                  * Let the caller to wait for the background allocation to
                  * finish and restart the allocation attempt.
                  */
                 return nullptr;
             }
         } else if (*bfs == BFS_JUST_FINISHED) {
@@ -1538,19 +1564,20 @@ ArenaLists::allocateFromArenaInline(Zone
                 }
             }
             void *thing = freeLists[thingKind].allocate(Arena::thingSize(thingKind));
             JS_ASSERT(thing);   // This allocation is infallible.
             return thing;
         }
 
         /* Make sure we hold the GC lock before we call PickChunk. */
+        JSRuntime *rt = zone->runtimeFromAnyThread();
         if (!maybeLock.locked())
-            maybeLock.lock(zone->runtimeFromAnyThread());
-        chunk = PickChunk(zone);
+            maybeLock.lock(rt);
+        chunk = rt->gc.pickChunk(zone);
         if (!chunk)
             return nullptr;
     }
 
     /*
      * While we still hold the GC lock get an arena from some chunk, mark it
      * as full as its single free span is moved to the free lits, and insert
      * it to the list as a fully allocated arena.
@@ -1947,30 +1974,36 @@ SliceBudget::checkOverBudget()
 }
 
 void
 js::MarkCompartmentActive(InterpreterFrame *fp)
 {
     fp->script()->compartment()->zone()->active = true;
 }
 
-static void
-RequestInterrupt(JSRuntime *rt, JS::gcreason::Reason reason)
-{
-    if (rt->gc.isNeeded)
+void
+GCRuntime::requestInterrupt(JS::gcreason::Reason reason)
+{
+    if (isNeeded)
         return;
 
-    rt->gc.isNeeded = true;
-    rt->gc.triggerReason = reason;
+    isNeeded = true;
+    triggerReason = reason;
     rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
 }
 
 bool
 js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason)
 {
+    return rt->gc.triggerGC(reason);
+}
+
+bool
+GCRuntime::triggerGC(JS::gcreason::Reason reason)
+{
     /* Wait till end of parallel section to trigger GC. */
     if (InParallelSection()) {
         ForkJoinContext::current()->requestGC(reason);
         return true;
     }
 
     /* Don't trigger GCs when allocating under the interrupt callback lock. */
     if (rt->currentThreadOwnsInterruptLock())
@@ -1978,106 +2011,118 @@ js::TriggerGC(JSRuntime *rt, JS::gcreaso
 
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
     JS::PrepareForFullGC(rt);
-    RequestInterrupt(rt, reason);
+    requestInterrupt(reason);
     return true;
 }
 
 bool
 js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
 {
+    return zone->runtimeFromMainThread()->gc.triggerZoneGC(zone,reason);
+}
+
+bool
+GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
+{
     /*
      * If parallel threads are running, wait till they
      * are stopped to trigger GC.
      */
     if (InParallelSection()) {
         ForkJoinContext::current()->requestZoneGC(zone, reason);
         return true;
     }
 
     /* Zones in use by a thread with an exclusive context can't be collected. */
     if (zone->usedByExclusiveThread)
         return false;
 
-    JSRuntime *rt = zone->runtimeFromMainThread();
-
     /* Don't trigger GCs when allocating under the interrupt callback lock. */
     if (rt->currentThreadOwnsInterruptLock())
         return false;
 
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
-    if (rt->gcZeal() == ZealAllocValue) {
+#ifdef JS_GC_ZEAL
+    if (zealMode == ZealAllocValue) {
         TriggerGC(rt, reason);
         return true;
     }
+#endif
 
     if (rt->isAtomsZone(zone)) {
         /* We can't do a zone GC of the atoms compartment. */
         TriggerGC(rt, reason);
         return true;
     }
 
     PrepareZoneForGC(zone);
-    RequestInterrupt(rt, reason);
+    requestInterrupt(reason);
     return true;
 }
 
 void
 js::MaybeGC(JSContext *cx)
 {
-    JSRuntime *rt = cx->runtime();
+    cx->runtime()->gc.maybeGC(cx->zone());
+}
+
+void
+GCRuntime::maybeGC(Zone *zone)
+{
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
-    if (rt->gcZeal() == ZealAllocValue || rt->gcZeal() == ZealPokeValue) {
+#ifdef JS_GC_ZEAL
+    if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) {
         JS::PrepareForFullGC(rt);
         GC(rt, GC_NORMAL, JS::gcreason::MAYBEGC);
         return;
     }
-
-    if (rt->gc.isNeeded) {
+#endif
+
+    if (isNeeded) {
         GCSlice(rt, GC_NORMAL, JS::gcreason::MAYBEGC);
         return;
     }
 
-    double factor = rt->gc.highFrequencyGC ? 0.85 : 0.9;
-    Zone *zone = cx->zone();
+    double factor = highFrequencyGC ? 0.85 : 0.9;
     if (zone->gcBytes > 1024 * 1024 &&
         zone->gcBytes >= factor * zone->gcTriggerBytes &&
-        rt->gc.incrementalState == NO_INCREMENTAL &&
-        !rt->gc.helperThread.sweeping())
+        incrementalState == NO_INCREMENTAL &&
+        !helperThread.sweeping())
     {
         PrepareZoneForGC(zone);
         GCSlice(rt, GC_NORMAL, JS::gcreason::MAYBEGC);
         return;
     }
 
 #ifndef JS_MORE_DETERMINISTIC
     /*
      * Access to the counters and, on 32 bit, setting gcNextFullGCTime below
      * is not atomic and a race condition could trigger or suppress the GC. We
      * tolerate this.
      */
     int64_t now = PRMJ_Now();
-    if (rt->gc.nextFullGCTime && rt->gc.nextFullGCTime <= now) {
-        if (rt->gc.chunkAllocationSinceLastGC ||
-            rt->gc.numArenasFreeCommitted > rt->gc.decommitThreshold)
+    if (nextFullGCTime && nextFullGCTime <= now) {
+        if (chunkAllocationSinceLastGC ||
+            numArenasFreeCommitted > decommitThreshold)
         {
             JS::PrepareForFullGC(rt);
             GCSlice(rt, GC_SHRINK, JS::gcreason::MAYBEGC);
         } else {
-            rt->gc.nextFullGCTime = now + GC_IDLE_FULL_SPAN;
+            nextFullGCTime = now + GC_IDLE_FULL_SPAN;
         }
     }
 #endif
 }
 
 static void
 DecommitArenasFromAvailableList(JSRuntime *rt, Chunk **availableListHeadp)
 {
@@ -2389,17 +2434,17 @@ GCHelperThread::threadLoop()
                     chunk = Chunk::allocate(rt);
                 }
 
                 /* OOM stops the background allocation. */
                 if (!chunk)
                     break;
                 JS_ASSERT(chunk->info.numArenasFreeCommitted == 0);
                 rt->gc.chunkPool.put(chunk);
-            } while (state == ALLOCATING && rt->gc.chunkPool.wantBackgroundAllocation(rt));
+            } while (state == ALLOCATING && rt->gc.wantBackgroundAllocation());
             if (state == ALLOCATING)
                 state = IDLE;
             break;
           }
           case CANCEL_ALLOCATION:
             state = IDLE;
             PR_NotifyAllCondVar(done);
             break;
@@ -2570,49 +2615,54 @@ GCHelperThread::onBackgroundThread()
 {
 #ifdef JS_THREADSAFE
     return PR_GetCurrentThread() == getThread();
 #else
     return false;
 #endif
 }
 
-static bool
-ReleaseObservedTypes(JSRuntime *rt)
-{
-    bool releaseTypes = rt->gcZeal() != 0;
+bool
+GCRuntime::releaseObservedTypes()
+{
+    bool releaseTypes = false;
+
+#ifdef JS_GC_ZEAL
+    if (zealMode != 0)
+        releaseTypes = true;
+#endif
 
 #ifndef JS_MORE_DETERMINISTIC
     int64_t now = PRMJ_Now();
-    if (now >= rt->gc.jitReleaseTime)
+    if (now >= jitReleaseTime)
         releaseTypes = true;
     if (releaseTypes)
-        rt->gc.jitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
+        jitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
 #endif
 
     return releaseTypes;
 }
 
 /*
  * It's simpler if we preserve the invariant that every zone has at least one
  * compartment. If we know we're deleting the entire zone, then
  * SweepCompartments is allowed to delete all compartments. In this case,
  * |keepAtleastOne| is false. If some objects remain in the zone so that it
  * cannot be deleted, then we set |keepAtleastOne| to true, which prohibits
  * SweepCompartments from deleting every compartment. Instead, it preserves an
  * arbitrary compartment in the zone.
  */
-static void
-SweepCompartments(FreeOp *fop, Zone *zone, bool keepAtleastOne, bool lastGC)
-{
-    JSRuntime *rt = zone->runtimeFromMainThread();
+void
+Zone::sweepCompartments(FreeOp *fop, bool keepAtleastOne, bool lastGC)
+{
+    JSRuntime *rt = runtimeFromMainThread();
     JSDestroyCompartmentCallback callback = rt->destroyCompartmentCallback;
 
-    JSCompartment **read = zone->compartments.begin();
-    JSCompartment **end = zone->compartments.end();
+    JSCompartment **read = compartments.begin();
+    JSCompartment **end = compartments.end();
     JSCompartment **write = read;
     bool foundOne = false;
     while (read < end) {
         JSCompartment *comp = *read++;
         JS_ASSERT(!rt->isAtomsCompartment(comp));
 
         /*
          * Don't delete the last compartment if all the ones before it were
@@ -2625,53 +2675,52 @@ SweepCompartments(FreeOp *fop, Zone *zon
             if (comp->principals)
                 JS_DropPrincipals(rt, comp->principals);
             js_delete(comp);
         } else {
             *write++ = comp;
             foundOne = true;
         }
     }
-    zone->compartments.resize(write - zone->compartments.begin());
-    JS_ASSERT_IF(keepAtleastOne, !zone->compartments.empty());
-}
-
-static void
-SweepZones(FreeOp *fop, bool lastGC)
-{
-    JSRuntime *rt = fop->runtime();
+    compartments.resize(write - compartments.begin());
+    JS_ASSERT_IF(keepAtleastOne, !compartments.empty());
+}
+
+void
+GCRuntime::sweepZones(FreeOp *fop, bool lastGC)
+{
     JSZoneCallback callback = rt->destroyZoneCallback;
 
     /* Skip the atomsCompartment zone. */
-    Zone **read = rt->gc.zones.begin() + 1;
-    Zone **end = rt->gc.zones.end();
+    Zone **read = zones.begin() + 1;
+    Zone **end = zones.end();
     Zone **write = read;
-    JS_ASSERT(rt->gc.zones.length() >= 1);
-    JS_ASSERT(rt->isAtomsZone(rt->gc.zones[0]));
+    JS_ASSERT(zones.length() >= 1);
+    JS_ASSERT(rt->isAtomsZone(zones[0]));
 
     while (read < end) {
         Zone *zone = *read++;
 
         if (zone->wasGCStarted()) {
             if ((zone->allocator.arenas.arenaListsAreEmpty() && !zone->hasMarkedCompartments()) ||
                 lastGC)
             {
                 zone->allocator.arenas.checkEmptyFreeLists();
                 if (callback)
                     callback(zone);
-                SweepCompartments(fop, zone, false, lastGC);
+                zone->sweepCompartments(fop, false, lastGC);
                 JS_ASSERT(zone->compartments.empty());
                 fop->delete_(zone);
                 continue;
             }
-            SweepCompartments(fop, zone, true, lastGC);
+            zone->sweepCompartments(fop, true, lastGC);
         }
         *write++ = zone;
     }
-    rt->gc.zones.resize(write - rt->gc.zones.begin());
+    zones.resize(write - zones.begin());
 }
 
 static void
 PurgeRuntime(JSRuntime *rt)
 {
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->purge();
 
@@ -2684,24 +2733,23 @@ PurgeRuntime(JSRuntime *rt)
     rt->nativeIterCache.purge();
     rt->sourceDataCache.purge();
     rt->evalCache.clear();
 
     if (!rt->hasActiveCompilations())
         rt->parseMapPool().purgeAll();
 }
 
-static bool
-ShouldPreserveJITCode(JSCompartment *comp, int64_t currentTime)
-{
-    JSRuntime *rt = comp->runtimeFromMainThread();
-    if (rt->gc.shouldCleanUpEverything)
+bool
+GCRuntime::shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime)
+{
+    if (shouldCleanUpEverything)
         return false;
 
-    if (rt->gc.alwaysPreserveCode)
+    if (alwaysPreserveCode)
         return true;
     if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
         return true;
 
     return false;
 }
 
 #ifdef DEBUG
@@ -2778,20 +2826,20 @@ CheckCompartmentCallback(JSTracer *trcAr
     if (comp && trc->compartment) {
         CheckCompartment(trc, comp, thing, kind);
     } else {
         JS_ASSERT(thing->tenuredZone() == trc->zone ||
                   trc->runtime()->isAtomsZone(thing->tenuredZone()));
     }
 }
 
-static void
-CheckForCompartmentMismatches(JSRuntime *rt)
-{
-    if (rt->gc.disableStrictProxyCheckingCount)
+void
+GCRuntime::checkForCompartmentMismatches()
+{
+    if (disableStrictProxyCheckingCount)
         return;
 
     CompartmentCheckTracer trc(rt, CheckCompartmentCallback);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
         for (size_t thingKind = 0; thingKind < FINALIZE_LAST; thingKind++) {
             for (ZoneCellIterUnderGC i(zone, AllocKind(thingKind)); !i.done(); i.next()) {
                 trc.src = i.getCell();
@@ -2799,55 +2847,55 @@ CheckForCompartmentMismatches(JSRuntime 
                 trc.compartment = CompartmentOfCell(trc.src, trc.srcKind);
                 JS_TraceChildren(&trc, trc.src, trc.srcKind);
             }
         }
     }
 }
 #endif
 
-static bool
-BeginMarkPhase(JSRuntime *rt)
+bool
+GCRuntime::beginMarkPhase()
 {
     int64_t currentTime = PRMJ_Now();
 
 #ifdef DEBUG
-    if (rt->gc.fullCompartmentChecks)
-        CheckForCompartmentMismatches(rt);
+    if (fullCompartmentChecks)
+        checkForCompartmentMismatches();
 #endif
 
-    rt->gc.isFull = true;
+    isFull = true;
     bool any = false;
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         /* Assert that zone state is as we expect */
         JS_ASSERT(!zone->isCollecting());
         JS_ASSERT(!zone->compartments.empty());
         for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
             JS_ASSERT(!zone->allocator.arenas.arenaListsToSweep[i]);
 
         /* Set up which zones will be collected. */
         if (zone->isGCScheduled()) {
             if (!rt->isAtomsZone(zone)) {
                 any = true;
                 zone->setGCState(Zone::Mark);
             }
         } else {
-            rt->gc.isFull = false;
+            isFull = false;
         }
 
         zone->scheduledForDestruction = false;
         zone->maybeAlive = false;
         zone->setPreservingCode(false);
     }
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         JS_ASSERT(c->gcLiveArrayBuffers.empty());
         c->marked = false;
-        if (ShouldPreserveJITCode(c, currentTime))
+        if (shouldPreserveJITCode(c, currentTime))
             c->zone()->setPreservingCode(true);
     }
 
     if (!rt->gc.shouldCleanUpEverything) {
 #ifdef JS_ION
         if (JSCompartment *comp = jit::TopmostIonActivationCompartment(rt))
             comp->zone()->setPreservingCode(true);
 #endif
@@ -2858,17 +2906,17 @@ BeginMarkPhase(JSRuntime *rt)
      * zones that are not being collected, we are not allowed to collect
      * atoms. Otherwise, the non-collected zones could contain pointers
      * to atoms that we would miss.
      *
      * keepAtoms() will only change on the main thread, which we are currently
      * on. If the value of keepAtoms() changes between GC slices, then we'll
      * cancel the incremental GC. See IsIncrementalGCSafe.
      */
-    if (rt->gc.isFull && !rt->keepAtoms()) {
+    if (isFull && !rt->keepAtoms()) {
         Zone *atomsZone = rt->atomsCompartment()->zone();
         if (atomsZone->isGCScheduled()) {
             JS_ASSERT(!atomsZone->isCollecting());
             atomsZone->setGCState(Zone::Mark);
             any = true;
         }
     }
 
@@ -2878,72 +2926,72 @@ BeginMarkPhase(JSRuntime *rt)
 
     /*
      * At the end of each incremental slice, we call prepareForIncrementalGC,
      * which marks objects in all arenas that we're currently allocating
      * into. This can cause leaks if unreachable objects are in these
      * arenas. This purge call ensures that we only mark arenas that have had
      * allocations after the incremental GC started.
      */
-    if (rt->gc.isIncremental) {
+    if (isIncremental) {
         for (GCZonesIter zone(rt); !zone.done(); zone.next())
             zone->allocator.arenas.purge();
     }
 
-    rt->gc.marker.start();
-    JS_ASSERT(!rt->gc.marker.callback);
-    JS_ASSERT(IS_GC_MARKING_TRACER(&rt->gc.marker));
+    marker.start();
+    JS_ASSERT(!marker.callback);
+    JS_ASSERT(IS_GC_MARKING_TRACER(&marker));
 
     /* For non-incremental GC the following sweep discards the jit code. */
-    if (rt->gc.isIncremental) {
+    if (isIncremental) {
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-            gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_DISCARD_CODE);
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_DISCARD_CODE);
             zone->discardJitCode(rt->defaultFreeOp());
         }
     }
 
-    GCMarker *gcmarker = &rt->gc.marker;
-
-    rt->gc.startNumber = rt->gc.number;
+    GCMarker *gcmarker = &marker;
+
+    startNumber = number;
 
     /*
      * We must purge the runtime at the beginning of an incremental GC. The
      * danger if we purge later is that the snapshot invariant of incremental
      * GC will be broken, as follows. If some object is reachable only through
      * some cache (say the dtoaCache) then it will not be part of the snapshot.
      * If we purge after root marking, then the mutator could obtain a pointer
      * to the object and start using it. This object might never be marked, so
      * a GC hazard would exist.
      */
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_PURGE);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_PURGE);
         PurgeRuntime(rt);
     }
 
     /*
      * Mark phase.
      */
-    gcstats::AutoPhase ap1(rt->gc.stats, gcstats::PHASE_MARK);
-    gcstats::AutoPhase ap2(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
+    gcstats::AutoPhase ap1(stats, gcstats::PHASE_MARK);
+    gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS);
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         /* Unmark everything in the zones being collected. */
         zone->allocator.arenas.unmarkAll();
     }
 
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         /* Reset weak map list for the compartments being collected. */
         WeakMapBase::resetCompartmentWeakMapList(c);
     }
 
-    if (rt->gc.isFull)
+    if (isFull)
         UnmarkScriptData(rt);
 
     MarkRuntime(gcmarker);
-    if (rt->gc.isIncremental)
+    if (isIncremental)
         BufferGrayRoots(gcmarker);
 
     /*
      * This code ensures that if a zone is "dead", then it will be
      * collected in this GC. A zone is considered dead if its maybeAlive
      * flag is false. The maybeAlive flag is set if:
      *   (1) the zone has incoming cross-compartment edges, or
      *   (2) an object in the zone was marked during root marking, either
@@ -2983,121 +3031,118 @@ BeginMarkPhase(JSRuntime *rt)
      * For black roots, code in gc/Marking.cpp will already have set maybeAlive
      * during MarkRuntime.
      */
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         if (!zone->maybeAlive && !rt->isAtomsZone(zone))
             zone->scheduledForDestruction = true;
     }
-    rt->gc.foundBlackGrayEdges = false;
+    foundBlackGrayEdges = false;
 
     return true;
 }
 
 template <class CompartmentIterT>
-static void
-MarkWeakReferences(JSRuntime *rt, gcstats::Phase phase)
-{
-    GCMarker *gcmarker = &rt->gc.marker;
-    JS_ASSERT(gcmarker->isDrained());
-
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP_MARK);
-    gcstats::AutoPhase ap1(rt->gc.stats, phase);
+void
+GCRuntime::markWeakReferences(gcstats::Phase phase)
+{
+    JS_ASSERT(marker.isDrained());
+
+    gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_MARK);
+    gcstats::AutoPhase ap1(stats, phase);
 
     for (;;) {
         bool markedAny = false;
         for (CompartmentIterT c(rt); !c.done(); c.next()) {
-            markedAny |= WatchpointMap::markCompartmentIteratively(c, gcmarker);
-            markedAny |= WeakMapBase::markCompartmentIteratively(c, gcmarker);
+            markedAny |= WatchpointMap::markCompartmentIteratively(c, &marker);
+            markedAny |= WeakMapBase::markCompartmentIteratively(c, &marker);
         }
-        markedAny |= Debugger::markAllIteratively(gcmarker);
+        markedAny |= Debugger::markAllIteratively(&marker);
 
         if (!markedAny)
             break;
 
         SliceBudget budget;
-        gcmarker->drainMarkStack(budget);
+        marker.drainMarkStack(budget);
     }
-    JS_ASSERT(gcmarker->isDrained());
-}
-
-static void
-MarkWeakReferencesInCurrentGroup(JSRuntime *rt, gcstats::Phase phase)
-{
-    MarkWeakReferences<GCCompartmentGroupIter>(rt, phase);
+    JS_ASSERT(marker.isDrained());
+}
+
+void
+GCRuntime::markWeakReferencesInCurrentGroup(gcstats::Phase phase)
+{
+    markWeakReferences<GCCompartmentGroupIter>(phase);
 }
 
 template <class ZoneIterT, class CompartmentIterT>
-static void
-MarkGrayReferences(JSRuntime *rt)
-{
-    GCMarker *gcmarker = &rt->gc.marker;
-
+void
+GCRuntime::markGrayReferences()
+{
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP_MARK);
-        gcstats::AutoPhase ap1(rt->gc.stats, gcstats::PHASE_SWEEP_MARK_GRAY);
-        gcmarker->setMarkColorGray();
-        if (gcmarker->hasBufferedGrayRoots()) {
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_MARK);
+        gcstats::AutoPhase ap1(stats, gcstats::PHASE_SWEEP_MARK_GRAY);
+        marker.setMarkColorGray();
+        if (marker.hasBufferedGrayRoots()) {
             for (ZoneIterT zone(rt); !zone.done(); zone.next())
-                gcmarker->markBufferedGrayRoots(zone);
+                marker.markBufferedGrayRoots(zone);
         } else {
-            JS_ASSERT(!rt->gc.isIncremental);
-            if (JSTraceDataOp op = rt->gc.grayRootTracer.op)
-                (*op)(gcmarker, rt->gc.grayRootTracer.data);
+            JS_ASSERT(!isIncremental);
+            if (JSTraceDataOp op = grayRootTracer.op)
+                (*op)(&marker, grayRootTracer.data);
         }
         SliceBudget budget;
-        gcmarker->drainMarkStack(budget);
+        marker.drainMarkStack(budget);
     }
 
-    MarkWeakReferences<CompartmentIterT>(rt, gcstats::PHASE_SWEEP_MARK_GRAY_WEAK);
-
-    JS_ASSERT(gcmarker->isDrained());
-
-    gcmarker->setMarkColorBlack();
-}
-
-static void
-MarkGrayReferencesInCurrentGroup(JSRuntime *rt)
-{
-    MarkGrayReferences<GCZoneGroupIter, GCCompartmentGroupIter>(rt);
+    markWeakReferences<CompartmentIterT>(gcstats::PHASE_SWEEP_MARK_GRAY_WEAK);
+
+    JS_ASSERT(marker.isDrained());
+
+    marker.setMarkColorBlack();
+}
+
+void
+GCRuntime::markGrayReferencesInCurrentGroup()
+{
+    markGrayReferences<GCZoneGroupIter, GCCompartmentGroupIter>();
 }
 
 #ifdef DEBUG
 
-static void
-MarkAllWeakReferences(JSRuntime *rt, gcstats::Phase phase)
-{
-    MarkWeakReferences<GCCompartmentsIter>(rt, phase);
-}
-
-static void
-MarkAllGrayReferences(JSRuntime *rt)
-{
-    MarkGrayReferences<GCZonesIter, GCCompartmentsIter>(rt);
+void
+GCRuntime::markAllWeakReferences(gcstats::Phase phase)
+{
+    markWeakReferences<GCCompartmentsIter>(phase);
+}
+
+void
+GCRuntime::markAllGrayReferences()
+{
+    markGrayReferences<GCZonesIter, GCCompartmentsIter>();
 }
 
 class js::gc::MarkingValidator
 {
   public:
-    MarkingValidator(JSRuntime *rt);
+    MarkingValidator(GCRuntime *gc);
     ~MarkingValidator();
     void nonIncrementalMark();
     void validate();
 
   private:
-    JSRuntime *runtime;
+    GCRuntime *gc;
     bool initialized;
 
     typedef HashMap<Chunk *, ChunkBitmap *, GCChunkHasher, SystemAllocPolicy> BitmapMap;
     BitmapMap map;
 };
 
-js::gc::MarkingValidator::MarkingValidator(JSRuntime *rt)
-  : runtime(rt),
+js::gc::MarkingValidator::MarkingValidator(GCRuntime *gc)
+  : gc(gc),
     initialized(false)
 {}
 
 js::gc::MarkingValidator::~MarkingValidator()
 {
     if (!map.initialized())
         return;
 
@@ -3113,20 +3158,21 @@ js::gc::MarkingValidator::nonIncremental
      * the results for later comparison.
      *
      * Currently this does not validate gray marking.
      */
 
     if (!map.init())
         return;
 
-    GCMarker *gcmarker = &runtime->gc.marker;
+    JSRuntime *runtime = gc->rt;
+    GCMarker *gcmarker = &gc->marker;
 
     /* Save existing mark bits. */
-    for (GCChunkSet::Range r(runtime->gc.chunkSet.all()); !r.empty(); r.popFront()) {
+    for (GCChunkSet::Range r(gc->chunkSet.all()); !r.empty(); r.popFront()) {
         ChunkBitmap *bitmap = &r.front()->bitmap;
 	ChunkBitmap *entry = js_new<ChunkBitmap>();
         if (!entry)
             return;
 
         memcpy((void *)entry->bitmap, (void *)bitmap->bitmap, sizeof(bitmap->bitmap));
         if (!map.putNew(r.front(), entry))
             return;
@@ -3154,88 +3200,88 @@ js::gc::MarkingValidator::nonIncremental
     initialized = true;
 
     for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
         WeakMapBase::resetCompartmentWeakMapList(c);
         ArrayBufferObject::resetArrayBufferList(c);
     }
 
     /* Re-do all the marking, but non-incrementally. */
-    js::gc::State state = runtime->gc.incrementalState;
-    runtime->gc.incrementalState = MARK_ROOTS;
+    js::gc::State state = gc->incrementalState;
+    gc->incrementalState = MARK_ROOTS;
 
     JS_ASSERT(gcmarker->isDrained());
     gcmarker->reset();
 
-    for (GCChunkSet::Range r(runtime->gc.chunkSet.all()); !r.empty(); r.popFront())
+    for (GCChunkSet::Range r(gc->chunkSet.all()); !r.empty(); r.popFront())
         r.front()->bitmap.clear();
 
     {
-        gcstats::AutoPhase ap1(runtime->gc.stats, gcstats::PHASE_MARK);
-        gcstats::AutoPhase ap2(runtime->gc.stats, gcstats::PHASE_MARK_ROOTS);
+        gcstats::AutoPhase ap1(gc->stats, gcstats::PHASE_MARK);
+        gcstats::AutoPhase ap2(gc->stats, gcstats::PHASE_MARK_ROOTS);
         MarkRuntime(gcmarker, true);
     }
 
     {
-        gcstats::AutoPhase ap1(runtime->gc.stats, gcstats::PHASE_MARK);
+        gcstats::AutoPhase ap1(gc->stats, gcstats::PHASE_MARK);
         SliceBudget budget;
-        runtime->gc.incrementalState = MARK;
-        runtime->gc.marker.drainMarkStack(budget);
+        gc->incrementalState = MARK;
+        gc->marker.drainMarkStack(budget);
     }
 
-    runtime->gc.incrementalState = SWEEP;
+    gc->incrementalState = SWEEP;
     {
-        gcstats::AutoPhase ap(runtime->gc.stats, gcstats::PHASE_SWEEP);
-        MarkAllWeakReferences(runtime, gcstats::PHASE_SWEEP_MARK_WEAK);
+        gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_SWEEP);
+        gc->markAllWeakReferences(gcstats::PHASE_SWEEP_MARK_WEAK);
 
         /* Update zone state for gray marking. */
         for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
             JS_ASSERT(zone->isGCMarkingBlack());
             zone->setGCState(Zone::MarkGray);
         }
 
-        MarkAllGrayReferences(runtime);
+        gc->markAllGrayReferences();
 
         /* Restore zone state. */
         for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
             JS_ASSERT(zone->isGCMarkingGray());
             zone->setGCState(Zone::Mark);
         }
     }
 
     /* Take a copy of the non-incremental mark state and restore the original. */
-    for (GCChunkSet::Range r(runtime->gc.chunkSet.all()); !r.empty(); r.popFront()) {
+    for (GCChunkSet::Range r(gc->chunkSet.all()); !r.empty(); r.popFront()) {
         Chunk *chunk = r.front();
         ChunkBitmap *bitmap = &chunk->bitmap;
         ChunkBitmap *entry = map.lookup(chunk)->value();
         Swap(*entry, *bitmap);
     }
 
     for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
         WeakMapBase::resetCompartmentWeakMapList(c);
         ArrayBufferObject::resetArrayBufferList(c);
     }
     WeakMapBase::restoreCompartmentWeakMapLists(weakmaps);
     ArrayBufferObject::restoreArrayBufferLists(arrayBuffers);
 
-    runtime->gc.incrementalState = state;
+    gc->incrementalState = state;
 }
 
 void
 js::gc::MarkingValidator::validate()
 {
     /*
      * Validates the incremental marking for a single compartment by comparing
      * the mark bits to those previously recorded for a non-incremental mark.
      */
 
     if (!initialized)
         return;
 
-    for (GCChunkSet::Range r(runtime->gc.chunkSet.all()); !r.empty(); r.popFront()) {
+    for (GCChunkSet::Range r(gc->chunkSet.all()); !r.empty(); r.popFront()) {
         Chunk *chunk = r.front();
         BitmapMap::Ptr ptr = map.lookup(chunk);
         if (!ptr)
             continue;  /* Allocated after we did the non-incremental mark. */
 
         ChunkBitmap *bitmap = ptr->value();
         ChunkBitmap *incBitmap = &chunk->bitmap;
 
@@ -3272,43 +3318,43 @@ js::gc::MarkingValidator::validate()
                 thing += Arena::thingSize(kind);
             }
         }
     }
 }
 
 #endif
 
-static void
-ComputeNonIncrementalMarkingForValidation(JSRuntime *rt)
+void
+GCRuntime::computeNonIncrementalMarkingForValidation()
 {
 #ifdef DEBUG
-    JS_ASSERT(!rt->gc.markingValidator);
-    if (rt->gc.isIncremental && rt->gc.validate)
-        rt->gc.markingValidator = js_new<MarkingValidator>(rt);
-    if (rt->gc.markingValidator)
-        rt->gc.markingValidator->nonIncrementalMark();
+    JS_ASSERT(!markingValidator);
+    if (isIncremental && validate)
+        markingValidator = js_new<MarkingValidator>(this);
+    if (markingValidator)
+        markingValidator->nonIncrementalMark();
 #endif
 }
 
-static void
-ValidateIncrementalMarking(JSRuntime *rt)
+void
+GCRuntime::validateIncrementalMarking()
 {
 #ifdef DEBUG
-    if (rt->gc.markingValidator)
-        rt->gc.markingValidator->validate();
+    if (markingValidator)
+        markingValidator->validate();
 #endif
 }
 
-static void
-FinishMarkingValidation(JSRuntime *rt)
+void
+GCRuntime::finishMarkingValidation()
 {
 #ifdef DEBUG
-    js_delete(rt->gc.markingValidator);
-    rt->gc.markingValidator = nullptr;
+    js_delete(markingValidator);
+    markingValidator = nullptr;
 #endif
 }
 
 static void
 AssertNeedsBarrierFlagsConsistent(JSRuntime *rt)
 {
 #ifdef DEBUG
     bool anyNeedsBarrier = false;
@@ -3396,68 +3442,68 @@ Zone::findOutgoingEdges(ComponentFinder<
     JSRuntime *rt = runtimeFromMainThread();
     if (rt->atomsCompartment()->zone()->isGCMarking())
         finder.addEdgeTo(rt->atomsCompartment()->zone());
 
     for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next())
         comp->findOutgoingEdges(finder);
 }
 
-static void
-FindZoneGroups(JSRuntime *rt)
+void
+GCRuntime::findZoneGroups()
 {
     ComponentFinder<Zone> finder(rt->mainThread.nativeStackLimit[StackForSystemCode]);
-    if (!rt->gc.isIncremental)
+    if (!isIncremental)
         finder.useOneComponent();
 
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
         JS_ASSERT(zone->isGCMarking());
         finder.addNode(zone);
     }
-    rt->gc.zoneGroups = finder.getResultsList();
-    rt->gc.currentZoneGroup = rt->gc.zoneGroups;
-    rt->gc.zoneGroupIndex = 0;
-    JS_ASSERT_IF(!rt->gc.isIncremental, !rt->gc.currentZoneGroup->nextGroup());
+    zoneGroups = finder.getResultsList();
+    currentZoneGroup = zoneGroups;
+    zoneGroupIndex = 0;
+    JS_ASSERT_IF(!isIncremental, !currentZoneGroup->nextGroup());
 }
 
 static void
 ResetGrayList(JSCompartment* comp);
 
-static void
-GetNextZoneGroup(JSRuntime *rt)
-{
-    rt->gc.currentZoneGroup = rt->gc.currentZoneGroup->nextGroup();
-    ++rt->gc.zoneGroupIndex;
-    if (!rt->gc.currentZoneGroup) {
-        rt->gc.abortSweepAfterCurrentGroup = false;
+void
+GCRuntime::getNextZoneGroup()
+{
+    currentZoneGroup = currentZoneGroup->nextGroup();
+    ++zoneGroupIndex;
+    if (!currentZoneGroup) {
+        abortSweepAfterCurrentGroup = false;
         return;
     }
 
-    if (!rt->gc.isIncremental)
-        ComponentFinder<Zone>::mergeGroups(rt->gc.currentZoneGroup);
-
-    if (rt->gc.abortSweepAfterCurrentGroup) {
-        JS_ASSERT(!rt->gc.isIncremental);
+    if (!isIncremental)
+        ComponentFinder<Zone>::mergeGroups(currentZoneGroup);
+
+    if (abortSweepAfterCurrentGroup) {
+        JS_ASSERT(!isIncremental);
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
             JS_ASSERT(!zone->gcNextGraphComponent);
             JS_ASSERT(zone->isGCMarking());
             zone->setNeedsBarrier(false, Zone::UpdateIon);
             zone->setGCState(Zone::NoGC);
             zone->gcGrayRoots.clearAndFree();
         }
         rt->setNeedsBarrier(false);
         AssertNeedsBarrierFlagsConsistent(rt);
 
         for (GCCompartmentGroupIter comp(rt); !comp.done(); comp.next()) {
             ArrayBufferObject::resetArrayBufferList(comp);
             ResetGrayList(comp);
         }
 
-        rt->gc.abortSweepAfterCurrentGroup = false;
-        rt->gc.currentZoneGroup = nullptr;
+        abortSweepAfterCurrentGroup = false;
+        currentZoneGroup = nullptr;
     }
 }
 
 /*
  * Gray marking:
  *
  * At the end of collection, anything reachable from a gray root that has not
  * otherwise been marked black must be marked gray.
@@ -3680,58 +3726,58 @@ js::NotifyGCPostSwap(JSObject *a, JSObje
      * either of them were in our gray pointer list, we re-add them again.
      */
     if (removedFlags & JS_GC_SWAP_OBJECT_A_REMOVED)
         DelayCrossCompartmentGrayMarking(b);
     if (removedFlags & JS_GC_SWAP_OBJECT_B_REMOVED)
         DelayCrossCompartmentGrayMarking(a);
 }
 
-static void
-EndMarkingZoneGroup(JSRuntime *rt)
+void
+GCRuntime::endMarkingZoneGroup()
 {
     /*
      * Mark any incoming black pointers from previously swept compartments
      * whose referents are not marked. This can occur when gray cells become
      * black by the action of UnmarkGray.
      */
     MarkIncomingCrossCompartmentPointers(rt, BLACK);
 
-    MarkWeakReferencesInCurrentGroup(rt, gcstats::PHASE_SWEEP_MARK_WEAK);
+    markWeakReferencesInCurrentGroup(gcstats::PHASE_SWEEP_MARK_WEAK);
 
     /*
      * Change state of current group to MarkGray to restrict marking to this
      * group.  Note that there may be pointers to the atoms compartment, and
      * these will be marked through, as they are not marked with
      * MarkCrossCompartmentXXX.
      */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         JS_ASSERT(zone->isGCMarkingBlack());
         zone->setGCState(Zone::MarkGray);
     }
 
     /* Mark incoming gray pointers from previously swept compartments. */
-    rt->gc.marker.setMarkColorGray();
+    marker.setMarkColorGray();
     MarkIncomingCrossCompartmentPointers(rt, GRAY);
-    rt->gc.marker.setMarkColorBlack();
+    marker.setMarkColorBlack();
 
     /* Mark gray roots and mark transitively inside the current compartment group. */
-    MarkGrayReferencesInCurrentGroup(rt);
+    markGrayReferencesInCurrentGroup();
 
     /* Restore marking state. */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         JS_ASSERT(zone->isGCMarkingGray());
         zone->setGCState(Zone::Mark);
     }
 
-    JS_ASSERT(rt->gc.marker.isDrained());
-}
-
-static void
-BeginSweepingZoneGroup(JSRuntime *rt)
+    JS_ASSERT(marker.isDrained());
+}
+
+void
+GCRuntime::beginSweepingZoneGroup()
 {
     /*
      * Begin sweeping the group of zones in gcCurrentZoneGroup,
      * performing actions that must be done before yielding to caller.
      */
 
     bool sweepingAtoms = false;
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
@@ -3744,57 +3790,57 @@ BeginSweepingZoneGroup(JSRuntime *rt)
 
         if (rt->isAtomsZone(zone))
             sweepingAtoms = true;
 
         if (rt->sweepZoneCallback)
             rt->sweepZoneCallback(zone);
     }
 
-    ValidateIncrementalMarking(rt);
-
-    FreeOp fop(rt, rt->gc.sweepOnBackgroundThread);
+    validateIncrementalMarking();
+
+    FreeOp fop(rt, sweepOnBackgroundThread);
 
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_FINALIZE_START);
-        if (rt->gc.finalizeCallback)
-            rt->gc.finalizeCallback(&fop, JSFINALIZE_GROUP_START, !rt->gc.isFull /* unused */);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
+        if (finalizeCallback)
+            finalizeCallback(&fop, JSFINALIZE_GROUP_START, !isFull /* unused */);
     }
 
     if (sweepingAtoms) {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP_ATOMS);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_ATOMS);
         rt->sweepAtoms();
     }
 
     /* Prune out dead views from ArrayBuffer's view lists. */
     for (GCCompartmentGroupIter c(rt); !c.done(); c.next())
         ArrayBufferObject::sweep(c);
 
     /* Collect watch points associated with unreachable objects. */
     WatchpointMap::sweepAll(rt);
 
     /* Detach unreachable debuggers and global objects from each other. */
     Debugger::sweepAll(&fop);
 
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
 
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-            gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP_DISCARD_CODE);
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_DISCARD_CODE);
             zone->discardJitCode(&fop);
         }
 
-        bool releaseTypes = ReleaseObservedTypes(rt);
+        bool releaseTypes = releaseObservedTypes();
         for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-            gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+            gcstats::AutoSCC scc(stats, zoneGroupIndex);
             c->sweep(&fop, releaseTypes && !c->zone()->isPreservingCode());
         }
 
         for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-            gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+            gcstats::AutoSCC scc(stats, zoneGroupIndex);
 
             // If there is an OOM while sweeping types, the type information
             // will be deoptimized so that it is still correct (i.e.
             // overapproximates the possible types in the zone), but the
             // constraints might not have been triggered on the deoptimization
             // or even copied over completely. In this case, destroy all JIT
             // code and new script addendums in the zone, the only things whose
             // correctness depends on the type constraints.
@@ -3813,230 +3859,230 @@ BeginSweepingZoneGroup(JSRuntime *rt)
      * Queue all GC things in all zones for sweeping, either in the
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
      *
      * Objects are finalized immediately but this may change in the future.
      */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-        gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+        gcstats::AutoSCC scc(stats, zoneGroupIndex);
         zone->allocator.arenas.queueObjectsForSweep(&fop);
     }
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-        gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+        gcstats::AutoSCC scc(stats, zoneGroupIndex);
         zone->allocator.arenas.queueStringsForSweep(&fop);
     }
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-        gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+        gcstats::AutoSCC scc(stats, zoneGroupIndex);
         zone->allocator.arenas.queueScriptsForSweep(&fop);
     }
 #ifdef JS_ION
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-        gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+        gcstats::AutoSCC scc(stats, zoneGroupIndex);
         zone->allocator.arenas.queueJitCodeForSweep(&fop);
     }
 #endif
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
-        gcstats::AutoSCC scc(rt->gc.stats, rt->gc.zoneGroupIndex);
+        gcstats::AutoSCC scc(stats, zoneGroupIndex);
         zone->allocator.arenas.queueShapesForSweep(&fop);
         zone->allocator.arenas.gcShapeArenasToSweep =
             zone->allocator.arenas.arenaListsToSweep[FINALIZE_SHAPE];
     }
 
-    rt->gc.sweepPhase = 0;
-    rt->gc.sweepZone = rt->gc.currentZoneGroup;
-    rt->gc.sweepKindIndex = 0;
+    finalizePhase = 0;
+    sweepZone = currentZoneGroup;
+    sweepKindIndex = 0;
 
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_FINALIZE_END);
-        if (rt->gc.finalizeCallback)
-            rt->gc.finalizeCallback(&fop, JSFINALIZE_GROUP_END, !rt->gc.isFull /* unused */);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
+        if (finalizeCallback)
+            finalizeCallback(&fop, JSFINALIZE_GROUP_END, !isFull /* unused */);
     }
 }
 
-static void
-EndSweepingZoneGroup(JSRuntime *rt)
+void
+GCRuntime::endSweepingZoneGroup()
 {
     /* Update the GC state for zones we have swept and unlink the list. */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         JS_ASSERT(zone->isGCSweeping());
         zone->setGCState(Zone::Finished);
     }
 
     /* Reset the list of arenas marked as being allocated during sweep phase. */
-    while (ArenaHeader *arena = rt->gc.arenasAllocatedDuringSweep) {
-        rt->gc.arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
+    while (ArenaHeader *arena = arenasAllocatedDuringSweep) {
+        arenasAllocatedDuringSweep = arena->getNextAllocDuringSweep();
         arena->unsetAllocDuringSweep();
     }
 }
 
-static void
-BeginSweepPhase(JSRuntime *rt, bool lastGC)
+void
+GCRuntime::beginSweepPhase(bool lastGC)
 {
     /*
      * Sweep phase.
      *
-     * Finalize as we sweep, outside of rt->gc.lock but with rt->isHeapBusy()
+     * Finalize as we sweep, outside of lock but with rt->isHeapBusy()
      * true so that any attempt to allocate a GC-thing from a finalizer will
      * fail, rather than nest badly and leave the unmarked newborn to be swept.
      */
 
-    JS_ASSERT(!rt->gc.abortSweepAfterCurrentGroup);
-
-    ComputeNonIncrementalMarkingForValidation(rt);
-
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP);
+    JS_ASSERT(!abortSweepAfterCurrentGroup);
+
+    computeNonIncrementalMarkingForValidation();
+
+    gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
 
 #ifdef JS_THREADSAFE
-    rt->gc.sweepOnBackgroundThread = !lastGC && rt->useHelperThreads();
+    sweepOnBackgroundThread = !lastGC && rt->useHelperThreads();
 #endif
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         JS_ASSERT(!c->gcIncomingGrayPointers);
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
                 AssertNotOnGrayList(&e.front().value().get().toObject());
         }
     }
 #endif
 
     DropStringWrappers(rt);
-    FindZoneGroups(rt);
-    EndMarkingZoneGroup(rt);
-    BeginSweepingZoneGroup(rt);
+    findZoneGroups();
+    endMarkingZoneGroup();
+    beginSweepingZoneGroup();
 }
 
 bool
 ArenaLists::foregroundFinalize(FreeOp *fop, AllocKind thingKind, SliceBudget &sliceBudget)
 {
     if (!arenaListsToSweep[thingKind])
         return true;
 
     ArenaList &dest = arenaLists[thingKind];
     return FinalizeArenas(fop, &arenaListsToSweep[thingKind], dest, thingKind, sliceBudget);
 }
 
-static bool
-DrainMarkStack(JSRuntime *rt, SliceBudget &sliceBudget, gcstats::Phase phase)
+bool
+GCRuntime::drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase)
 {
     /* Run a marking slice and return whether the stack is now empty. */
-    gcstats::AutoPhase ap(rt->gc.stats, phase);
-    return rt->gc.marker.drainMarkStack(sliceBudget);
-}
-
-static bool
-SweepPhase(JSRuntime *rt, SliceBudget &sliceBudget)
-{
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP);
-    FreeOp fop(rt, rt->gc.sweepOnBackgroundThread);
-
-    bool finished = DrainMarkStack(rt, sliceBudget, gcstats::PHASE_SWEEP_MARK);
+    gcstats::AutoPhase ap(stats, phase);
+    return marker.drainMarkStack(sliceBudget);
+}
+
+bool
+GCRuntime::sweepPhase(SliceBudget &sliceBudget)
+{
+    gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
+    FreeOp fop(rt, sweepOnBackgroundThread);
+
+    bool finished = drainMarkStack(sliceBudget, gcstats::PHASE_SWEEP_MARK);
     if (!finished)
         return false;
 
     for (;;) {
         /* Finalize foreground finalized things. */
-        for (; rt->gc.sweepPhase < FinalizePhaseCount ; ++rt->gc.sweepPhase) {
-            gcstats::AutoPhase ap(rt->gc.stats, FinalizePhaseStatsPhase[rt->gc.sweepPhase]);
-
-            for (; rt->gc.sweepZone; rt->gc.sweepZone = rt->gc.sweepZone->nextNodeInGroup()) {
-                Zone *zone = rt->gc.sweepZone;
-
-                while (rt->gc.sweepKindIndex < FinalizePhaseLength[rt->gc.sweepPhase]) {
-                    AllocKind kind = FinalizePhases[rt->gc.sweepPhase][rt->gc.sweepKindIndex];
+        for (; finalizePhase < FinalizePhaseCount ; ++finalizePhase) {
+            gcstats::AutoPhase ap(stats, FinalizePhaseStatsPhase[finalizePhase]);
+
+            for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
+                Zone *zone = sweepZone;
+
+                while (sweepKindIndex < FinalizePhaseLength[finalizePhase]) {
+                    AllocKind kind = FinalizePhases[finalizePhase][sweepKindIndex];
 
                     if (!zone->allocator.arenas.foregroundFinalize(&fop, kind, sliceBudget))
                         return false;  /* Yield to the mutator. */
 
-                    ++rt->gc.sweepKindIndex;
+                    ++sweepKindIndex;
                 }
-                rt->gc.sweepKindIndex = 0;
+                sweepKindIndex = 0;
             }
-            rt->gc.sweepZone = rt->gc.currentZoneGroup;
+            sweepZone = currentZoneGroup;
         }
 
         /* Remove dead shapes from the shape tree, but don't finalize them yet. */
         {
-            gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP_SHAPE);
-
-            for (; rt->gc.sweepZone; rt->gc.sweepZone = rt->gc.sweepZone->nextNodeInGroup()) {
-                Zone *zone = rt->gc.sweepZone;
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SHAPE);
+
+            for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
+                Zone *zone = sweepZone;
                 while (ArenaHeader *arena = zone->allocator.arenas.gcShapeArenasToSweep) {
                     for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
                         Shape *shape = i.get<Shape>();
                         if (!shape->isMarked())
                             shape->sweep();
                     }
 
                     zone->allocator.arenas.gcShapeArenasToSweep = arena->next;
                     sliceBudget.step(Arena::thingsPerArena(Arena::thingSize(FINALIZE_SHAPE)));
                     if (sliceBudget.isOverBudget())
                         return false;  /* Yield to the mutator. */
                 }
             }
         }
 
-        EndSweepingZoneGroup(rt);
-        GetNextZoneGroup(rt);
-        if (!rt->gc.currentZoneGroup)
+        endSweepingZoneGroup();
+        getNextZoneGroup();
+        if (!currentZoneGroup)
             return true;  /* We're finished. */
-        EndMarkingZoneGroup(rt);
-        BeginSweepingZoneGroup(rt);
+        endMarkingZoneGroup();
+        beginSweepingZoneGroup();
     }
 }
 
-static void
-EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
-{
-    gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_SWEEP);
-    FreeOp fop(rt, rt->gc.sweepOnBackgroundThread);
-
-    JS_ASSERT_IF(lastGC, !rt->gc.sweepOnBackgroundThread);
-
-    JS_ASSERT(rt->gc.marker.isDrained());
-    rt->gc.marker.stop();
+void
+GCRuntime::endSweepPhase(JSGCInvocationKind gckind, bool lastGC)
+{
+    gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
+    FreeOp fop(rt, sweepOnBackgroundThread);
+
+    JS_ASSERT_IF(lastGC, !sweepOnBackgroundThread);
+
+    JS_ASSERT(marker.isDrained());
+    marker.stop();
 
     /*
      * Recalculate whether GC was full or not as this may have changed due to
      * newly created zones.  Can only change from full to not full.
      */
-    if (rt->gc.isFull) {
+    if (isFull) {
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             if (!zone->isCollecting()) {
-                rt->gc.isFull = false;
+                isFull = false;
                 break;
             }
         }
     }
 
     /*
      * If we found any black->gray edges during marking, we completely clear the
      * mark bits of all uncollected zones, or if a reset has occured, zones that
      * will no longer be collected. This is safe, although it may
      * prevent the cycle collector from collecting some dead objects.
      */
-    if (rt->gc.foundBlackGrayEdges) {
+    if (foundBlackGrayEdges) {
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
             if (!zone->isCollecting())
                 zone->allocator.arenas.unmarkAll();
         }
     }
 
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_DESTROY);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_DESTROY);
 
         /*
          * Sweep script filenames after sweeping functions in the generic loop
          * above. In this way when a scripted function's finalizer destroys the
          * script and calls rt->destroyScriptHook, the hook can still access the
          * script's filename. See bug 323267.
          */
-        if (rt->gc.isFull)
+        if (isFull)
             SweepScriptData(rt);
 
         /* Clear out any small pools that we're hanging on to. */
         if (JSC::ExecutableAllocator *execAlloc = rt->maybeExecAlloc())
             execAlloc->purge();
 #ifdef JS_ION
         if (rt->jitRuntime() && rt->jitRuntime()->hasIonAlloc()) {
             JSRuntime::AutoLockForInterrupt lock(rt);
@@ -4044,75 +4090,75 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
         }
 #endif
 
         /*
          * This removes compartments from rt->compartment, so we do it last to make
          * sure we don't miss sweeping any compartments.
          */
         if (!lastGC)
-            SweepZones(&fop, lastGC);
-
-        if (!rt->gc.sweepOnBackgroundThread) {
+            sweepZones(&fop, lastGC);
+
+        if (!sweepOnBackgroundThread) {
             /*
              * Destroy arenas after we finished the sweeping so finalizers can
              * safely use IsAboutToBeFinalized(). This is done on the
              * GCHelperThread if possible. We acquire the lock only because
              * Expire needs to unlock it for other callers.
              */
             AutoLockGC lock(rt);
             ExpireChunksAndArenas(rt, gckind == GC_SHRINK);
         }
     }
 
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_FINALIZE_END);
-
-        if (rt->gc.finalizeCallback)
-            rt->gc.finalizeCallback(&fop, JSFINALIZE_COLLECTION_END, !rt->gc.isFull);
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
+
+        if (finalizeCallback)
+            finalizeCallback(&fop, JSFINALIZE_COLLECTION_END, !isFull);
 
         /* If we finished a full GC, then the gray bits are correct. */
-        if (rt->gc.isFull)
-            rt->gc.grayBitsValid = true;
+        if (isFull)
+            grayBitsValid = true;
     }
 
     /* Set up list of zones for sweeping of background things. */
-    JS_ASSERT(!rt->gc.sweepingZones);
+    JS_ASSERT(!sweepingZones);
     for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-        zone->gcNextGraphNode = rt->gc.sweepingZones;
-        rt->gc.sweepingZones = zone;
+        zone->gcNextGraphNode = sweepingZones;
+        sweepingZones = zone;
     }
 
     /* If not sweeping on background thread then we must do it here. */
-    if (!rt->gc.sweepOnBackgroundThread) {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_DESTROY);
+    if (!sweepOnBackgroundThread) {
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_DESTROY);
 
         SweepBackgroundThings(rt, false);
 
         rt->freeLifoAlloc.freeAll();
 
         /* Ensure the compartments get swept if it's the last GC. */
         if (lastGC)
-            SweepZones(&fop, lastGC);
+            sweepZones(&fop, lastGC);
     }
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         zone->setGCLastBytes(zone->gcBytes, gckind);
         if (zone->isCollecting()) {
             JS_ASSERT(zone->isGCFinished());
             zone->setGCState(Zone::NoGC);
         }
 
 #ifdef DEBUG
         JS_ASSERT(!zone->isCollecting());
         JS_ASSERT(!zone->wasGCStarted());
 
         for (unsigned i = 0 ; i < FINALIZE_LIMIT ; ++i) {
             JS_ASSERT_IF(!IsBackgroundFinalized(AllocKind(i)) ||
-                         !rt->gc.sweepOnBackgroundThread,
+                         !sweepOnBackgroundThread,
                          !zone->allocator.arenas.arenaListsToSweep[i]);
         }
 #endif
     }
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         JS_ASSERT(!c->gcIncomingGrayPointers);
@@ -4120,47 +4166,47 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocat
 
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
                 AssertNotOnGrayList(&e.front().value().get().toObject());
         }
     }
 #endif
 
-    FinishMarkingValidation(rt);
-
-    rt->gc.lastGCTime = PRMJ_Now();
+    finishMarkingValidation();
+
+    lastGCTime = PRMJ_Now();
 }
 
 namespace {
 
 /* ...while this class is to be used only for garbage collection. */
 class AutoGCSession
 {
-    JSRuntime *runtime;
+    GCRuntime *gc;
     AutoTraceSession session;
     bool canceled;
 
   public:
-    explicit AutoGCSession(JSRuntime *rt);
+    explicit AutoGCSession(GCRuntime *gc);
     ~AutoGCSession();
 
     void cancel() { canceled = true; }
 };
 
 } /* anonymous namespace */
 
 /* Start a new heap session. */
 AutoTraceSession::AutoTraceSession(JSRuntime *rt, js::HeapState heapState)
   : lock(rt),
     runtime(rt),
     prevState(rt->gc.heapState)
 {
     JS_ASSERT(!rt->gc.noGCOrAllocationCheck);
-    JS_ASSERT(!rt->isHeapBusy());
+    JS_ASSERT(rt->gc.heapState == Idle);
     JS_ASSERT(heapState != Idle);
 #ifdef JSGC_GENERATIONAL
     JS_ASSERT_IF(heapState == MajorCollecting, rt->gc.nursery.isEmpty());
 #endif
 
     // Threads with an exclusive context can hit refillFreeList while holding
     // the exclusive access lock. To avoid deadlocking when we try to acquire
     // this lock during GC and the other thread is waiting, make sure we hold
@@ -4195,54 +4241,54 @@ AutoTraceSession::~AutoTraceSession()
 #else
         MOZ_CRASH();
 #endif
     } else {
         runtime->gc.heapState = prevState;
     }
 }
 
-AutoGCSession::AutoGCSession(JSRuntime *rt)
-  : runtime(rt),
-    session(rt, MajorCollecting),
+AutoGCSession::AutoGCSession(GCRuntime *gc)
+  : gc(gc),
+    session(gc->rt, MajorCollecting),
     canceled(false)
 {
-    runtime->gc.isNeeded = false;
-    runtime->gc.interFrameGC = true;
-
-    runtime->gc.number++;
+    gc->isNeeded = false;
+    gc->interFrameGC = true;
+
+    gc->number++;
 
     // It's ok if threads other than the main thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
-    JS_ASSERT(!runtime->mainThread.suppressGC);
+    JS_ASSERT(!gc->rt->mainThread.suppressGC);
 }
 
 AutoGCSession::~AutoGCSession()
 {
     if (canceled)
         return;
 
 #ifndef JS_MORE_DETERMINISTIC
-    runtime->gc.nextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
+    gc->nextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 #endif
 
-    runtime->gc.chunkAllocationSinceLastGC = false;
+    gc->chunkAllocationSinceLastGC = false;
 
 #ifdef JS_GC_ZEAL
     /* Keeping these around after a GC is dangerous. */
-    runtime->gc.selectedForMarking.clearAndFree();
+    gc->selectedForMarking.clearAndFree();
 #endif
 
     /* Clear gcMallocBytes for all compartments */
-    for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
+    for (ZonesIter zone(gc->rt, WithAtoms); !zone.done(); zone.next()) {
         zone->resetGCMallocBytes();
         zone->unscheduleGC();
     }
 
-    runtime->resetGCMallocBytes();
+    gc->rt->resetGCMallocBytes();
 }
 
 AutoCopyFreeListToArenas::AutoCopyFreeListToArenas(JSRuntime *rt, ZoneSelector selector)
   : runtime(rt),
     selector(selector)
 {
     for (ZonesIter zone(rt, selector); !zone.done(); zone.next())
         zone->allocator.arenas.copyFreeListsToArenas();
@@ -4265,77 +4311,71 @@ class AutoCopyFreeListToArenasForGC
             zone->allocator.arenas.copyFreeListsToArenas();
     }
     ~AutoCopyFreeListToArenasForGC() {
         for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next())
             zone->allocator.arenas.clearFreeListsInArenas();
     }
 };
 
-static void
-IncrementalCollectSlice(JSRuntime *rt,
-                        int64_t budget,
-                        JS::gcreason::Reason gcReason,
-                        JSGCInvocationKind gcKind);
-
-static void
-ResetIncrementalGC(JSRuntime *rt, const char *reason)
-{
-    switch (rt->gc.incrementalState) {
+void
+GCRuntime::resetIncrementalGC(const char *reason)
+{
+    switch (incrementalState) {
       case NO_INCREMENTAL:
         return;
 
       case MARK: {
         /* Cancel any ongoing marking. */
         AutoCopyFreeListToArenasForGC copy(rt);
 
-        rt->gc.marker.reset();
-        rt->gc.marker.stop();
+        marker.reset();
+        marker.stop();
 
         for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
             ArrayBufferObject::resetArrayBufferList(c);
             ResetGrayList(c);
         }
 
         for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
             JS_ASSERT(zone->isGCMarking());
             zone->setNeedsBarrier(false, Zone::UpdateIon);
             zone->setGCState(Zone::NoGC);
         }
         rt->setNeedsBarrier(false);
         AssertNeedsBarrierFlagsConsistent(rt);
 
-        rt->gc.incrementalState = NO_INCREMENTAL;
-
-        JS_ASSERT(!rt->gc.strictCompartmentChecking);
+        incrementalState = NO_INCREMENTAL;
+
+        JS_ASSERT(!strictCompartmentChecking);
 
         break;
       }
 
       case SWEEP:
-        rt->gc.marker.reset();
+        marker.reset();
 
         for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
             zone->scheduledForDestruction = false;
 
         /* Finish sweeping the current zone group, then abort. */
-        rt->gc.abortSweepAfterCurrentGroup = true;
-        IncrementalCollectSlice(rt, SliceBudget::Unlimited, JS::gcreason::RESET, GC_NORMAL);
+        abortSweepAfterCurrentGroup = true;
+        incrementalCollectSlice(SliceBudget::Unlimited, JS::gcreason::RESET, GC_NORMAL);
 
         {
-            gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
-            rt->gc.helperThread.waitBackgroundSweepOrAllocEnd();
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
+            helperThread.waitBackgroundSweepOrAllocEnd();
         }
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid incremental GC state");
     }
 
-    rt->gc.stats.reset(reason);
+    stats.reset(reason);
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         JS_ASSERT(c->gcLiveArrayBuffers.empty());
 
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         JS_ASSERT(!zone->needsBarrier());
         for (unsigned i = 0; i < FINALIZE_LIMIT; ++i)
@@ -4399,152 +4439,148 @@ AutoGCSlice::~AutoGCSlice()
         } else {
             zone->setNeedsBarrier(false, Zone::UpdateIon);
         }
     }
     runtime->setNeedsBarrier(haveBarriers);
     AssertNeedsBarrierFlagsConsistent(runtime);
 }
 
-static void
-PushZealSelectedObjects(JSRuntime *rt)
+void
+GCRuntime::pushZealSelectedObjects()
 {
 #ifdef JS_GC_ZEAL
     /* Push selected objects onto the mark stack and clear the list. */
-    for (JSObject **obj = rt->gc.selectedForMarking.begin();
-         obj != rt->gc.selectedForMarking.end(); obj++)
-    {
-        MarkObjectUnbarriered(&rt->gc.marker, obj, "selected obj");
-    }
+    for (JSObject **obj = selectedForMarking.begin(); obj != selectedForMarking.end(); obj++)
+        MarkObjectUnbarriered(&marker, obj, "selected obj");
 #endif
 }
 
-static void
-IncrementalCollectSlice(JSRuntime *rt,
-                        int64_t budget,
-                        JS::gcreason::Reason reason,
-                        JSGCInvocationKind gckind)
+void
+GCRuntime::incrementalCollectSlice(int64_t budget,
+                                   JS::gcreason::Reason reason,
+                                   JSGCInvocationKind gckind)
 {
     JS_ASSERT(rt->currentThreadHasExclusiveAccess());
 
     AutoCopyFreeListToArenasForGC copy(rt);
     AutoGCSlice slice(rt);
 
     bool lastGC = (reason == JS::gcreason::DESTROY_RUNTIME);
 
-    gc::State initialState = rt->gc.incrementalState;
+    gc::State initialState = incrementalState;
 
     int zeal = 0;
 #ifdef JS_GC_ZEAL
     if (reason == JS::gcreason::DEBUG_GC && budget != SliceBudget::Unlimited) {
         /*
          * Do the incremental collection type specified by zeal mode if the
          * collection was triggered by RunDebugGC() and incremental GC has not
-         * been cancelled by ResetIncrementalGC.
+         * been cancelled by resetIncrementalGC().
          */
-        zeal = rt->gcZeal();
+        zeal = zealMode;
     }
 #endif
 
-    JS_ASSERT_IF(rt->gc.incrementalState != NO_INCREMENTAL, rt->gc.isIncremental);
-    rt->gc.isIncremental = budget != SliceBudget::Unlimited;
+    JS_ASSERT_IF(incrementalState != NO_INCREMENTAL, isIncremental);
+    isIncremental = budget != SliceBudget::Unlimited;
 
     if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
         /*
          * Yields between slices occurs at predetermined points in these modes;
          * the budget is not used.
          */
         budget = SliceBudget::Unlimited;
     }
 
     SliceBudget sliceBudget(budget);
 
-    if (rt->gc.incrementalState == NO_INCREMENTAL) {
-        rt->gc.incrementalState = MARK_ROOTS;
-        rt->gc.lastMarkSlice = false;
+    if (incrementalState == NO_INCREMENTAL) {
+        incrementalState = MARK_ROOTS;
+        lastMarkSlice = false;
     }
 
-    if (rt->gc.incrementalState == MARK)
-        AutoGCRooter::traceAllWrappers(&rt->gc.marker);
-
-    switch (rt->gc.incrementalState) {
+    if (incrementalState == MARK)
+        AutoGCRooter::traceAllWrappers(&marker);
+
+    switch (incrementalState) {
 
       case MARK_ROOTS:
-        if (!BeginMarkPhase(rt)) {
-            rt->gc.incrementalState = NO_INCREMENTAL;
+        if (!beginMarkPhase()) {
+            incrementalState = NO_INCREMENTAL;
             return;
         }
 
         if (!lastGC)
-            PushZealSelectedObjects(rt);
-
-        rt->gc.incrementalState = MARK;
-
-        if (rt->gc.isIncremental && zeal == ZealIncrementalRootsThenFinish)
+            pushZealSelectedObjects();
+
+        incrementalState = MARK;
+
+        if (isIncremental && zeal == ZealIncrementalRootsThenFinish)
             break;
 
         /* fall through */
 
       case MARK: {
         /* If we needed delayed marking for gray roots, then collect until done. */
-        if (!rt->gc.marker.hasBufferedGrayRoots()) {
+        if (!marker.hasBufferedGrayRoots()) {
             sliceBudget.reset();
-            rt->gc.isIncremental = false;
+            isIncremental = false;
         }
 
-        bool finished = DrainMarkStack(rt, sliceBudget, gcstats::PHASE_MARK);
+        bool finished = drainMarkStack(sliceBudget, gcstats::PHASE_MARK);
         if (!finished)
             break;
 
-        JS_ASSERT(rt->gc.marker.isDrained());
-
-        if (!rt->gc.lastMarkSlice && rt->gc.isIncremental &&
+        JS_ASSERT(marker.isDrained());
+
+        if (!lastMarkSlice && isIncremental &&
             ((initialState == MARK && zeal != ZealIncrementalRootsThenFinish) ||
              zeal == ZealIncrementalMarkAllThenFinish))
         {
             /*
              * Yield with the aim of starting the sweep in the next
              * slice.  We will need to mark anything new on the stack
              * when we resume, so we stay in MARK state.
              */
-            rt->gc.lastMarkSlice = true;
+            lastMarkSlice = true;
             break;
         }
 
-        rt->gc.incrementalState = SWEEP;
+        incrementalState = SWEEP;
 
         /*
          * This runs to completion, but we don't continue if the budget is
          * now exhasted.
          */
-        BeginSweepPhase(rt, lastGC);
+        beginSweepPhase(lastGC);
         if (sliceBudget.isOverBudget())
             break;
 
         /*
          * Always yield here when running in incremental multi-slice zeal
          * mode, so RunDebugGC can reset the slice buget.
          */
-        if (rt->gc.isIncremental && zeal == ZealIncrementalMultipleSlices)
+        if (isIncremental && zeal == ZealIncrementalMultipleSlices)
             break;
 
         /* fall through */
       }
 
       case SWEEP: {
-        bool finished = SweepPhase(rt, sliceBudget);
+        bool finished = sweepPhase(sliceBudget);
         if (!finished)
             break;
 
-        EndSweepPhase(rt, gckind, lastGC);
-
-        if (rt->gc.sweepOnBackgroundThread)
-            rt->gc.helperThread.startBackgroundSweep(gckind == GC_SHRINK);
-
-        rt->gc.incrementalState = NO_INCREMENTAL;
+        endSweepPhase(gckind, lastGC);
+
+        if (sweepOnBackgroundThread)
+            helperThread.startBackgroundSweep(gckind == GC_SHRINK);
+
+        incrementalState = NO_INCREMENTAL;
         break;
       }
 
       default:
         JS_ASSERT(false);
     }
 }
 
@@ -4557,106 +4593,106 @@ gc::IsIncrementalGCSafe(JSRuntime *rt)
         return IncrementalSafety::Unsafe("keepAtoms set");
 
     if (!rt->gc.incrementalEnabled)
         return IncrementalSafety::Unsafe("incremental permanently disabled");
 
     return IncrementalSafety::Safe();
 }
 
-static void
-BudgetIncrementalGC(JSRuntime *rt, int64_t *budget)
+void
+GCRuntime::budgetIncrementalGC(int64_t *budget)
 {
     IncrementalSafety safe = IsIncrementalGCSafe(rt);
     if (!safe) {
-        ResetIncrementalGC(rt, safe.reason());
+        resetIncrementalGC(safe.reason());
         *budget = SliceBudget::Unlimited;
-        rt->gc.stats.nonincremental(safe.reason());
+        stats.nonincremental(safe.reason());
         return;
     }
 
-    if (rt->gcMode() != JSGC_MODE_INCREMENTAL) {
-        ResetIncrementalGC(rt, "GC mode change");
+    if (mode != JSGC_MODE_INCREMENTAL) {
+        resetIncrementalGC("GC mode change");
         *budget = SliceBudget::Unlimited;
-        rt->gc.stats.nonincremental("GC mode");
+        stats.nonincremental("GC mode");
         return;
     }
 
     if (rt->isTooMuchMalloc()) {
         *budget = SliceBudget::Unlimited;
-        rt->gc.stats.nonincremental("malloc bytes trigger");
+        stats.nonincremental("malloc bytes trigger");
     }
 
     bool reset = false;
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (zone->gcBytes >= zone->gcTriggerBytes) {
             *budget = SliceBudget::Unlimited;
-            rt->gc.stats.nonincremental("allocation trigger");
+            stats.nonincremental("allocation trigger");
         }
 
-        if (rt->gc.incrementalState != NO_INCREMENTAL &&
+        if (incrementalState != NO_INCREMENTAL &&
             zone->isGCScheduled() != zone->wasGCStarted())
         {
             reset = true;
         }
 
         if (zone->isTooMuchMalloc()) {
             *budget = SliceBudget::Unlimited;
-            rt->gc.stats.nonincremental("malloc bytes trigger");
+            stats.nonincremental("malloc bytes trigger");
         }
     }
 
     if (reset)
-        ResetIncrementalGC(rt, "zone change");
+        resetIncrementalGC("zone change");
 }
 
 /*
  * Run one GC "cycle" (either a slice of incremental GC or an entire
  * non-incremental GC. We disable inlining to ensure that the bottom of the
  * stack with possible GC roots recorded in MarkRuntime excludes any pointers we
  * use during the marking implementation.
  *
  * Returns true if we "reset" an existing incremental GC, which would force us
  * to run another cycle.
  */
-static MOZ_NEVER_INLINE bool
-GCCycle(JSRuntime *rt, bool incremental, int64_t budget,
-        JSGCInvocationKind gckind, JS::gcreason::Reason reason)
-{
-    AutoGCSession gcsession(rt);
+MOZ_NEVER_INLINE bool
+GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
+                   JS::gcreason::Reason reason)
+{
+    AutoGCSession gcsession(this);
 
     /*
      * As we about to purge caches and clear the mark bits we must wait for
      * any background finalization to finish. We must also wait for the
      * background allocation to finish so we can avoid taking the GC lock
      * when manipulating the chunks during the GC.
      */
     {
-        gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
-        rt->gc.helperThread.waitBackgroundSweepOrAllocEnd();
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
+        helperThread.waitBackgroundSweepOrAllocEnd();
     }
 
-    State prevState = rt->gc.incrementalState;
+    State prevState = incrementalState;
 
     if (!incremental) {
         /* If non-incremental GC was requested, reset incremental GC. */
-        ResetIncrementalGC(rt, "requested");
-        rt->gc.stats.nonincremental("requested");
+        resetIncrementalGC("requested");
+        stats.nonincremental("requested");
         budget = SliceBudget::Unlimited;
     } else {
-        BudgetIncrementalGC(rt, &budget);
+        budgetIncrementalGC(&budget);
     }
 
     /* The GC was reset, so we need a do-over. */
-    if (prevState != NO_INCREMENTAL && rt->gc.incrementalState == NO_INCREMENTAL) {
+    if (prevState != NO_INCREMENTAL && incrementalState == NO_INCREMENTAL) {
         gcsession.cancel();
         return true;
     }
 
-    IncrementalCollectSlice(rt, budget, reason, gckind);
+    incrementalCollectSlice(budget, reason, gckind);
     return false;
 }
 
 #ifdef JS_GC_ZEAL
 static bool
 IsDeterministicGCReason(JS::gcreason::Reason reason)
 {
     if (reason > JS::gcreason::DEBUG_GC &&
@@ -4668,179 +4704,185 @@ IsDeterministicGCReason(JS::gcreason::Re
     if (reason == JS::gcreason::MAYBEGC)
         return false;
 
     return true;
 }
 #endif
 
 static bool
-ShouldCleanUpEverything(JSRuntime *rt, JS::gcreason::Reason reason, JSGCInvocationKind gckind)
+ShouldCleanUpEverything(JS::gcreason::Reason reason, JSGCInvocationKind gckind)
 {
     // During shutdown, we must clean everything up, for the sake of leak
     // detection. When a runtime has no contexts, or we're doing a GC before a
     // shutdown CC, those are strong indications that we're shutting down.
     return reason == JS::gcreason::DESTROY_RUNTIME ||
            reason == JS::gcreason::SHUTDOWN_CC ||
            gckind == GC_SHRINK;
 }
 
 namespace {
 
 #ifdef JSGC_GENERATIONAL
 class AutoDisableStoreBuffer
 {
-    JSRuntime *runtime;
+    StoreBuffer &sb;
     bool prior;
 
   public:
-    AutoDisableStoreBuffer(JSRuntime *rt) : runtime(rt) {
-        prior = rt->gc.storeBuffer.isEnabled();
-        rt->gc.storeBuffer.disable();
+    AutoDisableStoreBuffer(GCRuntime *gc) : sb(gc->storeBuffer) {
+        prior = sb.isEnabled();
+        sb.disable();
     }
     ~AutoDisableStoreBuffer() {
         if (prior)
-            runtime->gc.storeBuffer.enable();
+            sb.enable();
     }
 };
 #else
 struct AutoDisableStoreBuffer
 {
-    AutoDisableStoreBuffer(JSRuntime *) {}
+    AutoDisableStoreBuffer(GCRuntime *gc) {}
 };
 #endif
 
 } /* anonymous namespace */
 
-static void
-Collect(JSRuntime *rt, bool incremental, int64_t budget,
-        JSGCInvocationKind gckind, JS::gcreason::Reason reason)
+void
+GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
+                   JS::gcreason::Reason reason)
 {
     /* GC shouldn't be running in parallel execution mode */
     JS_ASSERT(!InParallelSection());
 
     JS_AbortIfWrongThread(rt);
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
     JS_ASSERT(!rt->isHeapBusy());
 
     if (rt->mainThread.suppressGC)
         return;
 
     TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logGC(logger, TraceLogger::GC);
 
 #ifdef JS_GC_ZEAL
-    if (rt->gc.deterministicOnly && !IsDeterministicGCReason(reason))
+    if (deterministicOnly && !IsDeterministicGCReason(reason))
         return;
 #endif
 
     JS_ASSERT_IF(!incremental || budget != SliceBudget::Unlimited, JSGC_INCREMENTAL);
 
     AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC ||
                                      reason == JS::gcreason::DESTROY_RUNTIME);
 
-    RecordNativeStackTopForGC(rt);
+    recordNativeStackTopForGC();
 
     int zoneCount = 0;
     int compartmentCount = 0;
     int collectedCount = 0;
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
-        if (rt->gcMode() == JSGC_MODE_GLOBAL)
+        if (mode == JSGC_MODE_GLOBAL)
             zone->scheduleGC();
 
         /* This is a heuristic to avoid resets. */
-        if (rt->gc.incrementalState != NO_INCREMENTAL && zone->needsBarrier())
+        if (incrementalState != NO_INCREMENTAL && zone->needsBarrier())
             zone->scheduleGC();
 
         zoneCount++;
         if (zone->isGCScheduled())
             collectedCount++;
     }
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next())
         compartmentCount++;
 
-    rt->gc.shouldCleanUpEverything = ShouldCleanUpEverything(rt, reason, gckind);
+    shouldCleanUpEverything = ShouldCleanUpEverything(reason, gckind);
 
     bool repeat = false;
     do {
-        MinorGC(rt, reason);
+        minorGC(reason);
 
         /*
          * Marking can trigger many incidental post barriers, some of them for
          * objects which are not going to be live after the GC.
          */
-        AutoDisableStoreBuffer adsb(rt);
-
-        gcstats::AutoGCSlice agc(rt->gc.stats, collectedCount, zoneCount, compartmentCount, reason);
+        AutoDisableStoreBuffer adsb(this);
+
+        gcstats::AutoGCSlice agc(stats, collectedCount, zoneCount, compartmentCount, reason);
 
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context). Invoke the callback regardless.
          */
-        if (rt->gc.incrementalState == NO_INCREMENTAL) {
-            gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_GC_BEGIN);
-            if (JSGCCallback callback = rt->gc.callback)
-                callback(rt, JSGC_BEGIN, rt->gc.callbackData);
+        if (incrementalState == NO_INCREMENTAL) {
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
+            if (gcCallback)
+                gcCallback(rt, JSGC_BEGIN, gcCallbackData);
         }
 
-        rt->gc.poke = false;
-        bool wasReset = GCCycle(rt, incremental, budget, gckind, reason);
-
-        if (rt->gc.incrementalState == NO_INCREMENTAL) {
-            gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_GC_END);
-            if (JSGCCallback callback = rt->gc.callback)
-                callback(rt, JSGC_END, rt->gc.callbackData);
+        poke = false;
+        bool wasReset = gcCycle(incremental, budget, gckind, reason);
+
+        if (incrementalState == NO_INCREMENTAL) {
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
+            if (gcCallback)
+                gcCallback(rt, JSGC_END, gcCallbackData);
         }
 
         /* Need to re-schedule all zones for GC. */
-        if (rt->gc.poke && rt->gc.shouldCleanUpEverything)
+        if (poke && shouldCleanUpEverything)
             JS::PrepareForFullGC(rt);
 
         /*
          * If we reset an existing GC, we need to start a new one. Also, we
          * repeat GCs that happen during shutdown (the gcShouldCleanUpEverything
          * case) until we can be sure that no additional garbage is created
          * (which typically happens if roots are dropped during finalizers).
          */
-        repeat = (rt->gc.poke && rt->gc.shouldCleanUpEverything) || wasReset;
+        repeat = (poke && shouldCleanUpEverything) || wasReset;
     } while (repeat);
 
-    if (rt->gc.incrementalState == NO_INCREMENTAL) {
+    if (incrementalState == NO_INCREMENTAL) {
 #ifdef JS_THREADSAFE
         EnqueuePendingParseTasksAfterGC(rt);
 #endif
     }
 }
 
 void
 js::GC(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason)
 {
-    Collect(rt, false, SliceBudget::Unlimited, gckind, reason);
+    rt->gc.collect(false, SliceBudget::Unlimited, gckind, reason);
 }
 
 void
 js::GCSlice(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
 {
-    int64_t sliceBudget;
+    rt->gc.gcSlice(gckind, reason, millis);
+}
+
+void
+GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
+{
+    int64_t budget;
     if (millis)
-        sliceBudget = SliceBudget::TimeBudget(millis);
-    else if (rt->gc.highFrequencyGC && rt->gc.dynamicMarkSlice)
-        sliceBudget = rt->gc.sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
+        budget = SliceBudget::TimeBudget(millis);
+    else if (highFrequencyGC && dynamicMarkSlice)
+        budget = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
     else
-        sliceBudget = rt->gc.sliceBudget;
-
-    Collect(rt, true, sliceBudget, gckind, reason);
+        budget = sliceBudget;
+
+    collect(true, budget, gckind, reason);
 }
 
 void
 js::GCFinalSlice(JSRuntime *rt, JSGCInvocationKind gckind, JS::gcreason::Reason reason)
 {
-    Collect(rt, true, SliceBudget::Unlimited, gckind, reason);
+    rt->gc.collect(true, SliceBudget::Unlimited, gckind, reason);
 }
 
 static bool
 ZonesSelected(JSRuntime *rt)
 {
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         if (zone->isGCScheduled())
             return true;
@@ -4853,17 +4895,17 @@ js::GCDebugSlice(JSRuntime *rt, bool lim
 {
     int64_t budget = limit ? SliceBudget::WorkBudget(objCount) : SliceBudget::Unlimited;
     if (!ZonesSelected(rt)) {
         if (JS::IsIncrementalGCInProgress(rt))
             JS::PrepareForIncrementalGC(rt);
         else
             JS::PrepareForFullGC(rt);
     }
-    Collect(rt, true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
+    rt->gc.collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
 }
 
 /* Schedule a full GC unless a zone will already be collected. */
 void
 js::PrepareForDebugGC(JSRuntime *rt)
 {
     if (!ZonesSelected(rt))
         JS::PrepareForFullGC(rt);
@@ -4879,60 +4921,74 @@ JS::ShrinkGCBuffers(JSRuntime *rt)
         ExpireChunksAndArenas(rt, true);
     else
         rt->gc.helperThread.startBackgroundShrink();
 }
 
 void
 js::MinorGC(JSRuntime *rt, JS::gcreason::Reason reason)
 {
+    rt->gc.minorGC(reason);
+}
+
+void
+GCRuntime::minorGC(JS::gcreason::Reason reason)
+{
 #ifdef JSGC_GENERATIONAL
     TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
-    rt->gc.nursery.collect(rt, reason, nullptr);
-    JS_ASSERT_IF(!rt->mainThread.suppressGC, rt->gc.nursery.isEmpty());
+    nursery.collect(rt, reason, nullptr);
+    JS_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty());
 #endif
 }
 
 void
 js::MinorGC(JSContext *cx, JS::gcreason::Reason reason)
 {
     // Alternate to the runtime-taking form above which allows marking type
     // objects as needing pretenuring.
+    cx->runtime()->gc.minorGC(cx, reason);
+}
+
+void
+GCRuntime::minorGC(JSContext *cx, JS::gcreason::Reason reason)
+{
 #ifdef JSGC_GENERATIONAL
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
-
     Nursery::TypeObjectList pretenureTypes;
-    JSRuntime *rt = cx->runtime();
-    rt->gc.nursery.collect(cx->runtime(), reason, &pretenureTypes);
+    nursery.collect(rt, reason, &pretenureTypes);
     for (size_t i = 0; i < pretenureTypes.length(); i++) {
         if (pretenureTypes[i]->canPreTenure())
             pretenureTypes[i]->setShouldPreTenure(cx);
     }
-    JS_ASSERT_IF(!rt->mainThread.suppressGC, rt->gc.nursery.isEmpty());
+    JS_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty());
 #endif
 }
 
 void
 js::gc::GCIfNeeded(JSContext *cx)
 {
-    JSRuntime *rt = cx->runtime();
-
+    cx->runtime()->gc.gcIfNeeded(cx);
+}
+
+void
+GCRuntime::gcIfNeeded(JSContext *cx)
+{
 #ifdef JSGC_GENERATIONAL
     /*
      * In case of store buffer overflow perform minor GC first so that the
      * correct reason is seen in the logs.
      */
-    if (rt->gc.storeBuffer.isAboutToOverflow())
-        MinorGC(cx, JS::gcreason::FULL_STORE_BUFFER);
+    if (storeBuffer.isAboutToOverflow())
+        minorGC(cx, JS::gcreason::FULL_STORE_BUFFER);
 #endif
 
-    if (rt->gc.isNeeded)
-        GCSlice(rt, GC_NORMAL, rt->gc.triggerReason);
+    if (isNeeded)
+        gcSlice(GC_NORMAL, rt->gc.triggerReason, 0);
 }
 
 void
 js::gc::FinishBackgroundFinalize(JSRuntime *rt)
 {
     rt->gc.helperThread.waitBackgroundSweepEnd();
 }
 
@@ -4946,17 +5002,17 @@ AutoFinishGC::AutoFinishGC(JSRuntime *rt
     gc::FinishBackgroundFinalize(rt);
 }
 
 AutoPrepareForTracing::AutoPrepareForTracing(JSRuntime *rt, ZoneSelector selector)
   : finish(rt),
     session(rt),
     copy(rt, selector)
 {
-    RecordNativeStackTopForGC(rt);
+    rt->gc.recordNativeStackTopForGC();
 }
 
 JSCompartment *
 js::NewCompartment(JSContext *cx, Zone *zone, JSPrincipals *principals,
                    const JS::CompartmentOptions &options)
 {
     JSRuntime *rt = cx->runtime();
     JS_AbortIfWrongThread(rt);
@@ -5048,63 +5104,68 @@ gc::MergeCompartments(JSCompartment *sou
 
     // Merge other info in source's zone into target's zone.
     target->zone()->types.typeLifoAlloc.transferFrom(&source->zone()->types.typeLifoAlloc);
 }
 
 void
 gc::RunDebugGC(JSContext *cx)
 {
+    cx->runtime()->gc.runDebugGC();
+}
+
+void
+GCRuntime::runDebugGC()
+{
 #ifdef JS_GC_ZEAL
-    JSRuntime *rt = cx->runtime();
-    int type = rt->gcZeal();
+    int type = zealMode;
 
     if (rt->mainThread.suppressGC)
         return;
 
     if (type == js::gc::ZealGenerationalGCValue)
         return MinorGC(rt, JS::gcreason::DEBUG_GC);
 
-    PrepareForDebugGC(cx->runtime());
+    PrepareForDebugGC(rt);
 
     if (type == ZealIncrementalRootsThenFinish ||
         type == ZealIncrementalMarkAllThenFinish ||
         type == ZealIncrementalMultipleSlices)
     {
-        js::gc::State initialState = rt->gc.incrementalState;
+        js::gc::State initialState = incrementalState;
         int64_t budget;
         if (type == ZealIncrementalMultipleSlices) {
             /*
              * Start with a small slice limit and double it every slice. This
              * ensure that we get multiple slices, and collection runs to
              * completion.
              */
             if (initialState == NO_INCREMENTAL)
-                rt->gc.incrementalLimit = rt->gc.zealFrequency / 2;
+                incrementalLimit = zealFrequency / 2;
             else
-                rt->gc.incrementalLimit *= 2;
-            budget = SliceBudget::WorkBudget(rt->gc.incrementalLimit);
+                incrementalLimit *= 2;
+            budget = SliceBudget::WorkBudget(incrementalLimit);
         } else {
             // This triggers incremental GC but is actually ignored by IncrementalMarkSlice.
             budget = SliceBudget::WorkBudget(1);
         }
 
-        Collect(rt, true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
+        collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
 
         /*
          * For multi-slice zeal, reset the slice size when we get to the sweep
          * phase.
          */
         if (type == ZealIncrementalMultipleSlices &&
-            initialState == MARK && rt->gc.incrementalState == SWEEP)
+            initialState == MARK && incrementalState == SWEEP)
         {
-            rt->gc.incrementalLimit = rt->gc.zealFrequency / 2;
+            incrementalLimit = zealFrequency / 2;
         }
     } else {
-        Collect(rt, false, SliceBudget::Unlimited, GC_NORMAL, JS::gcreason::DEBUG_GC);
+        collect(false, SliceBudget::Unlimited, GC_NORMAL, JS::gcreason::DEBUG_GC);
     }
 
 #endif
 }
 
 void
 gc::SetDeterministicGC(JSContext *cx, bool enabled)
 {
@@ -5295,17 +5356,17 @@ js::PurgeJITCaches(Zone *zone)
         jit::PurgeCaches(script, zone);
     }
 #endif
 }
 
 void
 ArenaLists::normalizeBackgroundFinalizeState(AllocKind thingKind)
 {
-    volatile uintptr_t *bfs = &backgroundFinalizeState[thingKind];
+    ArenaLists::BackgroundFinalizeState *bfs = &backgroundFinalizeState[thingKind];
     switch (*bfs) {
       case BFS_DONE:
         break;
       case BFS_JUST_FINISHED:
         // No allocations between end of last sweep and now.
         // Transfering over arenas is a kind of allocation.
         *bfs = BFS_DONE;
         break;
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -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/. */
 
 /* JS Garbage Collector. */
 
 #ifndef jsgc_h
 #define jsgc_h
 
+#include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jslock.h"
 #include "jsobj.h"
 
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
@@ -84,18 +85,16 @@ class ChunkPool {
     ChunkPool()
       : emptyChunkListHead(nullptr),
         emptyCount(0) { }
 
     size_t getEmptyCount() const {
         return emptyCount;
     }
 
-    inline bool wantBackgroundAllocation(JSRuntime *rt) const;
-
     /* Must be called with the GC lock taken. */
     inline Chunk *get(JSRuntime *rt);
 
     /* Must be called either during the GC or with the GC lock taken. */
     inline void put(Chunk *chunk);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
@@ -556,23 +555,26 @@ class ArenaLists
      * outside the GC lock.
      *
      * In BFS_RUN and BFS_JUST_FINISHED the allocation thread must take the
      * lock. The former indicates that the finalization still runs. The latter
      * signals that finalization just added to the list finalized arenas. In
      * that case the lock effectively serves as a read barrier to ensure that
      * the allocation thread sees all the writes done during finalization.
      */
-    enum BackgroundFinalizeState {
+    enum BackgroundFinalizeStateEnum {
         BFS_DONE,
         BFS_RUN,
         BFS_JUST_FINISHED
     };
 
-    volatile uintptr_t backgroundFinalizeState[FINALIZE_LIMIT];
+    typedef mozilla::Atomic<BackgroundFinalizeStateEnum, mozilla::ReleaseAcquire>
+        BackgroundFinalizeState;
+
+    BackgroundFinalizeState backgroundFinalizeState[FINALIZE_LIMIT];
 
   public:
     /* For each arena kind, a list of arenas remaining to be swept. */
     ArenaHeader *arenaListsToSweep[FINALIZE_LIMIT];
 
     /* Shape arenas to be swept in the foreground. */
     ArenaHeader *gcShapeArenasToSweep;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4023,18 +4023,18 @@ NativeLookupOwnProperty(ExclusiveContext
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 LookupPropertyInline(ExclusiveContext *cx,
                      typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
                      typename MaybeRooted<jsid, allowGC>::HandleType id,
                      typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
                      typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
 {
-    /* NB: The logic of this procedure is implicitly reflected in IonBuilder.cpp's
-     *     |CanEffectlesslyCallLookupGenericOnObject| logic.
+    /* NB: The logic of this procedure is implicitly reflected in
+     *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
      *     If this changes, please remember to update the logic there as well.
      */
 
     /* Search scopes starting with obj and following the prototype link. */
     typename MaybeRooted<JSObject*, allowGC>::RootType current(cx, obj);
 
     while (true) {
         bool done;
@@ -4084,19 +4084,18 @@ template bool
 baseops::LookupProperty<NoGC>(ExclusiveContext *cx, JSObject *obj, jsid id,
                               FakeMutableHandle<JSObject*> objp,
                               FakeMutableHandle<Shape*> propp);
 
 /* static */ bool
 JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
                         MutableHandleObject objp, MutableHandleShape propp)
 {
-    /*
-     * NB: The logic of lookupGeneric is implicitly reflected in IonBuilder.cpp's
-     *     |CanEffectlesslyCallLookupGenericOnObject| logic.
+    /* NB: The logic of lookupGeneric is implicitly reflected in
+     *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
      *     If this changes, please remember to update the logic there as well.
      */
     LookupGenericOp op = obj->getOps()->lookupGeneric;
     if (op)
         return op(cx, obj, id, objp, propp);
     return baseops::LookupProperty<js::CanGC>(cx, obj, id, objp, propp);
 }
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -154,16 +154,17 @@ typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
 void js_FinishGC(JSRuntime *rt);
 
 namespace js {
 namespace gc {
 class StoreBuffer;
 void MarkPersistentRootedChains(JSTracer *);
+void FinishPersistentRootedChains(JSRuntime *);
 }
 }
 
 namespace JS {
 
 typedef void (*OffThreadCompileCallback)(void *token, void *callbackData);
 
 namespace shadow {
@@ -207,17 +208,17 @@ struct Runtime
     static JS::shadow::Runtime *asShadowRuntime(JSRuntime *rt) {
         return reinterpret_cast<JS::shadow::Runtime*>(rt);
     }
 
     /* Allow inlining of PersistentRooted constructors and destructors. */
   private:
     template <typename Referent> friend class JS::PersistentRooted;
     friend void js::gc::MarkPersistentRootedChains(JSTracer *);
-    friend void ::js_FinishGC(JSRuntime *rt);
+    friend void js::gc::FinishPersistentRootedChains(JSRuntime *rt);
 
     mozilla::LinkedList<PersistentRootedFunction> functionPersistentRooteds;
     mozilla::LinkedList<PersistentRootedId>       idPersistentRooteds;
     mozilla::LinkedList<PersistentRootedObject>   objectPersistentRooteds;
     mozilla::LinkedList<PersistentRootedScript>   scriptPersistentRooteds;
     mozilla::LinkedList<PersistentRootedString>   stringPersistentRooteds;
     mozilla::LinkedList<PersistentRootedValue>    valuePersistentRooteds;
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -274,20 +274,17 @@ JSRuntime::init(uint32_t maxbytes)
     if (!mainThread.init())
         return false;
 
     js::TlsPerThreadData.set(&mainThread);
 
     if (!threadPool.init())
         return false;
 
-    if (!js_InitGC(this, maxbytes))
-        return false;
-
-    if (!gc.marker.init(gcMode()))
+    if (!gc.init(maxbytes))
         return false;
 
     const char *size = getenv("JSGC_MARK_STACK_LIMIT");
     if (size)
         SetMarkStackLimit(this, atoi(size));
 
     ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this));
     if (!atomsZone)
@@ -424,17 +421,17 @@ JSRuntime::~JSRuntime()
                 cxcount, (cxcount == 1) ? "" : "s");
     }
 #endif
 
 #if !EXPOSE_INTL_API
     FinishRuntimeNumberState(this);
 #endif
 
-    js_FinishGC(this);
+    gc.finish();
     atomsCompartment_ = nullptr;
 
 #ifdef JS_THREADSAFE
     if (gc.lock)
         PR_DestroyLock(gc.lock);
 #endif
 
     js_free(defaultLocale);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -920,20 +920,20 @@ struct JSRuntime : public JS::shadow::Ru
     bool                gcInitialized;
 
     JSGCMode gcMode() const { return gc.mode; }
     void setGCMode(JSGCMode mode) {
         gc.mode = mode;
         gc.marker.setGCMode(mode);
     }
 
-    bool isHeapBusy() { return gc.heapState != js::Idle; }
-    bool isHeapMajorCollecting() { return gc.heapState == js::MajorCollecting; }
-    bool isHeapMinorCollecting() { return gc.heapState == js::MinorCollecting; }
-    bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); }
+    bool isHeapBusy() { return gc.isHeapBusy(); }
+    bool isHeapMajorCollecting() { return gc.isHeapMajorCollecting(); }
+    bool isHeapMinorCollecting() { return gc.isHeapMinorCollecting(); }
+    bool isHeapCollecting() { return gc.isHeapCollecting(); }
 
 #ifdef JS_GC_ZEAL
     int gcZeal() { return gc.zealMode; }
 
     bool upcomingZealousGC() {
         return gc.nextScheduled == 1;
     }
 
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/bug1007065-1-ref.html
@@ -0,0 +1,15 @@
+<html class="reftest-wait">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  </head>
+  <body onload="start()">
+    <input type="text" style="text-align:right;">
+    <script>
+      function start() {
+        var input = document.querySelector("input");
+        input.focus();
+        document.documentElement.removeAttribute("class");
+      }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/bug1007065-1.html
@@ -0,0 +1,15 @@
+<html class="reftest-wait">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+  </head>
+  <body onload="start()">
+    <input type="text" style="text-align:right; overflow:hidden;">
+    <script>
+      function start() {
+        var input = document.querySelector("input");
+        input.focus();
+        document.documentElement.removeAttribute("class");
+      }
+    </script>
+  </body>
+</html>
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -152,16 +152,18 @@ support-files =
   bug923376.html
   bug923376-ref.html
   bug966992-1.html
   bug966992-1-ref.html
   bug966992-2.html
   bug966992-2-ref.html
   bug966992-3.html
   bug966992-3-ref.html
+  bug1007065-1.html
+  bug1007065-1-ref.html
 [test_bug514127.html]
 [test_bug518777.html]
 [test_bug548545.xhtml]
 [test_bug558663.html]
 [test_bug559499.html]
 [test_bug569520.html]
 [test_bug582181-1.html]
 [test_bug582181-2.html]
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -131,16 +131,17 @@ var tests = [
     function() {SpecialPowers.setBoolPref("bidi.browser.ui", true);} ,
     [ 'bug646382-1.html' , 'bug646382-1-ref.html' ] ,
     [ 'bug646382-2.html' , 'bug646382-2-ref.html' ] ,
     [ 'bug664087-1.html' , 'bug664087-1-ref.html' ] ,
     [ 'bug664087-2.html' , 'bug664087-2-ref.html' ] ,
     [ 'bug682712-1.html' , 'bug682712-1-ref.html' ] ,
     function() {SpecialPowers.clearUserPref("bidi.browser.ui");} ,
     [ 'bug746993-1.html' , 'bug746993-1-ref.html' ] ,
+    [ 'bug1007065-1.html' , 'bug1007065-1-ref.html' ] ,
 ];
 
 if (navigator.appVersion.indexOf("Android") == -1 &&
   SpecialPowers.Services.appinfo.name != "B2G") {
   tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]);
   tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]);
   tests.push(function() {SpecialPowers.setBoolPref("layout.css.overflow-clip-box.enabled", true);});
   tests.push([ 'bug966992-1.html' , 'bug966992-1-ref.html' ]);
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -41,24 +41,24 @@ nsBlockReflowState::nsBlockReflowState(c
     mFlags(0),
     mFloatBreakType(NS_STYLE_CLEAR_NONE),
     mConsumedHeight(aConsumedHeight)
 {
   SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nullptr);
   SetFlag(BRS_ISOVERFLOWCONTAINER,
           IS_TRUE_OVERFLOW_CONTAINER(aFrame));
 
-  const nsMargin& borderPadding = BorderPadding();
-  mContainerWidth = aReflowState.ComputedWidth() +
-                    aReflowState.ComputedPhysicalBorderPadding().LeftRight();
+  mBorderPadding = mReflowState.ComputedPhysicalBorderPadding();
+  aFrame->ApplySkipSides(mBorderPadding, &aReflowState);
+  mContainerWidth = aReflowState.ComputedWidth() + mBorderPadding.LeftRight();
 
-  if (aTopMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().top) {
+  if (aTopMarginRoot || 0 != mBorderPadding.top) {
     SetFlag(BRS_ISTOPMARGINROOT, true);
   }
-  if (aBottomMarginRoot || 0 != aReflowState.ComputedPhysicalBorderPadding().bottom) {
+  if (aBottomMarginRoot || 0 != mBorderPadding.bottom) {
     SetFlag(BRS_ISBOTTOMMARGINROOT, true);
   }
   if (GetFlag(BRS_ISTOPMARGINROOT)) {
     SetFlag(BRS_APPLYTOPMARGIN, true);
   }
   if (aBlockNeedsFloatManager) {
     SetFlag(BRS_FLOAT_MGR, true);
   }
@@ -88,27 +88,27 @@ nsBlockReflowState::nsBlockReflowState(c
   // managed by the "overflow" property. When we don't have a
   // specified style height then we may end up limiting our height if
   // the availableHeight is constrained (this situation occurs when we
   // are paginated).
   if (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight()) {
     // We are in a paginated situation. The bottom edge is just inside
     // the bottom border and padding. The content area height doesn't
     // include either border or padding edge.
-    mBottomEdge = aReflowState.AvailableHeight() - borderPadding.bottom;
-    mContentArea.height = std::max(0, mBottomEdge - borderPadding.top);
+    mBottomEdge = aReflowState.AvailableHeight() - mBorderPadding.bottom;
+    mContentArea.height = std::max(0, mBottomEdge - mBorderPadding.top);
   }
   else {
     // When we are not in a paginated situation then we always use
     // an constrained height.
     SetFlag(BRS_UNCONSTRAINEDHEIGHT, true);
     mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE;
   }
-  mContentArea.x = borderPadding.left;
-  mY = mContentArea.y = borderPadding.top;
+  mContentArea.x = mBorderPadding.left;
+  mY = mContentArea.y = mBorderPadding.top;
 
   mPrevChild = nullptr;
   mCurrentLine = aFrame->end_lines();
 
   mMinLineHeight = aReflowState.CalcLineHeight();
 }
 
 nscoord
--- a/layout/generic/nsBlockReflowState.h
+++ b/layout/generic/nsBlockReflowState.h
@@ -87,33 +87,24 @@ public:
   // Returns the first coordinate >= aY that clears the
   // floats indicated by aBreakType and has enough width between floats
   // (or no floats remaining) to accomodate aReplacedBlock.
   nscoord ClearFloats(nscoord aY, uint8_t aBreakType,
                       nsIFrame *aReplacedBlock = nullptr,
                       uint32_t aFlags = 0);
 
   bool IsAdjacentWithTop() const {
-    return mY ==
-      ((mFlags & BRS_ISFIRSTINFLOW) ? mReflowState.ComputedPhysicalBorderPadding().top : 0);
+    return mY == mBorderPadding.top;
   }
 
   /**
-   * Adjusts the border/padding to return 0 for the top if
-   * we are not the first in flow.
+   * Return mBlock's computed physical border+padding with GetSkipSides applied.
    */
-  nsMargin BorderPadding() const {
-    nsMargin result = mReflowState.ComputedPhysicalBorderPadding();
-    if (!(mFlags & BRS_ISFIRSTINFLOW)) {
-      result.top = 0;
-      if (mFlags & BRS_ISOVERFLOWCONTAINER) {
-        result.bottom = 0;
-      }
-    }
-    return result;
+  const nsMargin& BorderPadding() const {
+    return mBorderPadding;
   }
 
   /**
    * Retrieve the height "consumed" by any previous-in-flows.
    */
   nscoord GetConsumedHeight();
 
   // Reconstruct the previous bottom margin that goes above |aLine|.
@@ -217,16 +208,19 @@ public:
   // which we know is adjacent to the top of the block (in other words,
   // all lines before it are empty and do not have clearance. This line is
   // always before the current line.
   nsLineList::iterator mLineAdjacentToTop;
 
   // The current Y coordinate in the block
   nscoord mY;
 
+  // mBlock's computed physical border+padding with GetSkipSides applied.
+  nsMargin mBorderPadding;
+
   // The overflow areas of all floats placed so far
   nsOverflowAreas mFloatOverflowAreas;
 
   nsFloatCacheFreeList mFloatCacheFreeList;
 
   // Previous child. This is used when pulling up a frame to update
   // the sibling list.
   nsIFrame* mPrevChild;
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -578,16 +578,20 @@ public:
           type == nsGkAtoms::tableCellFrame ||
           type == nsGkAtoms::bcTableCellFrame ||
           type == nsGkAtoms::svgOuterSVGFrame ||
           type == nsGkAtoms::svgInnerSVGFrame ||
           type == nsGkAtoms::svgForeignObjectFrame) {
         return true;
       }
       if (aFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
+        if (type == nsGkAtoms::textInputFrame) {
+          // It always has an anonymous scroll frame that handles any overflow.
+          return false;
+        }
         return true;
       }
     }
 
     if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
       return false;
     }
 
--- a/layout/generic/nsSplittableFrame.cpp
+++ b/layout/generic/nsSplittableFrame.cpp
@@ -205,22 +205,19 @@ nsSplittableFrame::RemoveFromFlow(nsIFra
   aFrame->SetPrevInFlow(nullptr);
   aFrame->SetNextInFlow(nullptr);
 }
 
 nscoord
 nsSplittableFrame::GetConsumedHeight() const
 {
   nscoord height = 0;
-
-  // Reduce the height by the computed height of prev-in-flows.
   for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
-    height += prev->GetRect().height;
+    height += prev->GetContentRectRelativeToSelf().height;
   }
-
   return height;
 }
 
 nscoord
 nsSplittableFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState,
                                               nscoord aConsumedHeight) const
 {
   nscoord height = aReflowState.ComputedHeight();
@@ -229,26 +226,18 @@ nsSplittableFrame::GetEffectiveComputedH
   }
 
   if (aConsumedHeight == NS_INTRINSICSIZE) {
     aConsumedHeight = GetConsumedHeight();
   }
 
   height -= aConsumedHeight;
 
-  if (aConsumedHeight != 0 && aConsumedHeight != NS_INTRINSICSIZE) {
-    // We just subtracted our top-border padding, since it was included in the
-    // first frame's height. Add it back to get the content height.
-    height += aReflowState.ComputedPhysicalBorderPadding().top;
-  }
-
   // We may have stretched the frame beyond its computed height. Oh well.
-  height = std::max(0, height);
-
-  return height;
+  return std::max(0, height);
 }
 
 int
 nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
   if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
     return LOGICAL_SIDES_B_BOTH;
   }
@@ -266,17 +255,18 @@ nsSplittableFrame::GetLogicalSkipSides(c
   if (aReflowState) {
     // We're in the midst of reflow right now, so it's possible that we haven't
     // created a nif yet. If our content height is going to exceed our available
     // height, though, then we're going to need a next-in-flow, it just hasn't
     // been created yet.
 
     if (NS_UNCONSTRAINEDSIZE != aReflowState->AvailableHeight()) {
       nscoord effectiveCH = this->GetEffectiveComputedHeight(*aReflowState);
-      if (effectiveCH > aReflowState->AvailableHeight()) {
+      if (effectiveCH != NS_INTRINSICSIZE &&
+          effectiveCH > aReflowState->AvailableHeight()) {
         // Our content height is going to exceed our available height, so we're
         // going to need a next-in-flow.
         skip |= LOGICAL_SIDE_B_END;
       }
     }
   } else {
     nsIFrame* nif = GetNextInFlow();
     if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
new file mode 100644
--- /dev/null
+++ b/layout/mathml/crashtests/848725-1.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Bug 848725</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+    <div>
+      <math>
+        <mover>
+          <mo>&#x21A0;</mo>
+          <mspace width="500px"></mspace>
+        </mover>
+      </math>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/mathml/crashtests/848725-2.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Bug 848725</title>
+    <meta charset="utf-8"/>
+  </head>
+  <body>
+    <div>
+      <math>
+        <mover>
+          <mo>&#x21A1;</mo>
+          <mspace height="500px"></mspace>
+        </mover>
+      </math>
+    </div>
+  </body>
+</html>
--- a/layout/mathml/crashtests/crashtests.list
+++ b/layout/mathml/crashtests/crashtests.list
@@ -55,8 +55,10 @@ load 477740-1.xhtml
 load 541620-1.xhtml
 load 557474-1.html
 load 654928-1.html
 load 655451-1.xhtml
 load 713606-1.html
 load 716349-1.html
 load 947557-1.html
 test-pref(layout.css.sticky.enabled,true) load 973322-1.xhtml
+load 848725-1.html
+load 848725-2.html
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -29,16 +29,20 @@
 #include <algorithm>
 
 #include "gfxMathTable.h"
 
 using namespace mozilla;
 
 //#define NOISY_SEARCH 1
 
+// BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
+// are installed. "kMaxScaleFactor" is required to limit the scale for the
+// vertical and horizontal stretchy operators.
+static const float kMaxScaleFactor = 20.0;
 static const float kLargeOpFactor = float(M_SQRT2);
 static const float kIntegralFactor = 2.0;
 
 // -----------------------------------------------------------------------------
 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
 
 // -----------------------------------------------------------------------------
 // nsGlyphTable is a class that provides an interface for accessing glyphs
@@ -1658,30 +1662,30 @@ nsMathMLChar::StretchInternal(nsPresCont
   if (glyphFound) {
     return NS_OK;
   }
 
   // stretchy character
   if (stretchy) {
     if (isVertical) {
       float scale =
-        float(aContainerSize.ascent + aContainerSize.descent) /
-        (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
+        std::min(kMaxScaleFactor, float(aContainerSize.ascent + aContainerSize.descent) /
+        (aDesiredStretchSize.ascent + aDesiredStretchSize.descent));
       if (!largeop || scale > 1.0) {
         // make the character match the desired height.
         if (!maxWidth) {
           mScaleY *= scale;
         }
         aDesiredStretchSize.ascent *= scale;
         aDesiredStretchSize.descent *= scale;
       }
     } else {
       float scale =
-        float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
-        (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
+        std::min(kMaxScaleFactor, float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
+        (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing));
       if (!largeop || scale > 1.0) {
         // make the character match the desired width.
         if (!maxWidth) {
           mScaleX *= scale;
         }
         aDesiredStretchSize.leftBearing *= scale;
         aDesiredStretchSize.rightBearing *= scale;
         aDesiredStretchSize.width *= scale;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-break/box-decoration-break-block-border-padding-ref.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Testcase for box-decoration-break:clone, block border+padding</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=988653">
+  <meta charset="utf-8">
+  <style type="text/css">
+    html,body {
+      color:black; background-color:white; font-size:16px; padding:10px; margin:0;
+    }
+
+    .inner {
+      border: 5px dashed blue;
+      height: 33px;
+      width: 40px;
+      background: lime;
+      background-clip: content-box;
+      margin-bottom: 12px;
+    }
+
+    .columns {
+      -moz-columns: 3;
+      width: 250px;