Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 04 Mar 2015 16:35:35 -0500
changeset 231901 56492f7244a92579eacc3d6a968b8ecdc90085e3
parent 231844 6b08ec560bec5c9ab282fb2de075cb2ea38b88e3 (current diff)
parent 231900 608776d6b5d67b377e30a7103d8920ef00048e61 (diff)
child 231908 a83cba22add905c36bc89ea4fc597a24d0071cd2
child 231919 8da6897c4aa137e06a173bd6eadc240cb8a85759
child 231990 8d5f10959b2c4ae498b4a9a3704a5859038190f5
push id28362
push userryanvm@gmail.com
push dateWed, 04 Mar 2015 21:35:51 +0000
treeherdermozilla-central@56492f7244a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.0a1
first release with
nightly linux32
56492f7244a9 / 39.0a1 / 20150305030206 / files
nightly linux64
56492f7244a9 / 39.0a1 / 20150305030206 / files
nightly mac
56492f7244a9 / 39.0a1 / 20150305030206 / files
nightly win32
56492f7244a9 / 39.0a1 / 20150305030206 / files
nightly win64
56492f7244a9 / 39.0a1 / 20150305030206 / 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. a=merge
b2g/dev/config/mozconfigs/macosx-universal/mulet
browser/components/loop/test/functional/test_1_browser_call.py
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -247,16 +247,18 @@ GetAccService()
 }
 
 /**
  * Return true if we're in a content process and not B2G.
  */
 inline bool
 IPCAccessibilityActive()
 {
+	// XXX reenable when crashes are fixed
+	return false;
 #ifdef MOZ_B2G
   return false;
 #else
   return XRE_GetProcessType() == GeckoProcessType_Content &&
     mozilla::Preferences::GetBool("accessibility.ipc_architecture.enabled", true);
 #endif
 }
 
--- a/b2g/dev/config/mozconfigs/linux64/mulet
+++ b/b2g/dev/config/mozconfigs/linux64/mulet
@@ -1,6 +1,7 @@
+MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
 ac_add_options --enable-application=b2g/dev
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
deleted file mode 100644
--- a/b2g/dev/config/mozconfigs/macosx-universal/mulet
+++ /dev/null
@@ -1,3 +0,0 @@
-. "$topsrcdir/browser/config/mozconfigs/macosx-universal/nightly"
-
-ac_add_options --enable-application=b2g/dev
--- a/b2g/dev/config/mozconfigs/macosx64/mulet
+++ b/b2g/dev/config/mozconfigs/macosx64/mulet
@@ -1,8 +1,10 @@
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_PACKAGE_TESTS=0
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-application=b2g/dev
 ac_add_options --disable-install-strip
 ac_add_options --enable-signmar
 ac_add_options --enable-profiling
 ac_add_options --enable-instruments
 ac_add_options --enable-dtrace
--- a/b2g/dev/config/mozconfigs/win32/mulet
+++ b/b2g/dev/config/mozconfigs/win32/mulet
@@ -1,6 +1,10 @@
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_L10N_CHECK=0
+MOZ_AUTOMATION_PACKAGE_TESTS=0
+MOZ_AUTOMATION_INSTALLER=0
 . "$topsrcdir/browser/config/mozconfigs/win32/nightly"
 
 ac_add_options --enable-application=b2g/dev
 
 # Include Firefox OS fonts.
 MOZTTDIR=$topsrcdir/moztt
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1158,21 +1158,20 @@ pref("browser.bookmarks.editDialog.first
 
 // Whether to use a panel that looks like an OS X sheet for customization
 #ifdef XP_MACOSX
 pref("toolbar.customization.usesheet", true);
 #else
 pref("toolbar.customization.usesheet", false);
 #endif
 
-// Disable Flash protected mode to reduce hang/crash rates.
-pref("dom.ipc.plugins.flash.disable-protected-mode", true);
+pref("dom.ipc.plugins.flash.disable-protected-mode", false);
 
 // Feature-disable the protected-mode auto-flip
-pref("browser.flash-protected-mode-flip.enable", true);
+pref("browser.flash-protected-mode-flip.enable", false);
 
 // Whether we've already flipped protected mode automatically
 pref("browser.flash-protected-mode-flip.done", false);
 
 #ifdef XP_MACOSX
 // On mac, the default pref is per-architecture
 pref("dom.ipc.plugins.enabled.i386", true);
 pref("dom.ipc.plugins.enabled.x86_64", true);
@@ -1196,17 +1195,16 @@ pref("security.sandbox.windows.log", fal
 // the plugin's nice file name, see: nsPluginTag::GetNiceFileName.
 // On windows these levels are:
 // 0 - no sandbox
 // 1 - sandbox with USER_NON_ADMIN access token level
 // 2 - a more strict sandbox, which might cause functionality issues
 // 3 - the strongest settings we seem to be able to use without breaking
 //     everything, but will definitely cause some functionality restrictions
 pref("dom.ipc.plugins.sandbox-level.default", 0);
-pref("dom.ipc.plugins.sandbox-level.flash", 1);
 
 #if defined(MOZ_CONTENT_SANDBOX)
 // This controls whether the Windows content process sandbox is using a more
 // strict sandboxing policy.  This will require a restart.
 pref("security.sandbox.windows.content.moreStrict", false);
 
 #if defined(MOZ_STACKWALKING)
 // This controls the depth of stack trace that is logged when Windows sandbox
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -3797,17 +3797,17 @@ function XULWidgetSingleWrapper(aWidgetI
     }
     return node.getAttribute("overflowedItem") == "true";
   });
 
   Object.freeze(this);
 }
 
 const LAZY_RESIZE_INTERVAL_MS = 200;
-const OVERFLOW_PANEL_HIDE_DELAY_MS = 500
+const OVERFLOW_PANEL_HIDE_DELAY_MS = 500;
 
 function OverflowableToolbar(aToolbarNode) {
   this._toolbar = aToolbarNode;
   this._collapsed = new Map();
   this._enabled = true;
 
   this._toolbar.setAttribute("overflowable", "true");
   let doc = this._toolbar.ownerDocument;
@@ -4192,23 +4192,23 @@ OverflowableToolbar.prototype = {
     if (aNode.getAttribute("overflowedItem") == "true") {
       return this._list;
     }
     return this._target;
   },
 
   _hideTimeoutId: null,
   _showWithTimeout: function() {
-    this.show();
-    let window = this._toolbar.ownerDocument.defaultView;
-    if (this._hideTimeoutId) {
-      window.clearTimeout(this._hideTimeoutId);
-      this._hideTimeoutId = null;
-    }
-    this._hideTimeoutId = window.setTimeout(() => {
-      if (!this._panel.firstChild.matches(":hover")) {
-        this._panel.hidePopup();
+    this.show().then(function () {
+      let window = this._toolbar.ownerDocument.defaultView;
+      if (this._hideTimeoutId) {
+        window.clearTimeout(this._hideTimeoutId);
       }
-    }, OVERFLOW_PANEL_HIDE_DELAY_MS);
+      this._hideTimeoutId = window.setTimeout(() => {
+        if (!this._panel.firstChild.matches(":hover")) {
+          this._panel.hidePopup();
+        }
+      }, OVERFLOW_PANEL_HIDE_DELAY_MS);
+    }.bind(this));
   },
 };
 
 CustomizableUIInternal.initialize();
--- a/browser/components/loop/test/functional/test_1_browser_call.py
+++ b/browser/components/loop/test/functional/test_1_browser_call.py
@@ -1,13 +1,13 @@
-from marionette_test import MarionetteTestCase
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException, StaleElementException
 # noinspection PyUnresolvedReferences
 from marionette_driver import Wait
+from marionette import MarionetteTestCase
 
 import os
 import sys
 import urlparse
 sys.path.insert(1, os.path.dirname(os.path.abspath(__file__)))
 
 import pyperclip
 
--- a/browser/components/loop/test/shared/frontend_tester.py
+++ b/browser/components/loop/test/shared/frontend_tester.py
@@ -1,9 +1,9 @@
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import NoSuchElementException
 import threading
 import SimpleHTTPServer
 import SocketServer
 import BaseHTTPServer
 import socket
 import urllib
 import urlparse
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -281,16 +281,16 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA
          export CXX="$CXX"
          export LD="$LD"
          export ARFLAGS="$ARFLAGS"
          export CPPFLAGS="$ICU_CPPFLAGS $CPPFLAGS"
          export CFLAGS="$ICU_CFLAGS"
          export CXXFLAGS="$ICU_CXXFLAGS"
          export LDFLAGS="$ICU_LDFLAGS $LDFLAGS"
          ac_configure_args="$ICU_BUILD_OPTS $ICU_CROSS_BUILD_OPT $ICU_LINK_OPTS $ICU_TARGET_OPT"
-         ac_configure_args="$ac_configure_args --disable-extras --disable-icuio --disable-layout --disable-tests --disable-samples"
+         ac_configure_args="$ac_configure_args --disable-extras --disable-icuio --disable-layout --disable-tests --disable-samples --disable-strict"
          AC_OUTPUT_SUBDIRS(intl/icu/source:intl/icu/target)
         ) || exit 1
     fi
 
 fi
 
 ])
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -44,17 +44,16 @@ SEARCH_PATHS = [
     'other-licenses/ply',
     'xpcom/idl-parser',
     'testing',
     'testing/taskcluster',
     'testing/xpcshell',
     'testing/web-platform',
     'testing/web-platform/harness',
     'testing/marionette/client',
-    'testing/marionette/client/marionette',
     'testing/marionette/transport',
     'testing/marionette/driver',
     'testing/mozbase/mozcrash',
     'testing/mozbase/mozdebug',
     'testing/mozbase/mozdevice',
     'testing/mozbase/mozfile',
     'testing/mozbase/mozhttpd',
     'testing/mozbase/mozlog',
--- a/build/mozconfig.win-common
+++ b/build/mozconfig.win-common
@@ -1,9 +1,12 @@
 # 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/.
 
 if [ "x$IS_NIGHTLY" = "xyes" ]; then
   MOZ_AUTOMATION_UPLOAD_SYMBOLS=1
   MOZ_AUTOMATION_UPDATE_PACKAGING=1
 fi
-MOZ_AUTOMATION_INSTALLER=1
+
+# Some builds (eg: Mulet) don't want the installer, so only set this if it
+# hasn't already been set.
+MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-1}
--- a/client.mk
+++ b/client.mk
@@ -350,17 +350,19 @@ CREATE_MOZCONFIG_JSON = $(shell $(TOPSRC
 # case the result is non empty, and allowing an override on the make command
 # line not running the command (using := $(shell) still runs the shell command).
 ifneq (,$(CREATE_MOZCONFIG_JSON))
 endif
 
 $(OBJDIR)/.mozconfig.json: $(call mkdir_deps,$(OBJDIR)) ;
 
 save-mozconfig: $(FOUND_MOZCONFIG)
+ifdef FOUND_MOZCONFIG
 	-cp $(FOUND_MOZCONFIG) $(OBJDIR)/.mozconfig
+endif
 
 configure:: $(configure-preqs)
 	@echo cd $(OBJDIR);
 	@echo $(CONFIGURE) $(CONFIGURE_ARGS)
 	@cd $(OBJDIR) && $(BUILD_PROJECT_ARG) $(CONFIGURE_ENV_ARGS) $(CONFIGURE) $(CONFIGURE_ARGS) \
 	  || ( echo '*** Fix above errors and then restart with\
                "$(MAKE) -f client.mk build"' && exit 1 )
 	@touch $(OBJDIR)/Makefile
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -230,16 +230,20 @@ IsDOMObject(JSObject* obj)
 {
   return IsDOMClass(js::GetObjectClass(obj));
 }
 
 #define UNWRAP_OBJECT(Interface, obj, value)                                 \
   mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
     mozilla::dom::Interface##Binding::NativeType>(obj, value)
 
+#define UNWRAP_WORKER_OBJECT(Interface, obj, value)                           \
+  UnwrapObject<prototypes::id::Interface##_workers,                           \
+    mozilla::dom::Interface##Binding_workers::NativeType>(obj, value)
+
 // Some callers don't want to set an exception when unwrapping fails
 // (for example, overload resolution uses unwrapping to tell what sort
 // of thing it's looking at).
 // U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
 template <class T, typename U>
 MOZ_ALWAYS_INLINE nsresult
 UnwrapObject(JSObject* obj, U& value, prototypes::ID protoID,
              uint32_t protoDepth)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1499,16 +1499,24 @@ DOMInterfaces = {
 'Worker': {
     'headerFile': 'mozilla/dom/WorkerPrivate.h',
     'nativeType': 'mozilla::dom::workers::WorkerPrivate',
     'implicitJSContext': [
         'terminate',
     ],
 },
 
+'WorkerDebuggerGlobalScope': {
+    'headerFile': 'mozilla/dom/WorkerScope.h',
+    'nativeType': 'mozilla::dom::workers::WorkerDebuggerGlobalScope',
+    'implicitJSContext': [
+        'dump', 'global',
+    ],
+},
+
 'WorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'workers': True,
     'concrete': False,
     'implicitJSContext': [
         'close', 'importScripts',
     ],
     # Rename a few things so we don't have both classes and methods
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -110,17 +110,19 @@ CallbackObject::CallSetup::CallSetup(Cal
         // No DOM Window. Store the global and use the SafeJSContext.
         JSObject* glob = js::GetGlobalForObjectCrossCompartment(realCallback);
         globalObject = xpc::NativeGlobal(glob);
         MOZ_ASSERT(globalObject);
         cx = nsContentUtils::GetSafeJSContext();
       }
     } else {
       cx = workers::GetCurrentThreadJSContext();
-      globalObject = workers::GetCurrentThreadWorkerPrivate()->GlobalScope();
+      JSObject *global = js::GetGlobalForObjectCrossCompartment(realCallback);
+      globalObject = workers::GetGlobalObjectForGlobal(global);
+      MOZ_ASSERT(globalObject);
     }
 
     // Bail out if there's no useful global. This seems to happen intermittently
     // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
     // null in some kind of teardown state.
     if (!globalObject->GetGlobalJSObject()) {
       return;
     }
--- a/dom/cache/ActorChild.cpp
+++ b/dom/cache/ActorChild.cpp
@@ -10,17 +10,24 @@
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 void
 ActorChild::SetFeature(Feature* aFeature)
 {
-  MOZ_ASSERT(!mFeature);
+  // Some of the Cache actors can have multiple DOM objects associated with
+  // them.  In this case the feature will be added multiple times.  This is
+  // permitted, but the feature should be the same each time.
+  if (mFeature) {
+    MOZ_ASSERT(mFeature == aFeature);
+    return;
+  }
+
   mFeature = aFeature;
   if (mFeature) {
     mFeature->AddActor(this);
   }
 }
 
 void
 ActorChild::RemoveFeature()
--- a/dom/cache/test/mochitest/test_cache_add.js
+++ b/dom/cache/test/mochitest/test_cache_add.js
@@ -34,13 +34,19 @@ caches.open('adder').then(function(openC
   });
   promiseList.push(cache.match(singleUrl));
   return Promise.all(promiseList);
 }).then(function(resultList) {
   is(urlList.length + 1, resultList.length, 'Expected number of results');
   resultList.every(function(result) {
     ok(!!result, 'Responses should now be in cache for each URL.');
   });
+  return cache.matchAll();
+}).then(function(resultList) {
+  is(urlList.length + 1, resultList.length, 'Expected number of results');
+  resultList.every(function(result) {
+    ok(!!result, 'Responses should now be in cache for each URL.');
+  });
   workerTestDone();
 }).catch(function(err) {
   ok(false, 'Caught error: ' + err);
   workerTestDone();
 });
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -650,17 +650,19 @@ FetchDriver::OnStartRequest(nsIRequest* 
   // pipe has infinite space. The nsIChannel will continue to buffer data in
   // xpcom events even if we block on a fixed size pipe.  It might be possible
   // to suspend the channel and then resume when there is space available, but
   // for now use an infinite pipe to avoid blocking.
   nsCOMPtr<nsIInputStream> pipeInputStream;
   rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                   getter_AddRefs(mPipeOutputStream),
                   0, /* default segment size */
-                  UINT32_MAX /* infinite pipe */);
+                  UINT32_MAX /* infinite pipe */,
+                  true /* non-blocking input, otherwise you deadlock */,
+                  false /* blocking output, since the pipe is 'in'finite */ );
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
   }
 
   mResponse->SetBody(pipeInputStream);
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1161,17 +1161,19 @@ nsresult HTMLMediaElement::LoadResource(
   if (NS_CP_REJECTED(shouldLoad)) {
     return NS_ERROR_FAILURE;
   }
 
   // Set the media element's CORS mode only when loading a resource
   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
 
 #ifdef MOZ_EME
-  if (mMediaKeys && !IsMediaStreamURI(mLoadingSrc)) {
+  if (mMediaKeys &&
+      !IsMediaStreamURI(mLoadingSrc) &&
+      Preferences::GetBool("media.eme.mse-only", true)) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 #endif
 
   HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
   if (other && other->mDecoder) {
     // Clone it.
     nsresult rv = InitializeDecoderAsClone(other->mDecoder);
@@ -3288,17 +3290,17 @@ void HTMLMediaElement::CheckProgress(boo
         ChangeDelayLoadStatus(true);
       }
     }
   }
 
   if (now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
     DispatchAsyncEvent(NS_LITERAL_STRING("stalled"));
 
-    if (IsMediaSourceURI(mLoadingSrc)) {
+    if (mLoadingSrc && IsMediaSourceURI(mLoadingSrc)) {
       ChangeDelayLoadStatus(false);
     }
 
     NS_ASSERTION(mProgressTimer, "detected stalled without timer");
     // Stop timer events, which prevents repeated stalled events until there
     // is more progress.
     StopProgress();
   }
@@ -4361,17 +4363,19 @@ HTMLMediaElement::SetMediaKeys(mozilla::
     promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return promise.forget();
   }
   if (mMediaKeys) {
     // Existing MediaKeys object. Shut it down.
     mMediaKeys->Shutdown();
     mMediaKeys = nullptr;
   }
-  if (mDecoder && !mMediaSource) {
+  if (mDecoder &&
+      !mMediaSource &&
+      Preferences::GetBool("media.eme.mse-only", true)) {
     ShutdownDecoder();
     promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return promise.forget();
   }
 
   mMediaKeys = aMediaKeys;
   if (mMediaKeys) {
     if (NS_FAILED(mMediaKeys->Bind(this))) {
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -182,17 +182,17 @@ GMPVideoDecoderParent::Drain()
   return NS_OK;
 }
 
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 nsresult
 GMPVideoDecoderParent::Shutdown()
 {
   LOGD(("%s: %p", __FUNCTION__, this));
-  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+  MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (mShuttingDown) {
     return NS_OK;
   }
   mShuttingDown = true;
 
   // Notify client we're gone!  Won't occur after Close()
   if (mCallback) {
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -40,21 +40,16 @@
 #include "VideoFrameContainer.h"
 #include "VideoUtils.h"
 
 using namespace android;
 using namespace mozilla::layers;
 
 namespace mozilla {
 
-enum {
-  kNotifyCodecReserved = 'core',
-  kNotifyCodecCanceled = 'coca',
-};
-
 static const int64_t sInvalidDurationUs = INT64_C(-1);
 static const int64_t sInvalidTimestampUs = INT64_C(-1);
 
 // Try not to spend more than this much time (in seconds) in a single call
 // to GetCodecOutputData.
 static const double sMaxAudioDecodeDurationS = 0.1;
 static const double sMaxVideoDecodeDurationS = 0.1;
 
@@ -67,59 +62,40 @@ IsValidDurationUs(int64_t aDuration)
 }
 
 inline bool
 IsValidTimestampUs(int64_t aTimestamp)
 {
   return aTimestamp >= INT64_C(0);
 }
 
-MediaCodecReader::MessageHandler::MessageHandler(MediaCodecReader* aReader)
-  : mReader(aReader)
-{
-}
-
-MediaCodecReader::MessageHandler::~MessageHandler()
-{
-  mReader = nullptr;
-}
-
-void
-MediaCodecReader::MessageHandler::onMessageReceived(
-  const sp<AMessage>& aMessage)
-{
-  if (mReader) {
-    mReader->onMessageReceived(aMessage);
-  }
-}
-
 MediaCodecReader::VideoResourceListener::VideoResourceListener(
   MediaCodecReader* aReader)
   : mReader(aReader)
 {
 }
 
 MediaCodecReader::VideoResourceListener::~VideoResourceListener()
 {
   mReader = nullptr;
 }
 
 void
 MediaCodecReader::VideoResourceListener::codecReserved()
 {
   if (mReader) {
-    mReader->codecReserved(mReader->mVideoTrack);
+    mReader->VideoCodecReserved();
   }
 }
 
 void
 MediaCodecReader::VideoResourceListener::codecCanceled()
 {
   if (mReader) {
-    mReader->codecCanceled(mReader->mVideoTrack);
+    mReader->VideoCodecCanceled();
   }
 }
 
 MediaCodecReader::TrackInputCopier::~TrackInputCopier()
 {
 }
 
 bool
@@ -295,17 +271,16 @@ MediaCodecReader::MediaCodecReader(Abstr
   , mIsWaitingResources(false)
   , mTextureClientIndexesLock("MediaCodecReader::mTextureClientIndexesLock")
   , mColorConverterBufferSize(0)
   , mParserMonitor("MediaCodecReader::mParserMonitor")
   , mParseDataFromCache(true)
   , mNextParserPosition(INT64_C(0))
   , mParsedDataLength(INT64_C(0))
 {
-  mHandler = new MessageHandler(this);
   mVideoListener = new VideoResourceListener(this);
 }
 
 MediaCodecReader::~MediaCodecReader()
 {
 }
 
 nsresult
@@ -694,16 +669,24 @@ MediaCodecReader::ReadMetadata(MediaInfo
   // relies on IsWaitingMediaResources() function. And the waiting state will be
   // changed by binder thread, so we store the waiting state in a cache value to
   // make them in the same waiting state.
   UpdateIsWaitingMediaResources();
   if (IsWaitingMediaResources()) {
     return NS_OK;
   }
 
+  // Configure video codec after the codecReserved.
+  if (mVideoTrack.mSource != nullptr) {
+    if (!ConfigureMediaCodec(mVideoTrack)) {
+      DestroyMediaCodec(mVideoTrack);
+      return NS_ERROR_FAILURE;
+    }
+  }
+
   // TODO: start streaming
 
   if (!UpdateDuration()) {
     return NS_ERROR_FAILURE;
   }
 
   if (!UpdateAudioInfo()) {
     return NS_ERROR_FAILURE;
@@ -1098,18 +1081,18 @@ MediaCodecReader::GetAudioOffloadTrack()
 }
 
 bool
 MediaCodecReader::ReallocateResources()
 {
   if (CreateLooper() &&
       CreateExtractor() &&
       CreateMediaSources() &&
-      CreateMediaCodecs() &&
-      CreateTaskQueues()) {
+      CreateTaskQueues() &&
+      CreateMediaCodecs()) {
     return true;
   }
 
   ReleaseResources();
   return false;
 }
 
 void
@@ -1145,19 +1128,16 @@ MediaCodecReader::CreateLooper()
   if (mLooper != nullptr) {
     return true;
   }
 
   // Create ALooper
   sp<ALooper> looper = new ALooper;
   looper->setName("MediaCodecReader::mLooper");
 
-  // Register AMessage handler to ALooper.
-  looper->registerHandler(mHandler);
-
   // Start ALooper thread.
   if (looper->start() != OK) {
     return false;
   }
 
   mLooper = looper;
 
   return true;
@@ -1165,21 +1145,16 @@ MediaCodecReader::CreateLooper()
 
 void
 MediaCodecReader::DestroyLooper()
 {
   if (mLooper == nullptr) {
     return;
   }
 
-  // Unregister AMessage handler from ALooper.
-  if (mHandler != nullptr) {
-    mLooper->unregisterHandler(mHandler->id());
-  }
-
   // Stop ALooper thread.
   mLooper->stop();
 
   // Clear ALooper
   mLooper = nullptr;
 }
 
 bool
@@ -1299,23 +1274,21 @@ MediaCodecReader::ShutdownTaskQueues()
     mVideoTrack.mReleaseBufferTaskQueue->AwaitShutdownAndIdle();
     mVideoTrack.mReleaseBufferTaskQueue = nullptr;
   }
 }
 
 bool
 MediaCodecReader::CreateTaskQueues()
 {
-  if (mAudioTrack.mSource != nullptr && mAudioTrack.mCodec != nullptr &&
-      !mAudioTrack.mTaskQueue) {
+  if (mAudioTrack.mSource != nullptr && !mAudioTrack.mTaskQueue) {
     mAudioTrack.mTaskQueue = CreateFlushableMediaDecodeTaskQueue();
     NS_ENSURE_TRUE(mAudioTrack.mTaskQueue, false);
   }
-  if (mVideoTrack.mSource != nullptr && mVideoTrack.mCodec != nullptr &&
-      !mVideoTrack.mTaskQueue) {
+  if (mVideoTrack.mSource != nullptr && !mVideoTrack.mTaskQueue) {
     mVideoTrack.mTaskQueue = CreateFlushableMediaDecodeTaskQueue();
     NS_ENSURE_TRUE(mVideoTrack.mTaskQueue, false);
     mVideoTrack.mReleaseBufferTaskQueue = CreateMediaDecodeTaskQueue();
     NS_ENSURE_TRUE(mVideoTrack.mReleaseBufferTaskQueue, false);
   }
 
   return true;
 }
@@ -1938,64 +1911,27 @@ MediaCodecReader::GetColorConverterBuffe
 
 void
 MediaCodecReader::ClearColorConverterBuffer()
 {
   mColorConverterBuffer = nullptr;
   mColorConverterBufferSize = 0;
 }
 
-// Called on MediaCodecReader::mLooper thread.
+// Called on Binder thread.
 void
-MediaCodecReader::onMessageReceived(const sp<AMessage>& aMessage)
+MediaCodecReader::VideoCodecReserved()
 {
-  switch (aMessage->what()) {
-
-    case kNotifyCodecReserved:
-    {
-      // Our decode may have acquired the hardware resource that it needs
-      // to start. Notify the state machine to resume loading metadata.
-      mDecoder->NotifyWaitingForResourcesStatusChanged();
-      break;
-    }
-
-    case kNotifyCodecCanceled:
-    {
-      ReleaseCriticalResources();
-      break;
-    }
-
-    default:
-      TRESPASS();
-      break;
-  }
+  mDecoder->NotifyWaitingForResourcesStatusChanged();
 }
 
 // Called on Binder thread.
 void
-MediaCodecReader::codecReserved(Track& aTrack)
+MediaCodecReader::VideoCodecCanceled()
 {
-  if (!ConfigureMediaCodec(aTrack)) {
-    DestroyMediaCodec(aTrack);
-    return;
-  }
-
-  if (mHandler != nullptr) {
-    // post kNotifyCodecReserved to MediaCodecReader::mLooper thread.
-    sp<AMessage> notify = new AMessage(kNotifyCodecReserved, mHandler->id());
-    notify->post();
-  }
-}
-
-// Called on Binder thread.
-void
-MediaCodecReader::codecCanceled(Track& aTrack)
-{
-  DestroyMediaCodec(aTrack);
-
-  if (mHandler != nullptr) {
-    // post kNotifyCodecCanceled to MediaCodecReader::mLooper thread.
-    sp<AMessage> notify = new AMessage(kNotifyCodecCanceled, mHandler->id());
-    notify->post();
+  if (mVideoTrack.mTaskQueue) {
+    RefPtr<nsIRunnable> task =
+      NS_NewRunnableMethod(this, &MediaCodecReader::ReleaseCriticalResources);
+    mVideoTrack.mTaskQueue->Dispatch(task);
   }
 }
 
 } // namespace mozilla
--- a/dom/media/omx/MediaCodecReader.h
+++ b/dom/media/omx/MediaCodecReader.h
@@ -174,50 +174,31 @@ protected:
   };
 
   // Receive a message from MessageHandler.
   // Called on MediaCodecReader::mLooper thread.
   void onMessageReceived(const android::sp<android::AMessage>& aMessage);
 
   // Receive a notify from ResourceListener.
   // Called on Binder thread.
-  virtual void codecReserved(Track& aTrack);
-  virtual void codecCanceled(Track& aTrack);
+  virtual void VideoCodecReserved();
+  virtual void VideoCodecCanceled();
 
   virtual bool CreateExtractor();
 
   // Check the underlying HW resource is available and store the result in
   // mIsWaitingResources.
   void UpdateIsWaitingMediaResources();
 
   android::sp<android::MediaExtractor> mExtractor;
   // A cache value updated by UpdateIsWaitingMediaResources(), makes the
   // "waiting resources state" is synchronous to StateMachine.
   bool mIsWaitingResources;
 
 private:
-  // An intermediary class that can be managed by android::sp<T>.
-  // Redirect onMessageReceived() to MediaCodecReader.
-  class MessageHandler : public android::AHandler
-  {
-  public:
-    MessageHandler(MediaCodecReader* aReader);
-    ~MessageHandler();
-
-    virtual void onMessageReceived(const android::sp<android::AMessage>& aMessage);
-
-  private:
-    // Forbidden
-    MessageHandler() = delete;
-    MessageHandler(const MessageHandler& rhs) = delete;
-    const MessageHandler& operator=(const MessageHandler& rhs) = delete;
-
-    MediaCodecReader *mReader;
-  };
-  friend class MessageHandler;
 
   // An intermediary class that can be managed by android::sp<T>.
   // Redirect codecReserved() and codecCanceled() to MediaCodecReader.
   class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
   {
   public:
     VideoResourceListener(MediaCodecReader* aReader);
     ~VideoResourceListener();
@@ -427,17 +408,16 @@ private:
   static PLDHashOperator ReleaseTextureClient(TextureClient* aClient,
                                               size_t& aIndex,
                                               void* aUserArg);
   PLDHashOperator ReleaseTextureClient(TextureClient* aClient,
                                        size_t& aIndex);
 
   void ReleaseAllTextureClients();
 
-  android::sp<MessageHandler> mHandler;
   android::sp<VideoResourceListener> mVideoListener;
 
   android::sp<android::ALooper> mLooper;
   android::sp<android::MetaData> mMetaData;
 
   Mutex mTextureClientIndexesLock;
   nsDataHashtable<nsPtrHashKey<TextureClient>, size_t> mTextureClientIndexes;
 
--- a/dom/webidl/EventTarget.webidl
+++ b/dom/webidl/EventTarget.webidl
@@ -5,17 +5,17 @@
  *
  * The origin of this IDL file is
  * http://www.w3.org/TR/2012/WD-dom-20120105/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Exposed=(Window,Worker,System)]
+[Exposed=(Window,Worker,WorkerDebugger,System)]
 interface EventTarget {
   /* Passing null for wantsUntrusted means "default behavior", which
      differs in content and chrome.  In content that default boolean
      value is true, while in chrome the default boolean value is
      false. */
   [Throws]
   void addEventListener(DOMString type,
                         EventListener? listener,
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WorkerDebuggerGlobalScope.webidl
@@ -0,0 +1,14 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[Global=(WorkerDebugger), Exposed=WorkerDebugger]
+interface WorkerDebuggerGlobalScope : EventTarget {
+  readonly attribute object global;
+};
+
+// So you can debug while you debug
+partial interface WorkerDebuggerGlobalScope {
+  void dump(optional DOMString string);
+};
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -7,17 +7,17 @@
  * http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html#the-workerglobalscope-common-interface
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and Opera
  * Software ASA.
  * You are granted a license to use, reproduce and create derivative works of
  * this document.
  */
 
-[Exposed=Worker]
+[Exposed=(Worker)]
 interface WorkerGlobalScope : EventTarget {
   readonly attribute WorkerGlobalScope self;
 
   [Replaceable]
   readonly attribute Console console;
 
   readonly attribute WorkerLocation location;
 
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -544,16 +544,17 @@ WEBIDL_FILES = [
     'WebComponents.webidl',
     'WebGL2RenderingContext.webidl',
     'WebGLRenderingContext.webidl',
     'WebSocket.webidl',
     'WheelEvent.webidl',
     'WifiOptions.webidl',
     'WindowRoot.webidl',
     'Worker.webidl',
+    'WorkerDebuggerGlobalScope.webidl',
     'WorkerGlobalScope.webidl',
     'WorkerLocation.webidl',
     'WorkerNavigator.webidl',
     'XMLDocument.webidl',
     'XMLHttpRequest.webidl',
     'XMLHttpRequestEventTarget.webidl',
     'XMLHttpRequestUpload.webidl',
     'XMLSerializer.webidl',
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -835,29 +835,81 @@ PreserveWrapper(JSContext *cx, JSObject 
 {
     MOZ_ASSERT(cx);
     MOZ_ASSERT(obj);
     MOZ_ASSERT(mozilla::dom::IsDOMObject(obj));
 
     return mozilla::dom::TryPreserveWrapper(obj);
 }
 
+class DebuggeeGlobalSecurityWrapper : public js::CrossCompartmentSecurityWrapper {
+public:
+  DebuggeeGlobalSecurityWrapper()
+  : js::CrossCompartmentSecurityWrapper(CROSS_COMPARTMENT, false)
+  {
+  }
+
+  bool enter(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
+             js::Wrapper::Action act, bool* bp) const
+  {
+    *bp = false;
+    return false;
+  }
+
+  static const DebuggeeGlobalSecurityWrapper singleton;
+};
+
+const DebuggeeGlobalSecurityWrapper DebuggeeGlobalSecurityWrapper::singleton;
+
+JSObject*
+Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj)
+{
+  JSObject* targetGlobal = JS::CurrentGlobalOrNull(cx);
+  if (!IsDebuggerGlobal(targetGlobal)) {
+    MOZ_CRASH("There should be no edges from the debuggee to the debugger.");
+  }
+
+  JSObject* originGlobal = js::GetGlobalForObjectCrossCompartment(obj);
+
+  const js::Wrapper* wrapper = nullptr;
+  if (IsDebuggerGlobal(originGlobal)) {
+    wrapper = &js::CrossCompartmentWrapper::singleton;
+  } else {
+    if (obj != originGlobal) {
+      MOZ_CRASH("The should be only edges from the debugger to the debuggee global.");
+    }
+
+    wrapper = &DebuggeeGlobalSecurityWrapper::singleton;
+  }
+
+  if (existing) {
+    js::Wrapper::Renew(cx, existing, obj, wrapper);
+  }
+  return js::Wrapper::New(cx, obj, wrapper);
+}
+
+static const JSWrapObjectCallbacks WrapObjectCallbacks = {
+  Wrap,
+  nullptr,
+};
+
 class WorkerJSRuntime : public mozilla::CycleCollectedJSRuntime
 {
 public:
   // The heap size passed here doesn't matter, we will change it later in the
   // call to JS_SetGCParameter inside CreateJSContextForWorker.
   WorkerJSRuntime(JSRuntime* aParentRuntime, WorkerPrivate* aWorkerPrivate)
     : CycleCollectedJSRuntime(aParentRuntime,
                               WORKER_DEFAULT_RUNTIME_HEAPSIZE,
                               WORKER_DEFAULT_NURSERY_SIZE),
     mWorkerPrivate(aWorkerPrivate)
   {
     js::SetPreserveWrapperCallback(Runtime(), PreserveWrapper);
     JS_InitDestroyPrincipalsCallback(Runtime(), DestroyWorkerPrincipals);
+    JS_SetWrapObjectCallbacks(Runtime(), &WrapObjectCallbacks);
   }
 
   ~WorkerJSRuntime()
   {
     auto rtPrivate = static_cast<WorkerThreadRuntimePrivate*>(JS_GetRuntimePrivate(Runtime()));
     delete rtPrivate;
     JS_SetRuntimePrivate(Runtime(), nullptr);
 
@@ -2223,17 +2275,17 @@ RuntimeService::CreateServiceWorker(cons
   serviceWorker->mURL = aScriptURL;
 
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
 
 nsresult
 RuntimeService::CreateServiceWorkerFromLoadInfo(JSContext* aCx,
-                                               WorkerPrivate::LoadInfo* aLoadInfo,
+                                               WorkerLoadInfo* aLoadInfo,
                                                const nsAString& aScriptURL,
                                                const nsACString& aScope,
                                                ServiceWorker** aServiceWorker)
 {
 
   nsRefPtr<SharedWorker> sharedWorker;
   nsresult rv = CreateSharedWorkerFromLoadInfo(aCx, aLoadInfo, aScriptURL, aScope,
                                                WorkerTypeService,
@@ -2262,30 +2314,30 @@ RuntimeService::CreateSharedWorkerIntern
   AssertIsOnMainThread();
   MOZ_ASSERT(aType == WorkerTypeShared || aType == WorkerTypeService);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(window);
 
   JSContext* cx = aGlobal.Context();
 
-  WorkerPrivate::LoadInfo loadInfo;
+  WorkerLoadInfo loadInfo;
   nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
                                            false,
                                            WorkerPrivate::OverrideLoadGroup,
                                            &loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName, aType,
                                         aSharedWorker);
 }
 
 nsresult
 RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx,
-                                               WorkerPrivate::LoadInfo* aLoadInfo,
+                                               WorkerLoadInfo* aLoadInfo,
                                                const nsAString& aScriptURL,
                                                const nsACString& aName,
                                                WorkerType aType,
                                                SharedWorker** aSharedWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aLoadInfo);
   MOZ_ASSERT(aLoadInfo->mResolvedScriptURI);
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -3,25 +3,23 @@
 /* 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 mozilla_dom_workers_runtimeservice_h__
 #define mozilla_dom_workers_runtimeservice_h__
 
 #include "Workers.h"
-#include "WorkerPrivate.h" // For the WorkerType enum.
 
 #include "nsIObserver.h"
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
-#include "WorkerPrivate.h"
 
 class nsIRunnable;
 class nsITimer;
 class nsPIDOMWindow;
 
 BEGIN_WORKERS_NAMESPACE
 
 class ServiceWorker;
@@ -150,17 +148,17 @@ public:
   nsresult
   CreateServiceWorker(const GlobalObject& aGlobal,
                       const nsAString& aScriptURL,
                       const nsACString& aScope,
                       ServiceWorker** aServiceWorker);
 
   nsresult
   CreateServiceWorkerFromLoadInfo(JSContext* aCx,
-                                  WorkerPrivate::LoadInfo* aLoadInfo,
+                                  WorkerLoadInfo* aLoadInfo,
                                   const nsAString& aScriptURL,
                                   const nsACString& aScope,
                                   ServiceWorker** aServiceWorker);
 
   void
   ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
 
   const NavigatorProperties&
@@ -303,17 +301,17 @@ private:
   CreateSharedWorkerInternal(const GlobalObject& aGlobal,
                              const nsAString& aScriptURL,
                              const nsACString& aName,
                              WorkerType aType,
                              SharedWorker** aSharedWorker);
 
   nsresult
   CreateSharedWorkerFromLoadInfo(JSContext* aCx,
-                                 WorkerPrivate::LoadInfo* aLoadInfo,
+                                 WorkerLoadInfo* aLoadInfo,
                                  const nsAString& aScriptURL,
                                  const nsACString& aName,
                                  WorkerType aType,
                                  SharedWorker** aSharedWorker);
 };
 
 END_WORKERS_NAMESPACE
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2458,17 +2458,17 @@ NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal,
                                           const nsACString& aScriptSpec,
                                           const nsACString& aScope,
                                           ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
 
-  WorkerPrivate::LoadInfo info;
+  WorkerLoadInfo info;
   nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aScriptSpec, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   info.mResolvedScriptURI = info.mBaseURI;
 
   rv = info.mBaseURI->GetHost(info.mDomain);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -52,16 +52,18 @@
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredClone.h"
 #include "mozilla/dom/WorkerBinding.h"
+#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
+#include "mozilla/dom/WorkerGlobalScopeBinding.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Preferences.h"
 #include "MultipartFileImpl.h"
@@ -886,23 +888,24 @@ public:
   explicit CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
   { }
 
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
-    JS::Rooted<JSObject*> global(aCx,
-      aWorkerPrivate->CreateGlobalScope(aCx));
-    if (!global) {
+    WorkerGlobalScope* globalScope =
+      aWorkerPrivate->GetOrCreateGlobalScope(aCx);
+    if (!globalScope) {
       NS_WARNING("Failed to make global!");
       return false;
     }
 
+    JS::Rooted<JSObject*> global(aCx, globalScope->GetWrapper());
     JSAutoCompartment ac(aCx, global);
     bool result = scriptloader::LoadWorkerScript(aCx);
     if (result) {
       aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
     }
     return result;
   }
 };
@@ -1234,39 +1237,49 @@ public:
         if (status == nsEventStatus_eConsumeNoDefault) {
           return true;
         }
       }
 
       // Now fire an event at the global object, but don't do that if the error
       // code is too much recursion and this is the same script threw the error.
       if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
-        JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
-        NS_ASSERTION(target, "This should never be null!");
+        JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+        NS_ASSERTION(global, "This should never be null!");
 
         nsEventStatus status = nsEventStatus_eIgnore;
         nsIScriptGlobalObject* sgo;
 
         if (aWorkerPrivate) {
-          WorkerGlobalScope* globalTarget = aWorkerPrivate->GlobalScope();
-          MOZ_ASSERT(target == globalTarget->GetWrapperPreserveColor());
+          nsIDOMEventTarget* target = nullptr;
+          WorkerGlobalScope* globalScope = nullptr;
+          UNWRAP_WORKER_OBJECT(WorkerGlobalScope, global, globalScope);
+          if (globalScope) {
+            MOZ_ASSERT(global == globalScope->GetWrapperPreserveColor());
+            target = static_cast<nsIDOMEventTarget*>(globalScope);
+          } else {
+            WorkerDebuggerGlobalScope* globalScope = nullptr;
+            UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalScope);
+            MOZ_ASSERT(globalScope);
+            MOZ_ASSERT(global == globalScope->GetWrapperPreserveColor());
+            target = static_cast<nsIDOMEventTarget*>(globalScope);
+          }
 
           nsRefPtr<ErrorEvent> event =
             ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
           event->SetTrusted(true);
 
-          nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalTarget);
           if (NS_FAILED(EventDispatcher::DispatchDOMEvent(target, nullptr,
                                                           event, nullptr,
                                                           &status))) {
             NS_WARNING("Failed to dispatch worker thread error event!");
             status = nsEventStatus_eIgnore;
           }
         }
-        else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
+        else if ((sgo = nsJSUtils::GetStaticScriptGlobal(global))) {
           MOZ_ASSERT(NS_IsMainThread());
 
           if (NS_FAILED(sgo->HandleScriptError(init, &status))) {
             NS_WARNING("Failed to dispatch main thread error event!");
             status = nsEventStatus_eIgnore;
           }
         }
 
@@ -1915,38 +1928,135 @@ PRThreadFromThread(nsIThread* aThread)
 } /* anonymous namespace */
 
 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
 
-template <class Derived>
-WorkerPrivateParent<Derived>::
-LoadInfo::LoadInfo()
+WorkerLoadInfo::WorkerLoadInfo()
   : mWindowID(UINT64_MAX)
   , mFromWindow(false)
   , mEvalAllowed(false)
   , mReportCSPViolations(false)
   , mXHRParamsAllowed(false)
   , mPrincipalIsSystem(false)
   , mIsInPrivilegedApp(false)
   , mIsInCertifiedApp(false)
   , mIndexedDBAllowed(false)
 {
-  MOZ_COUNT_CTOR(WorkerPrivateParent<Derived>::LoadInfo);
+  MOZ_COUNT_CTOR(WorkerLoadInfo);
+}
+
+WorkerLoadInfo::~WorkerLoadInfo()
+{
+  MOZ_COUNT_DTOR(WorkerLoadInfo);
+}
+
+void
+WorkerLoadInfo::StealFrom(WorkerLoadInfo& aOther)
+{
+  MOZ_ASSERT(!mBaseURI);
+  aOther.mBaseURI.swap(mBaseURI);
+
+  MOZ_ASSERT(!mResolvedScriptURI);
+  aOther.mResolvedScriptURI.swap(mResolvedScriptURI);
+
+  MOZ_ASSERT(!mPrincipal);
+  aOther.mPrincipal.swap(mPrincipal);
+
+  MOZ_ASSERT(!mScriptContext);
+  aOther.mScriptContext.swap(mScriptContext);
+
+  MOZ_ASSERT(!mWindow);
+  aOther.mWindow.swap(mWindow);
+
+  MOZ_ASSERT(!mCSP);
+  aOther.mCSP.swap(mCSP);
+
+  MOZ_ASSERT(!mChannel);
+  aOther.mChannel.swap(mChannel);
+
+  MOZ_ASSERT(!mLoadGroup);
+  aOther.mLoadGroup.swap(mLoadGroup);
+
+  MOZ_ASSERT(!mInterfaceRequestor);
+  aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
+
+  MOZ_ASSERT(!mPrincipalInfo);
+  mPrincipalInfo = aOther.mPrincipalInfo.forget();
+
+  mDomain = aOther.mDomain;
+  mWindowID = aOther.mWindowID;
+  mFromWindow = aOther.mFromWindow;
+  mEvalAllowed = aOther.mEvalAllowed;
+  mReportCSPViolations = aOther.mReportCSPViolations;
+  mXHRParamsAllowed = aOther.mXHRParamsAllowed;
+  mPrincipalIsSystem = aOther.mPrincipalIsSystem;
+  mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
+  mIsInCertifiedApp = aOther.mIsInCertifiedApp;
+  mIndexedDBAllowed = aOther.mIndexedDBAllowed;
 }
 
 template <class Derived>
-WorkerPrivateParent<Derived>::
-LoadInfo::~LoadInfo()
-{
-  MOZ_COUNT_DTOR(WorkerPrivateParent<Derived>::LoadInfo);
-}
+class WorkerPrivateParent<Derived>::EventTarget MOZ_FINAL
+  : public nsIEventTarget
+{
+  // This mutex protects mWorkerPrivate and must be acquired *before* the
+  // WorkerPrivate's mutex whenever they must both be held.
+  mozilla::Mutex mMutex;
+  WorkerPrivate* mWorkerPrivate;
+  nsIEventTarget* mWeakNestedEventTarget;
+  nsCOMPtr<nsIEventTarget> mNestedEventTarget;
+
+public:
+  explicit EventTarget(WorkerPrivate* aWorkerPrivate)
+  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
+    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+  }
+
+  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
+  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
+    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
+    mNestedEventTarget(aNestedEventTarget)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aNestedEventTarget);
+  }
+
+  void
+  Disable()
+  {
+    nsCOMPtr<nsIEventTarget> nestedEventTarget;
+    {
+      MutexAutoLock lock(mMutex);
+
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate = nullptr;
+      mNestedEventTarget.swap(nestedEventTarget);
+    }
+  }
+
+  nsIEventTarget*
+  GetWeakNestedEventTarget() const
+  {
+    MOZ_ASSERT(mWeakNestedEventTarget);
+    return mWeakNestedEventTarget;
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIEVENTTARGET
+
+private:
+  ~EventTarget()
+  { }
+};
 
 template <class Derived>
 class WorkerPrivateParent<Derived>::SynchronizeAndResumeRunnable MOZ_FINAL
   : public nsRunnable
 {
   friend class nsRevocableEventPtr<SynchronizeAndResumeRunnable>;
 
   WorkerPrivate* mWorkerPrivate;
@@ -1994,194 +2104,120 @@ private:
     MOZ_ASSERT(mWorkerPrivate);
     MOZ_ASSERT(mWindow);
 
     mWorkerPrivate = nullptr;
     mWindow = nullptr;
   }
 };
 
-template <class Derived>
-class WorkerPrivateParent<Derived>::InterfaceRequestor MOZ_FINAL
-  : public nsIInterfaceRequestor
-{
-  NS_DECL_ISUPPORTS
-
-public:
-  InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(aPrincipal);
-
-    // Look for an existing LoadContext.  This is optional and it's ok if
-    // we don't find one.
-    nsCOMPtr<nsILoadContext> baseContext;
-    if (aLoadGroup) {
-      nsCOMPtr<nsIInterfaceRequestor> callbacks;
-      aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
-      if (callbacks) {
-        callbacks->GetInterface(NS_GET_IID(nsILoadContext),
-                                getter_AddRefs(baseContext));
-      }
-    }
-
-    mLoadContext = new LoadContext(aPrincipal, baseContext);
-  }
-
-  void
-  MaybeAddTabChild(nsILoadGroup* aLoadGroup)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (!aLoadGroup) {
-      return;
-    }
-
+WorkerLoadInfo::
+InterfaceRequestor::InterfaceRequestor(nsIPrincipal* aPrincipal,
+                                       nsILoadGroup* aLoadGroup)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+
+  // Look for an existing LoadContext.  This is optional and it's ok if
+  // we don't find one.
+  nsCOMPtr<nsILoadContext> baseContext;
+  if (aLoadGroup) {
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
-    if (!callbacks) {
-      return;
-    }
-
-    nsCOMPtr<nsITabChild> tabChild;
-    callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild));
+    if (callbacks) {
+      callbacks->GetInterface(NS_GET_IID(nsILoadContext),
+                              getter_AddRefs(baseContext));
+    }
+  }
+
+  mLoadContext = new LoadContext(aPrincipal, baseContext);
+}
+
+void
+WorkerLoadInfo::
+InterfaceRequestor::MaybeAddTabChild(nsILoadGroup* aLoadGroup)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aLoadGroup) {
+    return;
+  }
+
+  nsCOMPtr<nsIInterfaceRequestor> callbacks;
+  aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  if (!callbacks) {
+    return;
+  }
+
+  nsCOMPtr<nsITabChild> tabChild;
+  callbacks->GetInterface(NS_GET_IID(nsITabChild), getter_AddRefs(tabChild));
+  if (!tabChild) {
+    return;
+  }
+
+  // Use weak references to the tab child.  Holding a strong reference will
+  // not prevent an ActorDestroy() from being called on the TabChild.
+  // Therefore, we should let the TabChild destroy itself as soon as possible.
+  mTabChildList.AppendElement(do_GetWeakReference(tabChild));
+}
+
+NS_IMETHODIMP
+WorkerLoadInfo::
+InterfaceRequestor::GetInterface(const nsIID& aIID, void** aSink)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mLoadContext);
+
+  if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
+    nsCOMPtr<nsILoadContext> ref = mLoadContext;
+    ref.forget(aSink);
+    return NS_OK;
+  }
+
+  // If we still have an active nsITabChild, then return it.  Its possible,
+  // though, that all of the TabChild objects have been destroyed.  In that
+  // case we return NS_NOINTERFACE.
+  if (aIID.Equals(NS_GET_IID(nsITabChild))) {
+    nsCOMPtr<nsITabChild> tabChild = GetAnyLiveTabChild();
     if (!tabChild) {
-      return;
-    }
-
-    // Use weak references to the tab child.  Holding a strong reference will
-    // not prevent an ActorDestroy() from being called on the TabChild.
-    // Therefore, we should let the TabChild destroy itself as soon as possible.
-    mTabChildList.AppendElement(do_GetWeakReference(tabChild));
-  }
-
-  NS_IMETHOD
-  GetInterface(const nsIID& aIID, void** aSink) MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-    MOZ_ASSERT(mLoadContext);
-
-    if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
-      nsCOMPtr<nsILoadContext> ref = mLoadContext;
-      ref.forget(aSink);
-      return NS_OK;
-    }
-
-    // If we still have an active nsITabChild, then return it.  Its possible,
-    // though, that all of the TabChild objects have been destroyed.  In that
-    // case we return NS_NOINTERFACE.
-    if(aIID.Equals(NS_GET_IID(nsITabChild))) {
-      nsCOMPtr<nsITabChild> tabChild = GetAnyLiveTabChild();
-      if (!tabChild) {
-        return NS_NOINTERFACE;
-      }
-      tabChild.forget(aSink);
-      return NS_OK;
-    }
-
-    return NS_NOINTERFACE;
-  }
-
-private:
-  ~InterfaceRequestor() { }
-
-  already_AddRefed<nsITabChild>
-  GetAnyLiveTabChild()
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    // Search our list of known TabChild objects for one that still exists.
-    while (!mTabChildList.IsEmpty()) {
-      nsCOMPtr<nsITabChild> tabChild =
-        do_QueryReferent(mTabChildList.LastElement());
-
-      // Does this tab child still exist?  If so, return it.  We are done.
-      if (tabChild) {
-        return tabChild.forget();
-      }
-
-      // Otherwise remove the stale weak reference and check the next one
-      mTabChildList.RemoveElementAt(mTabChildList.Length() - 1);
-    }
-
-    return nullptr;
-  }
-
-  nsCOMPtr<nsILoadContext> mLoadContext;
-
-  // Array of weak references to nsITabChild.  We do not want to keep TabChild
-  // actors alive for long after their ActorDestroy() methods are called.
-  nsTArray<nsWeakPtr> mTabChildList;
-};
-
-template <class Derived>
-NS_IMPL_ADDREF(WorkerPrivateParent<Derived>::InterfaceRequestor)
-
-template <class Derived>
-NS_IMPL_RELEASE(WorkerPrivateParent<Derived>::InterfaceRequestor)
-
-template <class Derived>
-NS_IMPL_QUERY_INTERFACE(WorkerPrivateParent<Derived>::InterfaceRequestor,
-                        nsIInterfaceRequestor)
-
-template <class Derived>
-class WorkerPrivateParent<Derived>::EventTarget MOZ_FINAL
-  : public nsIEventTarget
-{
-  // This mutex protects mWorkerPrivate and must be acquired *before* the
-  // WorkerPrivate's mutex whenever they must both be held.
-  mozilla::Mutex mMutex;
-  WorkerPrivate* mWorkerPrivate;
-  nsIEventTarget* mWeakNestedEventTarget;
-  nsCOMPtr<nsIEventTarget> mNestedEventTarget;
-
-public:
-  explicit EventTarget(WorkerPrivate* aWorkerPrivate)
-  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
-    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(nullptr)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-  }
-
-  EventTarget(WorkerPrivate* aWorkerPrivate, nsIEventTarget* aNestedEventTarget)
-  : mMutex("WorkerPrivateParent::EventTarget::mMutex"),
-    mWorkerPrivate(aWorkerPrivate), mWeakNestedEventTarget(aNestedEventTarget),
-    mNestedEventTarget(aNestedEventTarget)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    MOZ_ASSERT(aNestedEventTarget);
-  }
-
-  void
-  Disable()
-  {
-    nsCOMPtr<nsIEventTarget> nestedEventTarget;
-    {
-      MutexAutoLock lock(mMutex);
-
-      MOZ_ASSERT(mWorkerPrivate);
-      mWorkerPrivate = nullptr;
-      mNestedEventTarget.swap(nestedEventTarget);
-    }
-  }
-
-  nsIEventTarget*
-  GetWeakNestedEventTarget() const
-  {
-    MOZ_ASSERT(mWeakNestedEventTarget);
-    return mWeakNestedEventTarget;
-  }
-
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIEVENTTARGET
-
-private:
-  ~EventTarget()
-  { }
-};
+      return NS_NOINTERFACE;
+    }
+    tabChild.forget(aSink);
+    return NS_OK;
+  }
+
+  return NS_NOINTERFACE;
+}
+
+already_AddRefed<nsITabChild>
+WorkerLoadInfo::
+InterfaceRequestor::GetAnyLiveTabChild()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Search our list of known TabChild objects for one that still exists.
+  while (!mTabChildList.IsEmpty()) {
+    nsCOMPtr<nsITabChild> tabChild =
+      do_QueryReferent(mTabChildList.LastElement());
+
+    // Does this tab child still exist?  If so, return it.  We are done.
+    if (tabChild) {
+      return tabChild.forget();
+    }
+
+    // Otherwise remove the stale weak reference and check the next one
+    mTabChildList.RemoveElementAt(mTabChildList.Length() - 1);
+  }
+
+  return nullptr;
+}
+
+NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
+NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
+NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor, nsIInterfaceRequestor)
 
 struct WorkerPrivate::TimeoutInfo
 {
   TimeoutInfo()
   : mTimeoutCallable(JS::UndefinedValue()), mLineNumber(0), mId(0),
     mIsInterval(false), mCanceled(false)
   {
     MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
@@ -2375,17 +2411,17 @@ typename WorkerPrivateParent<Derived>::c
 template <class Derived>
 WorkerPrivateParent<Derived>::WorkerPrivateParent(
                                            JSContext* aCx,
                                            WorkerPrivate* aParent,
                                            const nsAString& aScriptURL,
                                            bool aIsChromeWorker,
                                            WorkerType aWorkerType,
                                            const nsACString& aSharedWorkerName,
-                                           LoadInfo& aLoadInfo)
+                                           WorkerLoadInfo& aLoadInfo)
 : mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
   mParent(aParent), mScriptURL(aScriptURL),
   mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
   mParentStatus(Pending), mParentSuspended(false),
   mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
   mWorkerType(aWorkerType),
@@ -2572,16 +2608,47 @@ WorkerPrivateParent<Derived>::DispatchCo
 
     mCondVar.Notify();
   }
 
   return NS_OK;
 }
 
 template <class Derived>
+nsresult
+WorkerPrivateParent<Derived>::DispatchDebuggerRunnable(
+                                              WorkerRunnable *aDebuggerRunnable)
+{
+  // May be called on any thread!
+
+  MOZ_ASSERT(aDebuggerRunnable);
+
+  nsRefPtr<WorkerRunnable> runnable = aDebuggerRunnable;
+
+  WorkerPrivate* self = ParentAsWorkerPrivate();
+
+  {
+    MutexAutoLock lock(mMutex);
+
+    if (self->mStatus == Dead) {
+      NS_WARNING("A debugger runnable was posted to a worker that is already "
+                 "shutting down!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    // Transfer ownership to the debugger queue.
+    self->mDebuggerQueue.Push(runnable.forget().take());
+
+    mCondVar.Notify();
+  }
+
+  return NS_OK;
+}
+
+template <class Derived>
 already_AddRefed<WorkerRunnable>
 WorkerPrivateParent<Derived>::MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable)
 {
   // May be called on any thread!
 
   MOZ_ASSERT(aRunnable);
 
   nsRefPtr<WorkerRunnable> workerRunnable =
@@ -4088,17 +4155,17 @@ WorkerDebugger::Disable()
   NotifyIsEnabled(false);
 }
 
 WorkerPrivate::WorkerPrivate(JSContext* aCx,
                              WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
                              const nsACString& aSharedWorkerName,
-                             LoadInfo& aLoadInfo)
+                             WorkerLoadInfo& aLoadInfo)
   : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
                                        aIsChromeWorker, aWorkerType,
                                        aSharedWorkerName, aLoadInfo)
   , mJSContext(nullptr)
   , mPRThread(nullptr)
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
@@ -4190,46 +4257,46 @@ ChromeWorkerPrivate::WorkerAvailable(JSC
 }
 
 // static
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(const GlobalObject& aGlobal,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
                            const nsACString& aSharedWorkerName,
-                           LoadInfo* aLoadInfo, ErrorResult& aRv)
+                           WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
   return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
                      aSharedWorkerName, aLoadInfo, aRv);
 }
 
 // static
 already_AddRefed<WorkerPrivate>
 WorkerPrivate::Constructor(JSContext* aCx,
                            const nsAString& aScriptURL,
                            bool aIsChromeWorker, WorkerType aWorkerType,
                            const nsACString& aSharedWorkerName,
-                           LoadInfo* aLoadInfo, ErrorResult& aRv)
+                           WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
 {
   WorkerPrivate* parent = NS_IsMainThread() ?
                           nullptr :
                           GetCurrentThreadWorkerPrivate();
   if (parent) {
     parent->AssertIsOnWorkerThread();
   } else {
     AssertIsOnMainThread();
   }
 
   MOZ_ASSERT_IF(aWorkerType != WorkerTypeDedicated,
                 !aSharedWorkerName.IsVoid());
   MOZ_ASSERT_IF(aWorkerType == WorkerTypeDedicated,
                 aSharedWorkerName.IsEmpty());
 
-  Maybe<LoadInfo> stackLoadInfo;
+  Maybe<WorkerLoadInfo> stackLoadInfo;
   if (!aLoadInfo) {
     stackLoadInfo.emplace();
 
     nsresult rv = GetLoadInfo(aCx, nullptr, parent, aScriptURL,
                               aIsChromeWorker, InheritLoadGroup,
                               stackLoadInfo.ptr());
     if (NS_FAILED(rv)) {
       scriptloader::ReportLoadError(aCx, aScriptURL, rv, !parent);
@@ -4282,29 +4349,29 @@ WorkerPrivate::Constructor(JSContext* aC
 }
 
 // static
 nsresult
 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
                            WorkerPrivate* aParent, const nsAString& aScriptURL,
                            bool aIsChromeWorker,
                            LoadGroupBehavior aLoadGroupBehavior,
-                           LoadInfo* aLoadInfo)
+                           WorkerLoadInfo* aLoadInfo)
 {
   using namespace mozilla::dom::workers::scriptloader;
   using mozilla::dom::indexedDB::IDBFactory;
 
   MOZ_ASSERT(aCx);
   MOZ_ASSERT_IF(NS_IsMainThread(), aCx == nsContentUtils::GetCurrentJSContext());
 
   if (aWindow) {
     AssertIsOnMainThread();
   }
 
-  LoadInfo loadInfo;
+  WorkerLoadInfo loadInfo;
   nsresult rv;
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     // If the parent is going away give up now.
     Status parentStatus;
     {
@@ -4538,22 +4605,23 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
   }
 
   aLoadInfo->StealFrom(loadInfo);
   return NS_OK;
 }
 
 // static
 void
-WorkerPrivate::OverrideLoadInfoLoadGroup(LoadInfo& aLoadInfo)
+WorkerPrivate::OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo)
 {
   MOZ_ASSERT(!aLoadInfo.mInterfaceRequestor);
 
-  aLoadInfo.mInterfaceRequestor = new InterfaceRequestor(aLoadInfo.mPrincipal,
-                                                         aLoadInfo.mLoadGroup);
+  aLoadInfo.mInterfaceRequestor =
+    new WorkerLoadInfo::InterfaceRequestor(aLoadInfo.mPrincipal,
+                                           aLoadInfo.mLoadGroup);
   aLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aLoadInfo.mLoadGroup);
 
   nsCOMPtr<nsILoadGroup> loadGroup =
     do_CreateInstance(NS_LOADGROUP_CONTRACTID);
 
   nsresult rv =
     loadGroup->SetNotificationCallbacks(aLoadInfo.mInterfaceRequestor);
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
@@ -4577,29 +4645,25 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
 
   EnableMemoryReporter();
 
   InitializeGCTimers();
 
   Maybe<JSAutoCompartment> workerCompartment;
 
   for (;;) {
-    // Workers lazily create a global object in CompileScriptRunnable. We need
-    // to enter the global's compartment as soon as it has been created.
-    if (!workerCompartment && GlobalScope()) {
-      workerCompartment.emplace(aCx, GlobalScope()->GetGlobalJSObject());
-    }
-
     Status currentStatus;
+    bool debuggerRunnablesPending = false;
     bool normalRunnablesPending = false;
 
     {
       MutexAutoLock lock(mMutex);
 
       while (mControlQueue.IsEmpty() &&
+             !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
              !(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
         WaitForWorkerEvents();
       }
 
       ProcessAllControlRunnablesLocked();
 
       currentStatus = mStatus;
     }
@@ -4643,50 +4707,72 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
             runnable->Cancel();
             runnable->Release();
           }
         }
 
         // Clear away our MessagePorts.
         mWorkerPorts.Clear();
 
-        // Unroot the global
+        // Unroot the globals
         mScope = nullptr;
+        mDebuggerScope = nullptr;
 
         return;
       }
     }
 
-    // Nothing to do here if we don't have any runnables in the main queue.
-    if (!normalRunnablesPending) {
-      SetGCTimerMode(IdleTimer);
-      continue;
-    }
-
-    MOZ_ASSERT(NS_HasPendingEvents(mThread));
-
-    // Start the periodic GC timer if it is not already running.
-    SetGCTimerMode(PeriodicTimer);
-
-    // Process a single runnable from the main queue.
-    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
-
-    // Only perform the Promise microtask checkpoint on the outermost event
-    // loop.  Don't run it, for example, during sync XHR or importScripts.
-    (void)Promise::PerformMicroTaskCheckpoint();
-
-    if (NS_HasPendingEvents(mThread)) {
-      // Now *might* be a good time to GC. Let the JS engine make the decision.
-      if (workerCompartment) {
+    if (debuggerRunnablesPending || normalRunnablesPending) {
+      // Start the periodic GC timer if it is not already running.
+      SetGCTimerMode(PeriodicTimer);
+    }
+
+    if (debuggerRunnablesPending) {
+      WorkerRunnable* runnable;
+
+      {
+        MutexAutoLock lock(mMutex);
+
+        mDebuggerQueue.Pop(runnable);
+        debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
+      }
+
+      MOZ_ASSERT(runnable);
+      static_cast<nsIRunnable*>(runnable)->Run();
+      runnable->Release();
+
+      if (debuggerRunnablesPending) {
+        WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
+        MOZ_ASSERT(globalScope);
+
+        // Now *might* be a good time to GC. Let the JS engine make the decision.
+        JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
         JS_MaybeGC(aCx);
       }
-    }
-    else {
-      // The normal event queue has been exhausted, cancel the periodic GC timer
-      // and schedule the idle GC timer.
+    } else if (normalRunnablesPending) {
+      MOZ_ASSERT(NS_HasPendingEvents(mThread));
+
+      // Process a single runnable from the main queue.
+      MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
+
+      // Only perform the Promise microtask checkpoint on the outermost event
+      // loop.  Don't run it, for example, during sync XHR or importScripts.
+      (void)Promise::PerformMicroTaskCheckpoint();
+
+      normalRunnablesPending = NS_HasPendingEvents(mThread);
+      if (normalRunnablesPending && GlobalScope()) {
+        // Now *might* be a good time to GC. Let the JS engine make the decision.
+        JSAutoCompartment ac(aCx, GlobalScope()->GetGlobalJSObject());
+        JS_MaybeGC(aCx);
+      }
+    }
+
+    if (!debuggerRunnablesPending && !normalRunnablesPending) {
+      // Both the debugger event queue and the normal event queue has been
+      // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
       SetGCTimerMode(IdleTimer);
     }
   }
 
   MOZ_CRASH("Shouldn't get here!");
 }
 
 void
@@ -6496,46 +6582,72 @@ WorkerPrivate::GetMessagePort(uint64_t a
   nsRefPtr<MessagePort> port;
   if (mWorkerPorts.Get(aMessagePortSerial, getter_AddRefs(port))) {
     return port;
   }
 
   return nullptr;
 }
 
-JSObject*
-WorkerPrivate::CreateGlobalScope(JSContext* aCx)
+WorkerGlobalScope*
+WorkerPrivate::GetOrCreateGlobalScope(JSContext* aCx)
 {
   AssertIsOnWorkerThread();
 
-  nsRefPtr<WorkerGlobalScope> globalScope;
-  if (IsSharedWorker()) {
-    globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName());
-  } else if (IsServiceWorker()) {
-    globalScope = new ServiceWorkerGlobalScope(this, SharedWorkerName());
-  } else {
-    globalScope = new DedicatedWorkerGlobalScope(this);
-  }
+  if (!mScope) {
+    nsRefPtr<WorkerGlobalScope> globalScope;
+    if (IsSharedWorker()) {
+      globalScope = new SharedWorkerGlobalScope(this, SharedWorkerName());
+    } else if (IsServiceWorker()) {
+      globalScope = new ServiceWorkerGlobalScope(this, SharedWorkerName());
+    } else {
+      globalScope = new DedicatedWorkerGlobalScope(this);
+    }
+
+    JS::Rooted<JSObject*> global(aCx);
+    NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
+
+    JSAutoCompartment ac(aCx, global);
+
+    if (!RegisterBindings(aCx, global)) {
+      return nullptr;
+    }
+
+    JS_FireOnNewGlobalObject(aCx, global);
+
+    mScope = globalScope.forget();
+  }
+
+  return mScope;
+}
+
+WorkerDebuggerGlobalScope*
+WorkerPrivate::CreateDebuggerGlobalScope(JSContext* aCx)
+{
+  AssertIsOnWorkerThread();
+
+  MOZ_ASSERT(!mDebuggerScope);
+
+  nsRefPtr<WorkerDebuggerGlobalScope> globalScope =
+    new WorkerDebuggerGlobalScope(this);
 
   JS::Rooted<JSObject*> global(aCx);
-  if (!globalScope->WrapGlobalObject(aCx, &global)) {
-    return nullptr;
-  }
+  NS_ENSURE_TRUE(globalScope->WrapGlobalObject(aCx, &global), nullptr);
 
   JSAutoCompartment ac(aCx, global);
 
-  if (!RegisterBindings(aCx, global)) {
+  if (!JS_DefineDebuggerObject(aCx, global)) {
     return nullptr;
   }
 
-  mScope = globalScope.forget();
-
   JS_FireOnNewGlobalObject(aCx, global);
 
-  return global;
+  mDebuggerScope = globalScope.forget();
+
+  return mDebuggerScope;
 }
 
 #ifdef DEBUG
 
 void
 WorkerPrivate::AssertIsOnWorkerThread() const
 {
   // This is much more complicated than it needs to be but we can't use mThread
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -58,30 +58,22 @@ struct PRThread;
 
 BEGIN_WORKERS_NAMESPACE
 
 class AutoSyncLoopHolder;
 class MessagePort;
 class SharedWorker;
 class WorkerControlRunnable;
 class WorkerDebugger;
+class WorkerDebuggerGlobalScope;
 class WorkerGlobalScope;
 class WorkerPrivate;
 class WorkerRunnable;
 class WorkerThread;
 
-// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to
-// be updated too.
-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
@@ -127,17 +119,16 @@ public:
 };
 
 template <class Derived>
 class WorkerPrivateParent : public DOMEventTargetHelper
 {
   class SynchronizeAndResumeRunnable;
 
 protected:
-  class InterfaceRequestor;
   class EventTarget;
   friend class EventTarget;
 
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
 
 public:
   struct LocationInfo
   {
@@ -147,94 +138,16 @@ public:
     nsCString mHostname;
     nsCString mPort;
     nsCString mPathname;
     nsCString mSearch;
     nsCString mHash;
     nsString mOrigin;
   };
 
-  struct LoadInfo
-  {
-    // All of these should be released in ForgetMainThreadObjects.
-    nsCOMPtr<nsIURI> mBaseURI;
-    nsCOMPtr<nsIURI> mResolvedScriptURI;
-    nsCOMPtr<nsIPrincipal> mPrincipal;
-    nsCOMPtr<nsIScriptContext> mScriptContext;
-    nsCOMPtr<nsPIDOMWindow> mWindow;
-    nsCOMPtr<nsIContentSecurityPolicy> mCSP;
-    nsCOMPtr<nsIChannel> mChannel;
-    nsCOMPtr<nsILoadGroup> mLoadGroup;
-
-    // Only set if we have a custom overriden load group
-    nsRefPtr<InterfaceRequestor> mInterfaceRequestor;
-
-    nsAutoPtr<PrincipalInfo> mPrincipalInfo;
-    nsCString mDomain;
-
-    uint64_t mWindowID;
-
-    bool mFromWindow;
-    bool mEvalAllowed;
-    bool mReportCSPViolations;
-    bool mXHRParamsAllowed;
-    bool mPrincipalIsSystem;
-    bool mIsInPrivilegedApp;
-    bool mIsInCertifiedApp;
-    bool mIndexedDBAllowed;
-
-    LoadInfo();
-    ~LoadInfo();
-
-    void
-    StealFrom(LoadInfo& aOther)
-    {
-      MOZ_ASSERT(!mBaseURI);
-      aOther.mBaseURI.swap(mBaseURI);
-
-      MOZ_ASSERT(!mResolvedScriptURI);
-      aOther.mResolvedScriptURI.swap(mResolvedScriptURI);
-
-      MOZ_ASSERT(!mPrincipal);
-      aOther.mPrincipal.swap(mPrincipal);
-
-      MOZ_ASSERT(!mScriptContext);
-      aOther.mScriptContext.swap(mScriptContext);
-
-      MOZ_ASSERT(!mWindow);
-      aOther.mWindow.swap(mWindow);
-
-      MOZ_ASSERT(!mCSP);
-      aOther.mCSP.swap(mCSP);
-
-      MOZ_ASSERT(!mChannel);
-      aOther.mChannel.swap(mChannel);
-
-      MOZ_ASSERT(!mLoadGroup);
-      aOther.mLoadGroup.swap(mLoadGroup);
-
-      MOZ_ASSERT(!mInterfaceRequestor);
-      aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
-
-      MOZ_ASSERT(!mPrincipalInfo);
-      mPrincipalInfo = aOther.mPrincipalInfo.forget();
-
-      mDomain = aOther.mDomain;
-      mWindowID = aOther.mWindowID;
-      mFromWindow = aOther.mFromWindow;
-      mEvalAllowed = aOther.mEvalAllowed;
-      mReportCSPViolations = aOther.mReportCSPViolations;
-      mXHRParamsAllowed = aOther.mXHRParamsAllowed;
-      mPrincipalIsSystem = aOther.mPrincipalIsSystem;
-      mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
-      mIsInCertifiedApp = aOther.mIsInCertifiedApp;
-      mIndexedDBAllowed = aOther.mIndexedDBAllowed;
-    }
-  };
-
 protected:
   typedef mozilla::ErrorResult ErrorResult;
 
   SharedMutex mMutex;
   mozilla::CondVar mCondVar;
   mozilla::CondVar mMemoryReportCondVar;
 
   // Protected by mMutex.
@@ -243,17 +156,17 @@ protected:
 
 private:
   WorkerPrivate* mParent;
   nsString mScriptURL;
   nsCString mSharedWorkerName;
   LocationInfo mLocationInfo;
   // The lifetime of these objects within LoadInfo is managed explicitly;
   // they do not need to be cycle collected.
-  LoadInfo mLoadInfo;
+  WorkerLoadInfo mLoadInfo;
 
   // Only used for top level workers.
   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
   nsRevocableEventPtr<SynchronizeAndResumeRunnable> mSynchronizeRunnable;
 
   // Only for ChromeWorkers without window and only touched on the main thread.
   nsTArray<nsCString> mHostObjectURIs;
 
@@ -279,17 +192,17 @@ protected:
   // in Construct() and emptied by WorkerFinishedRunnable, and conditionally
   // traversed by the cycle collector if the busy count is zero.
   nsRefPtr<WorkerPrivate> mSelfRef;
 
   WorkerPrivateParent(JSContext* aCx, WorkerPrivate* aParent,
                       const nsAString& aScriptURL, bool aIsChromeWorker,
                       WorkerType aWorkerType,
                       const nsACString& aSharedWorkerName,
-                      LoadInfo& aLoadInfo);
+                      WorkerLoadInfo& aLoadInfo);
 
   ~WorkerPrivateParent();
 
 private:
   Derived*
   ParentAsWorkerPrivate() const
   {
     return static_cast<Derived*>(const_cast<WorkerPrivateParent*>(this));
@@ -341,16 +254,19 @@ public:
   Dispatch(WorkerRunnable* aRunnable)
   {
     return DispatchPrivate(aRunnable, nullptr);
   }
 
   nsresult
   DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable);
 
+  nsresult
+  DispatchDebuggerRunnable(WorkerRunnable* aDebuggerRunnable);
+
   already_AddRefed<WorkerRunnable>
   MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable);
 
   already_AddRefed<nsIEventTarget>
   GetEventTarget();
 
   // May be called on any thread...
   bool
@@ -845,26 +761,28 @@ class WorkerPrivate : public WorkerPriva
     PeriodicTimer = 0,
     IdleTimer,
     NoTimer
   };
 
   nsRefPtr<WorkerDebugger> mDebugger;
 
   Queue<WorkerControlRunnable*, 4> mControlQueue;
+  Queue<WorkerRunnable*, 4> mDebuggerQueue;
 
   // Touched on multiple threads, protected with mMutex.
   JSContext* mJSContext;
   nsRefPtr<WorkerCrossThreadDispatcher> mCrossThreadDispatcher;
   nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
   nsRefPtr<WorkerThread> mThread;
   PRThread* mPRThread;
 
   // Things touched on worker thread only.
   nsRefPtr<WorkerGlobalScope> mScope;
+  nsRefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
   nsTArray<ParentType*> mChildWorkers;
   nsTArray<WorkerFeature*> mFeatures;
   nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
 
   struct SyncLoopInfo
   {
     explicit SyncLoopInfo(EventTarget* aEventTarget);
 
@@ -919,39 +837,39 @@ public:
   static already_AddRefed<WorkerPrivate>
   Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
               ErrorResult& aRv);
 
   static already_AddRefed<WorkerPrivate>
   Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
               bool aIsChromeWorker, WorkerType aWorkerType,
               const nsACString& aSharedWorkerName,
-              LoadInfo* aLoadInfo, ErrorResult& aRv);
+              WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
 
   static already_AddRefed<WorkerPrivate>
   Constructor(JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
               WorkerType aWorkerType, const nsACString& aSharedWorkerName,
-              LoadInfo* aLoadInfo, ErrorResult& aRv);
+              WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
 
   static bool
   WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
 
   enum LoadGroupBehavior
   {
     InheritLoadGroup,
     OverrideLoadGroup
   };
 
   static nsresult
   GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
               const nsAString& aScriptURL, bool aIsChromeWorker,
-              LoadGroupBehavior aLoadGroupBehavior, LoadInfo* aLoadInfo);
+              LoadGroupBehavior aLoadGroupBehavior, WorkerLoadInfo* aLoadInfo);
 
   static void
-  OverrideLoadInfoLoadGroup(LoadInfo& aLoadInfo);
+  OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo);
 
   WorkerDebugger*
   Debugger() const
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mDebugger);
     return mDebugger;
   }
@@ -1108,16 +1026,23 @@ public:
 
   WorkerGlobalScope*
   GlobalScope() const
   {
     AssertIsOnWorkerThread();
     return mScope;
   }
 
+  WorkerDebuggerGlobalScope*
+  DebuggerGlobalScope() const
+  {
+    AssertIsOnWorkerThread();
+    return mDebuggerScope;
+  }
+
   void
   SetThread(WorkerThread* aThread);
 
   void
   AssertIsOnWorkerThread() const
 #ifdef DEBUG
   ;
 #else
@@ -1155,18 +1080,21 @@ public:
   ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial);
 
   void
   DisconnectMessagePort(uint64_t aMessagePortSerial);
 
   MessagePort*
   GetMessagePort(uint64_t aMessagePortSerial);
 
-  JSObject*
-  CreateGlobalScope(JSContext* aCx);
+  WorkerGlobalScope*
+  GetOrCreateGlobalScope(JSContext* aCx);
+
+  WorkerDebuggerGlobalScope*
+  CreateDebuggerGlobalScope(JSContext* aCx);
 
   bool
   RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 
   bool
   DumpEnabled() const
   {
     AssertIsOnWorkerThread();
@@ -1238,17 +1166,17 @@ public:
   // thread.
   bool
   RunBeforeNextEvent(nsIRunnable* aRunnable);
 
 private:
   WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsACString& aSharedWorkerName,
-                LoadInfo& aLoadInfo);
+                WorkerLoadInfo& aLoadInfo);
 
   void
   ClearMainEventQueue(WorkerRanOrNot aRanOrNot);
 
   bool
   MayContinueRunning()
   {
     AssertIsOnWorkerThread();
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -46,16 +46,32 @@ WorkerRunnable::WorkerRunnable(WorkerPri
 : mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
   mCallingCancelWithinRun(false)
 {
   MOZ_ASSERT(aWorkerPrivate);
 }
 #endif
 
 bool
+WorkerRunnable::IsDebuggerRunnable() const
+{
+  return false;
+}
+
+nsIGlobalObject*
+WorkerRunnable::DefaultGlobalObject() const
+{
+  if (IsDebuggerRunnable()) {
+    return mWorkerPrivate->DebuggerGlobalScope();
+  } else {
+    return mWorkerPrivate->GlobalScope();
+  }
+}
+
+bool
 WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 {
 #ifdef DEBUG
   MOZ_ASSERT(aWorkerPrivate);
 
   switch (mBehavior) {
     case ParentThreadUnchangedBusyCount:
       aWorkerPrivate->AssertIsOnWorkerThread();
@@ -116,17 +132,21 @@ WorkerRunnable::Dispatch(JSContext* aCx)
   return ok;
 }
 
 bool
 WorkerRunnable::DispatchInternal()
 {
   if (mBehavior == WorkerThreadModifyBusyCount ||
       mBehavior == WorkerThreadUnchangedBusyCount) {
-    return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
+    if (IsDebuggerRunnable()) {
+      return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(this));
+    } else {
+      return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
+    }
   }
 
   MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
 
   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
     return NS_SUCCEEDED(parent->Dispatch(this));
   }
 
@@ -280,19 +300,23 @@ WorkerRunnable::Run()
  }
 
   // Track down the appropriate global to use for the AutoJSAPI/AutoEntryScript.
   nsCOMPtr<nsIGlobalObject> globalObject;
   bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent();
   MOZ_ASSERT(isMainThread == NS_IsMainThread());
   nsRefPtr<WorkerPrivate> kungFuDeathGrip;
   if (targetIsWorkerThread) {
-    globalObject = mWorkerPrivate->GlobalScope();
-  }
-  else {
+    JSObject* global = JS::CurrentGlobalOrNull(GetCurrentThreadJSContext());
+    if (global) {
+      globalObject = GetGlobalObjectForGlobal(global);
+    } else {
+      globalObject = DefaultGlobalObject();
+    }
+  } else {
     kungFuDeathGrip = mWorkerPrivate;
     if (isMainThread) {
       globalObject = static_cast<nsGlobalWindow*>(mWorkerPrivate->GetWindow());
     } else {
       globalObject = mWorkerPrivate->GetParent()->GlobalScope();
     }
   }
 
@@ -322,18 +346,18 @@ WorkerRunnable::Run()
   if (!targetIsWorkerThread && mWorkerPrivate->GetWrapper()) {
     ac.emplace(cx, mWorkerPrivate->GetWrapper());
   }
 
   bool result = WorkerRun(cx, mWorkerPrivate);
 
   // In the case of CompileScriptRunnnable, WorkerRun above can cause us to
   // lazily create a global, so we construct aes here before calling PostRun.
-  if (targetIsWorkerThread && !aes && mWorkerPrivate->GlobalScope()) {
-    aes.emplace(mWorkerPrivate->GlobalScope(), false, GetCurrentThreadJSContext());
+  if (targetIsWorkerThread && !aes && DefaultGlobalObject()) {
+    aes.emplace(DefaultGlobalObject(), false, GetCurrentThreadJSContext());
     cx = aes->cx();
   }
 
   PostRun(cx, mWorkerPrivate, result);
 
   return result ? NS_OK : NS_ERROR_FAILURE;
 }
 
@@ -344,16 +368,24 @@ WorkerRunnable::Cancel()
 
   MOZ_ASSERT(canceledCount, "Cancel() overflow!");
 
   // The docs say that Cancel() should not be called more than once and that we
   // should throw NS_ERROR_UNEXPECTED if it is.
   return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
 }
 
+void
+WorkerDebuggerRunnable::PostDispatch(JSContext* aCx,
+                                     WorkerPrivate* aWorkerPrivate,
+                                     bool aDispatchResult)
+{
+  MaybeReportMainThreadException(aCx, aDispatchResult);
+}
+
 WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
                                        nsIEventTarget* aSyncLoopTarget)
 : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
   mSyncLoopTarget(aSyncLoopTarget)
 {
 #ifdef DEBUG
   if (mSyncLoopTarget) {
     mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -94,16 +94,24 @@ protected:
     mCallingCancelWithinRun(false)
   { }
 #endif
 
   // This class is reference counted.
   virtual ~WorkerRunnable()
   { }
 
+  // Returns true if this runnable should be dispatched to the debugger queue,
+  // and false otherwise.
+  virtual bool
+  IsDebuggerRunnable() const;
+
+  nsIGlobalObject*
+  DefaultGlobalObject() const;
+
   // By default asserts that Dispatch() is being called on the right thread
   // (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
   // Also increments the busy count of |mWorkerPrivate| if targeting the
   // WorkerThread.
   virtual bool
   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
   // By default asserts that Dispatch() is being called on the right thread
@@ -128,16 +136,48 @@ protected:
   virtual bool
   DispatchInternal();
 
   // Calling Run() directly is not supported. Just call Dispatch() and
   // WorkerRun() will be called on the correct thread automatically.
   NS_DECL_NSIRUNNABLE
 };
 
+// This runnable is used to send a message to a worker debugger.
+class WorkerDebuggerRunnable : public WorkerRunnable
+{
+protected:
+  explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
+  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+  {
+  }
+
+  virtual ~WorkerDebuggerRunnable()
+  { }
+
+private:
+  virtual bool
+  IsDebuggerRunnable() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
+  virtual bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+
+    return true;
+  }
+
+  virtual void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult) MOZ_OVERRIDE;
+};
+
 // This runnable is used to send a message directly to a worker's sync loop.
 class WorkerSyncRunnable : public WorkerRunnable
 {
 protected:
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
 
   // Passing null for aSyncLoopTarget is allowed and will result in the behavior
   // of a normal WorkerRunnable.
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -11,16 +11,18 @@
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
+#include "mozilla/dom/WorkerGlobalScopeBinding.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/Services.h"
 #include "nsServiceManagerUtils.h"
 
 #include "nsIDocument.h"
 #include "nsIServiceWorkerManager.h"
 
@@ -33,20 +35,16 @@
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "Performance.h"
 #include "ServiceWorkerClients.h"
 
-#define UNWRAP_WORKER_OBJECT(Interface, obj, value)                           \
-  UnwrapObject<prototypes::id::Interface##_workers,                           \
-    mozilla::dom::Interface##Binding_workers::NativeType>(obj, value)
-
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
 
 using mozilla::dom::cache::CacheStorage;
 using mozilla::dom::indexedDB::IDBFactory;
 using mozilla::ipc::PrincipalInfo;
 
@@ -466,16 +464,91 @@ ServiceWorkerGlobalScope::Clients()
 {
   if (!mClients) {
     mClients = new ServiceWorkerClients(this);
   }
 
   return mClients;
 }
 
+WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
+                                                  WorkerPrivate* aWorkerPrivate)
+: mWorkerPrivate(aWorkerPrivate)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+NS_IMPL_ADDREF_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerGlobalScope)
+  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+bool
+WorkerDebuggerGlobalScope::WrapGlobalObject(JSContext* aCx,
+                                            JS::MutableHandle<JSObject*> aReflector)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  JS::CompartmentOptions options;
+  mWorkerPrivate->CopyJSCompartmentOptions(options);
+
+  return WorkerDebuggerGlobalScopeBinding::Wrap(aCx, this, this, options,
+                                                GetWorkerPrincipal(), true,
+                                                aReflector);
+}
+
+void
+WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx,
+                                     JS::MutableHandle<JSObject*> aGlobal)
+{
+  aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper());
+}
+
+void
+WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
+                                const Optional<nsAString>& aString) const
+{
+  return mWorkerPrivate->GetOrCreateGlobalScope(aCx)->Dump(aString);
+}
+
+nsIGlobalObject*
+GetGlobalObjectForGlobal(JSObject* global)
+{
+  nsIGlobalObject* globalObject = nullptr;
+  UNWRAP_WORKER_OBJECT(WorkerGlobalScope, global, globalObject);
+  if (!globalObject) {
+    UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalObject);
+    MOZ_ASSERT(globalObject);
+  }
+  return globalObject;
+}
+
+bool
+IsWorkerGlobal(JSObject* object)
+{
+  nsIGlobalObject* globalObject;
+  return NS_SUCCEEDED(UNWRAP_WORKER_OBJECT(WorkerGlobalScope, object,
+                                           globalObject)) && !!globalObject;
+}
+
+bool
+IsDebuggerGlobal(JSObject* object)
+{
+  nsIGlobalObject* globalObject;
+  return NS_SUCCEEDED(UNWRAP_OBJECT(WorkerDebuggerGlobalScope, object,
+                                    globalObject)) && !!globalObject;
+}
+
 bool
 GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
   JS_ReportErrorNumber(aCx, js::GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
   return false;
 }
 
 namespace {
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -236,18 +236,51 @@ public:
   IMPL_EVENT_HANDLER(activate)
   IMPL_EVENT_HANDLER(beforeevicted)
   IMPL_EVENT_HANDLER(evicted)
   IMPL_EVENT_HANDLER(fetch)
   IMPL_EVENT_HANDLER(install)
   IMPL_EVENT_HANDLER(message)
 };
 
-JSObject*
-CreateGlobalScope(JSContext* aCx);
+class WorkerDebuggerGlobalScope MOZ_FINAL : public DOMEventTargetHelper,
+                                            public nsIGlobalObject
+{
+  WorkerPrivate* mWorkerPrivate;
+
+public:
+  explicit WorkerDebuggerGlobalScope(WorkerPrivate* aWorkerPrivate);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE
+  {
+    MOZ_CRASH("Shouldn't get here!");
+  }
+
+  virtual bool
+  WrapGlobalObject(JSContext* aCx,
+                   JS::MutableHandle<JSObject*> aReflector);
+
+  virtual JSObject*
+  GetGlobalJSObject(void) MOZ_OVERRIDE
+  {
+    return GetWrapper();
+  }
+
+  void
+  GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal);
+
+  void
+  Dump(JSContext* aCx, const Optional<nsAString>& aString) const;
+
+private:
+  virtual ~WorkerDebuggerGlobalScope();
+};
 
 END_WORKERS_NAMESPACE
 
 inline nsISupports*
 ToSupports(mozilla::dom::workers::WorkerGlobalScope* aScope)
 {
   return static_cast<nsIDOMEventTarget*>(aScope);
 }
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -9,28 +9,57 @@
 #include "jsapi.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 #include <stdint.h>
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsString.h"
+#include "nsTArray.h"
+
+#include "nsILoadContext.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIInterfaceRequestor.h"
 
 #define BEGIN_WORKERS_NAMESPACE \
   namespace mozilla { namespace dom { namespace workers {
 #define END_WORKERS_NAMESPACE \
   } /* namespace workers */ } /* namespace dom */ } /* namespace mozilla */
 #define USING_WORKERS_NAMESPACE \
   using namespace mozilla::dom::workers;
 
 #define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
 
+class nsIContentSecurityPolicy;
 class nsIScriptContext;
+class nsIGlobalObject;
 class nsPIDOMWindow;
+class nsIPrincipal;
+class nsILoadGroup;
+class nsITabChild;
+class nsIChannel;
+class nsIURI;
+
+namespace mozilla {
+namespace ipc {
+class PrincipalInfo;
+}
+
+namespace dom {
+// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to
+// be updated too.
+enum WorkerType
+{
+  WorkerTypeDedicated,
+  WorkerTypeShared,
+  WorkerTypeService
+};
+}
+}
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerPrivate;
 
 struct PrivatizableBase
 { };
 
@@ -165,16 +194,74 @@ struct JSSettings
 enum WorkerPreference
 {
   WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
   WORKERPREF_DOM_FETCH,// dom.fetch.enabled
   WORKERPREF_DOM_CACHES, // dom.caches.enabled
   WORKERPREF_COUNT
 };
 
+// Implemented in WorkerPrivate.cpp
+
+struct WorkerLoadInfo
+{
+  // All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
+  nsCOMPtr<nsIURI> mBaseURI;
+  nsCOMPtr<nsIURI> mResolvedScriptURI;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIScriptContext> mScriptContext;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIContentSecurityPolicy> mCSP;
+  nsCOMPtr<nsIChannel> mChannel;
+  nsCOMPtr<nsILoadGroup> mLoadGroup;
+
+  class InterfaceRequestor MOZ_FINAL : public nsIInterfaceRequestor
+  {
+    NS_DECL_ISUPPORTS
+
+  public:
+    InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
+    void MaybeAddTabChild(nsILoadGroup* aLoadGroup);
+    NS_IMETHOD GetInterface(const nsIID& aIID, void** aSink) MOZ_OVERRIDE;
+
+  private:
+    ~InterfaceRequestor() { }
+
+    already_AddRefed<nsITabChild> GetAnyLiveTabChild();
+
+    nsCOMPtr<nsILoadContext> mLoadContext;
+
+    // Array of weak references to nsITabChild.  We do not want to keep TabChild
+    // actors alive for long after their ActorDestroy() methods are called.
+    nsTArray<nsWeakPtr> mTabChildList;
+  };
+
+  // Only set if we have a custom overriden load group
+  nsRefPtr<InterfaceRequestor> mInterfaceRequestor;
+
+  nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
+  nsCString mDomain;
+
+  uint64_t mWindowID;
+
+  bool mFromWindow;
+  bool mEvalAllowed;
+  bool mReportCSPViolations;
+  bool mXHRParamsAllowed;
+  bool mPrincipalIsSystem;
+  bool mIsInPrivilegedApp;
+  bool mIsInCertifiedApp;
+  bool mIndexedDBAllowed;
+
+  WorkerLoadInfo();
+  ~WorkerLoadInfo();
+
+  void StealFrom(WorkerLoadInfo& aOther);
+};
+
 // All of these are implemented in RuntimeService.cpp
 
 void
 CancelWorkersForWindow(nsPIDOMWindow* aWindow);
 
 void
 SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
 
@@ -238,16 +325,25 @@ const uint32_t kJSPrincipalsDebugToken =
 namespace exceptions {
 
 // Implemented in Exceptions.cpp
 void
 ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult);
 
 } // namespace exceptions
 
+nsIGlobalObject*
+GetGlobalObjectForGlobal(JSObject* global);
+
+bool
+IsWorkerGlobal(JSObject* global);
+
+bool
+IsDebuggerGlobal(JSObject* global);
+
 // Throws the JSMSG_GETTER_ONLY exception.  This shouldn't be used going
 // forward -- getter-only properties should just use JS_PSG for the setter
 // (implying no setter at all), which will not throw when set in non-strict
 // code but will in strict code.  Old code should use this only for temporary
 // compatibility reasons.
 extern bool
 GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -438,17 +438,17 @@ LayerManagerComposite::RenderDebugOverla
                             aBounds, effects, alpha, gfx::Matrix4x4());
       mCompositor->DrawQuad(gfx::Rect(aBounds.width - border - width, border + width, width, aBounds.height - 2 * border - 2 * width),
                             aBounds, effects, alpha, gfx::Matrix4x4());
       SetDebugOverlayWantsNextFrame(true);
     }
 #endif
 
     float fillRatio = mCompositor->GetFillRatio();
-    mFPS->DrawFPS(now, drawFrameColorBars ? 10 : 0, 0, unsigned(fillRatio), mCompositor);
+    mFPS->DrawFPS(now, drawFrameColorBars ? 10 : 1, 2, unsigned(fillRatio), mCompositor);
 
     if (mUnusedApzTransformWarning) {
       // If we have an unused APZ transform on this composite, draw a 20x20 red box
       // in the top-right corner
       EffectChain effects;
       effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(1, 0, 0, 1));
       mCompositor->DrawQuad(gfx::Rect(aBounds.width - 20, 0, aBounds.width, 20),
                             aBounds, effects, alpha, gfx::Matrix4x4());
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -237,26 +237,26 @@
     BITWISE_COMMONX4_SIMD_OP(_)      \
     WITH_COMMONX4_SIMD_OP(_)         \
     _(bitselect)                     \
     _(select)                        \
     _(splat)                         \
     _(not)                           \
     _(neg)                           \
     _(swizzle)                       \
+    _(load)                          \
+    _(store)                         \
     _(check)
 #define FOREACH_COMMONX4_SIMD_OP(_)  \
     ION_COMMONX4_SIMD_OP(_)          \
     COMP_COMMONX4_TO_INT32X4_SIMD_OP(_) \
     _(shuffle)                       \
-    _(load)                          \
     _(loadX)                         \
     _(loadXY)                        \
     _(loadXYZ)                       \
-    _(store)                         \
     _(storeX)                        \
     _(storeXY)                       \
     _(storeXYZ)
 #define FORALL_SIMD_OP(_)            \
     FOREACH_INT32X4_SIMD_OP(_)       \
     FOREACH_FLOAT32X4_SIMD_OP(_)     \
     FOREACH_COMMONX4_SIMD_OP(_)
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1628,16 +1628,18 @@ ReportTypedObjTypeError(JSContext *cx,
     return false;
 }
 
 /* static */ void
 OutlineTypedObject::obj_trace(JSTracer *trc, JSObject *object)
 {
     OutlineTypedObject &typedObj = object->as<OutlineTypedObject>();
 
+    MarkShape(trc, &typedObj.shape_, "OutlineTypedObject_shape");
+
     if (!typedObj.owner_)
         return;
 
     // When this is called for compacting GC, the related objects we touch here
     // may not have had their slots updated yet. Note that this does not apply
     // to generational GC because these objects (type descriptors and
     // prototypes) are never allocated in the nursery.
     TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
@@ -2151,20 +2153,23 @@ InlineTypedObject::createCopy(JSContext 
     return res;
 }
 
 /* static */ void
 InlineTypedObject::obj_trace(JSTracer *trc, JSObject *object)
 {
     InlineTypedObject &typedObj = object->as<InlineTypedObject>();
 
-    // Inline transparent objects do not have references and do not need to be
-    // traced. If they have an entry in the compartment's LazyArrayBufferTable,
+    MarkShape(trc, &typedObj.shape_, "InlineTypedObject_shape");
+
+    // Inline transparent objects do not have references and do not need more
+    // tracing. If there is an entry in the compartment's LazyArrayBufferTable,
     // tracing that reference will be taken care of by the table itself.
-    MOZ_ASSERT(typedObj.is<InlineOpaqueTypedObject>());
+    if (typedObj.is<InlineTransparentTypedObject>())
+        return;
 
     // When this is called for compacting GC, the related objects we touch here
     // may not have had their slots updated yet.
     TypeDescr &descr = typedObj.maybeForwardedTypeDescr();
 
     descr.traceInstances(trc, typedObj.inlineTypedMem(), 1);
 }
 
@@ -2332,17 +2337,17 @@ LazyArrayBufferTable::sizeOfIncludingThi
             nullptr,   /* getElements */                 \
             TypedObject::obj_enumerate,                  \
             nullptr, /* thisObject */                    \
         }                                                \
     }
 
 DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject, OutlineTypedObject::obj_trace);
 DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,      OutlineTypedObject::obj_trace);
-DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,  nullptr);
+DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,  InlineTypedObject::obj_trace);
 DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,       InlineTypedObject::obj_trace);
 
 static int32_t
 LengthForType(TypeDescr &descr)
 {
     switch (descr.kind()) {
       case type::Scalar:
       case type::Reference:
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -364,17 +364,17 @@ bool CreateUserSizeAndAlignmentPropertie
 
 class ArrayTypeDescr;
 
 /*
  * Properties and methods of the `ArrayType` meta type object. There
  * is no `class_` field because `ArrayType` is just a native
  * constructor function.
  */
-class ArrayMetaTypeDescr : public JSObject
+class ArrayMetaTypeDescr : public NativeObject
 {
   private:
     // Helper for creating a new ArrayType object.
     //
     // - `arrayTypePrototype` - prototype for the new object to be created
     // - `elementType` - type object for the elements in the array
     // - `stringRepr` - canonical string representation for the array
     // - `size` - length of the array
@@ -428,17 +428,17 @@ class ArrayTypeDescr : public ComplexTyp
     }
 };
 
 /*
  * Properties and methods of the `StructType` meta type object. There
  * is no `class_` field because `StructType` is just a native
  * constructor function.
  */
-class StructMetaTypeDescr : public JSObject
+class StructMetaTypeDescr : public NativeObject
 {
   private:
     static JSObject *create(JSContext *cx, HandleObject structTypeGlobal,
                             HandleObject fields);
 
   public:
     // Properties and methods to be installed on StructType.prototype,
     // and hence inherited by all struct type objects:
@@ -505,26 +505,27 @@ class TypedObjectModuleObject : public N
     };
 
     static const Class class_;
 };
 
 /* Base type for transparent and opaque typed objects. */
 class TypedObject : public JSObject
 {
-  private:
     static const bool IsTypedObjectClass = true;
 
     static bool obj_getArrayElement(JSContext *cx,
                                     Handle<TypedObject*> typedObj,
                                     Handle<TypeDescr*> typeDescr,
                                     uint32_t index,
                                     MutableHandleValue vp);
 
   protected:
+    HeapPtrShape shape_;
+
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
                                    MutableHandleShape propp);
 
     static bool obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
                                   MutableHandleObject objp, MutableHandleShape propp);
 
     static bool obj_defineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
@@ -596,16 +597,18 @@ class TypedObject : public JSObject
 
     // User-accessible constructor (`new TypeDescriptor(...)`). Note that the
     // callee here is the type descriptor.
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
 
     /* Accessors for self hosted code. */
     static bool GetBuffer(JSContext *cx, unsigned argc, Value *vp);
     static bool GetByteOffset(JSContext *cx, unsigned argc, Value *vp);
+
+    Shape *shapeFromGC() { return shape_; }
 };
 
 typedef Handle<TypedObject*> HandleTypedObject;
 
 class OutlineTypedObject : public TypedObject
 {
     // The object which owns the data this object points to. Because this
     // pointer is managed in tandem with |data|, this is not a HeapPtr and
@@ -706,18 +709,16 @@ class InlineTypedObject : public TypedOb
     static gc::AllocKind allocKindForTypeDescriptor(TypeDescr *descr) {
         size_t nbytes = descr->size();
         MOZ_ASSERT(nbytes <= MaximumSize);
 
         return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
     }
 
     uint8_t *inlineTypedMem() const {
-        static_assert(offsetof(InlineTypedObject, data_) == sizeof(JSObject),
-                      "The data for an inline typed object must follow the shape and type.");
         return (uint8_t *) &data_;
     }
 
     static void obj_trace(JSTracer *trace, JSObject *object);
     static void objectMovedDuringMinorGC(JSTracer *trc, JSObject *dst, JSObject *src);
 
     static size_t offsetOfDataStart() {
         return offsetof(InlineTypedObject, data_);
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -677,31 +677,31 @@ struct HeapPtrHasher
 
     static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
     static bool match(const Key &k, Lookup l) { return k.get() == l; }
     static void rekey(Key &k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
 /* Specialized hashing policy for HeapPtrs. */
 template <class T>
-struct DefaultHasher< HeapPtr<T> > : HeapPtrHasher<T> { };
+struct DefaultHasher<HeapPtr<T>> : HeapPtrHasher<T> { };
 
 template <class T>
 struct PreBarrieredHasher
 {
     typedef PreBarriered<T> Key;
     typedef T Lookup;
 
     static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
     static bool match(const Key &k, Lookup l) { return k.get() == l; }
     static void rekey(Key &k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
 template <class T>
-struct DefaultHasher< PreBarriered<T> > : PreBarrieredHasher<T> { };
+struct DefaultHasher<PreBarriered<T>> : PreBarrieredHasher<T> { };
 
 /*
  * Incremental GC requires that weak pointers have read barriers. This is mostly
  * an issue for empty shapes stored in JSCompartment. The problem happens when,
  * during an incremental GC, some JS code stores one of the compartment's empty
  * shapes into an object already marked black. Normally, this would not be a
  * problem, because the empty shape would have been part of the initial snapshot
  * when the GC started. However, since this is a weak pointer, it isn't. So we
@@ -735,16 +735,32 @@ class ReadBarriered
     T operator->() const { return get(); }
 
     T *unsafeGet() { return &value; }
     T const * unsafeGet() const { return &value; }
 
     void set(T v) { value = v; }
 };
 
+/* Useful for hashtables with a ReadBarriered as key. */
+template <class T>
+struct ReadBarrieredHasher
+{
+    typedef ReadBarriered<T> Key;
+    typedef T Lookup;
+
+    static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
+    static bool match(const Key &k, Lookup l) { return k.get() == l; }
+    static void rekey(Key &k, const Key& newKey) { k.set(newKey); }
+};
+
+/* Specialized hashing policy for ReadBarriereds. */
+template <class T>
+struct DefaultHasher<ReadBarriered<T>> : ReadBarrieredHasher<T> { };
+
 class ArrayObject;
 class ArrayBufferObject;
 class NestedScopeObject;
 class DebugScopeObject;
 class GlobalObject;
 class ScriptSourceObject;
 class Shape;
 class BaseShape;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1673,51 +1673,54 @@ GCMarker::processMarkStackTop(SliceBudge
         if (budget.isOverBudget()) {
             repush(obj);
             return;
         }
 
         ObjectGroup *group = obj->groupFromGC();
         traverse(group);
 
-        Shape *shape = obj->lastProperty();
-        PushMarkStack(this, shape);
-
         /* Call the trace hook if necessary. */
         const Class *clasp = group->clasp();
         if (clasp->trace) {
             // Global objects all have the same trace hook. That hook is safe without barriers
             // if the global has no custom trace hook of its own, or has been moved to a different
             // compartment, and so can't have one.
             MOZ_ASSERT_IF(!(clasp->trace == JS_GlobalObjectTraceHook &&
                             (!obj->compartment()->options().getTrace() || !obj->isOwnGlobal())),
                           clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
             if (clasp->trace == InlineTypedObject::obj_trace) {
-                TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
+                Shape *shape = obj->as<InlineTypedObject>().shapeFromGC();
+                PushMarkStack(this, shape);
+                TypeDescr *descr = &obj->as<InlineTypedObject>().typeDescr();
                 if (!descr->hasTraceList())
                     return;
                 unboxedTraceList = descr->traceList();
-                unboxedMemory = obj->as<InlineOpaqueTypedObject>().inlineTypedMem();
+                unboxedMemory = obj->as<InlineTypedObject>().inlineTypedMem();
                 goto scan_unboxed;
             }
             if (clasp == &UnboxedPlainObject::class_) {
                 const UnboxedLayout &layout = obj->as<UnboxedPlainObject>().layout();
                 unboxedTraceList = layout.traceList();
                 if (!unboxedTraceList)
                     return;
                 unboxedMemory = obj->as<UnboxedPlainObject>().data();
                 goto scan_unboxed;
             }
             clasp->trace(this, obj);
         }
 
-        if (!shape->isNative())
+        if (!clasp->isNative())
             return;
 
         NativeObject *nobj = &obj->as<NativeObject>();
+
+        Shape *shape = nobj->lastProperty();
+        PushMarkStack(this, shape);
+
         unsigned nslots = nobj->slotSpan();
 
         do {
             if (nobj->hasEmptyElements())
                 break;
 
             if (nobj->denseElementsAreCopyOnWrite()) {
                 JSObject *owner = nobj->getElementsHeader()->ownerObject();
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -3,16 +3,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Nursery-inl.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Move.h"
 
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsutil.h"
 #include "prmjtime.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Memory.h"
@@ -31,16 +32,29 @@
 
 using namespace js;
 using namespace gc;
 
 using mozilla::ArrayLength;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
+struct js::Nursery::FreeHugeSlotsTask : public GCParallelTask
+{
+    explicit FreeHugeSlotsTask(FreeOp *fop) : fop_(fop) {}
+    bool init() { return slots_.init(); }
+    void transferSlotsToFree(HugeSlotsSet &slotsToFree);
+
+  private:
+    FreeOp *fop_;
+    HugeSlotsSet slots_;
+
+    virtual void run() MOZ_OVERRIDE;
+};
+
 bool
 js::Nursery::init(uint32_t maxNurseryBytes)
 {
     /* maxNurseryBytes parameter is rounded down to a multiple of chunk size. */
     numNurseryChunks_ = maxNurseryBytes >> ChunkShift;
 
     /* If no chunks are specified then the nursery is permenantly disabled. */
     if (numNurseryChunks_ == 0)
@@ -48,16 +62,20 @@ js::Nursery::init(uint32_t maxNurseryByt
 
     if (!hugeSlots.init())
         return false;
 
     void *heap = MapAlignedPages(nurserySize(), Alignment);
     if (!heap)
         return false;
 
+    freeHugeSlotsTask = js_new<FreeHugeSlotsTask>(runtime()->defaultFreeOp());
+    if (!freeHugeSlotsTask || !freeHugeSlotsTask->init())
+        return false;
+
     heapStart_ = uintptr_t(heap);
     heapEnd_ = heapStart_ + nurserySize();
     currentStart_ = start();
     numActiveChunks_ = 1;
     JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, nurserySize());
     setCurrentChunk(0);
     updateDecommittedRegion();
 
@@ -75,16 +93,18 @@ js::Nursery::init(uint32_t maxNurseryByt
     MOZ_ASSERT(isEnabled());
     return true;
 }
 
 js::Nursery::~Nursery()
 {
     if (start())
         UnmapPages((void *)start(), nurserySize());
+
+    js_delete(freeHugeSlotsTask);
 }
 
 void
 js::Nursery::updateDecommittedRegion()
 {
 #ifndef JS_GC_ZEAL
     if (numActiveChunks_ < numNurseryChunks_) {
         // Bug 994054: madvise on MacOS is too slow to make this
@@ -666,25 +686,26 @@ js::Nursery::moveObjectToTenured(MinorCo
     if (src->is<ArrayObject>())
         tenuredSize = srcSize = sizeof(NativeObject);
 
     js_memcpy(dst, src, srcSize);
     if (src->isNative()) {
         NativeObject *ndst = &dst->as<NativeObject>(), *nsrc = &src->as<NativeObject>();
         tenuredSize += moveSlotsToTenured(ndst, nsrc, dstKind);
         tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind);
+
+        // The shape's list head may point into the old object. This can only
+        // happen for dictionaries, which are native objects.
+        if (&nsrc->shape_ == ndst->shape_->listp)
+            ndst->shape_->listp = &ndst->shape_;
     }
 
     if (src->is<InlineTypedObject>())
         InlineTypedObject::objectMovedDuringMinorGC(trc, dst, src);
 
-    /* The shape's list head may point into the old object. */
-    if (&src->shape_ == dst->shape_->listp)
-        dst->shape_->listp = &dst->shape_;
-
     return tenuredSize;
 }
 
 MOZ_ALWAYS_INLINE size_t
 js::Nursery::moveSlotsToTenured(NativeObject *dst, NativeObject *src, AllocKind dstKind)
 {
     /* Fixed slots have already been copied over. */
     if (!src->hasDynamicSlots())
@@ -959,22 +980,57 @@ js::Nursery::collect(JSRuntime *rt, JS::
     }
 }
 
 #undef TIME_START
 #undef TIME_END
 #undef TIME_TOTAL
 
 void
+js::Nursery::FreeHugeSlotsTask::transferSlotsToFree(HugeSlotsSet &slotsToFree)
+{
+    // Transfer the contents of the source set to the task's slots_ member by
+    // swapping the sets, which also clears the source.
+    MOZ_ASSERT(!isRunning());
+    MOZ_ASSERT(slots_.empty());
+    mozilla::Swap(slots_, slotsToFree);
+}
+
+void
+js::Nursery::FreeHugeSlotsTask::run()
+{
+    for (HugeSlotsSet::Range r = slots_.all(); !r.empty(); r.popFront())
+        fop_->free_(r.front());
+    slots_.clear();
+}
+
+void
 js::Nursery::freeHugeSlots()
 {
-    FreeOp *fop = runtime()->defaultFreeOp();
-    for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront())
-        fop->free_(r.front());
-    hugeSlots.clear();
+    if (hugeSlots.empty())
+        return;
+
+    bool started;
+    {
+        AutoLockHelperThreadState lock;
+        freeHugeSlotsTask->joinWithLockHeld();
+        freeHugeSlotsTask->transferSlotsToFree(hugeSlots);
+        started = freeHugeSlotsTask->startWithLockHeld();
+    }
+
+    if (!started)
+        freeHugeSlotsTask->runFromMainThread(runtime());
+
+    MOZ_ASSERT(hugeSlots.empty());
+}
+
+void
+js::Nursery::waitBackgroundFreeEnd()
+{
+    freeHugeSlotsTask->join();
 }
 
 void
 js::Nursery::runFinalizers()
 {
     verifyFinalizerList();
 
     FreeOp *fop = runtime()->defaultFreeOp();
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -61,17 +61,18 @@ class Nursery
         currentEnd_(0),
         heapStart_(0),
         heapEnd_(0),
         currentChunk_(0),
         numActiveChunks_(0),
         numNurseryChunks_(0),
         finalizers_(nullptr),
         profileThreshold_(0),
-        enableProfiling_(false)
+        enableProfiling_(false),
+        freeHugeSlotsTask(nullptr)
     {}
     ~Nursery();
 
     bool init(uint32_t maxNurseryBytes);
 
     bool exists() const { return numNurseryChunks_ != 0; }
     size_t numChunks() const { return numNurseryChunks_; }
     size_t nurserySize() const { return numNurseryChunks_ << ChunkShift; }
@@ -134,16 +135,18 @@ class Nursery
     /* Forward a slots/elements pointer stored in an Ion frame. */
     void forwardBufferPointer(HeapSlot **pSlotsElems);
 
     void maybeSetForwardingPointer(JSTracer *trc, void *oldData, void *newData, bool direct) {
         if (IsMinorCollectionTracer(trc) && isInside(oldData))
             setForwardingPointer(oldData, newData, direct);
     }
 
+    void waitBackgroundFreeEnd();
+
     size_t sizeOfHeapCommitted() const {
         return numActiveChunks_ * gc::ChunkSize;
     }
     size_t sizeOfHeapDecommitted() const {
         return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize;
     }
     size_t sizeOfHugeSlots(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
@@ -217,16 +220,20 @@ class Nursery
     /*
      * The set of externally malloced slots potentially kept live by objects
      * stored in the nursery. Any external slots that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
     typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet;
     HugeSlotsSet hugeSlots;
 
+    /* A task structure used to free the huge slots on a background thread. */
+    struct FreeHugeSlotsTask;
+    FreeHugeSlotsTask *freeHugeSlotsTask;
+
     /*
      * During a collection most hoisted slot and element buffers indicate their
      * new location with a forwarding pointer at the base. This does not work
      * for buffers whose length is less than pointer width, or when different
      * buffers might overlap each other. For these, an entry in the following
      * table is used.
      */
     typedef HashMap<void *, void *, PointerHasher<void *, 1>, SystemAllocPolicy> ForwardedBufferMap;
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -425,17 +425,17 @@ js::gc::GCRuntime::markRuntime(JSTracer 
 
     if (traceOrMark == MarkRuntime) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_CCWS);
 
         for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
             if (!c->zone()->isCollecting())
                 c->markCrossCompartmentWrappers(trc);
         }
-        Debugger::markAllCrossCompartmentEdges(trc);
+        Debugger::markIncomingCrossCompartmentEdges(trc);
     }
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTERS);
 
         AutoGCRooter::traceAll(trc);
 
         if (!rt->isBeingDestroyed()) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/SIMD/load.js
@@ -0,0 +1,48 @@
+load(libdir + 'simd.js');
+
+setJitCompilerOption("ion.warmup.trigger", 40);
+
+function f() {
+    var f32 = new Float32Array(16);
+    for (var i = 0; i < 16; i++)
+        f32[i] = i + 1;
+
+    var f64 = new Float64Array(f32.buffer);
+    var i32 = new Int32Array(f32.buffer);
+    var u32 = new Uint32Array(f32.buffer);
+    var i16 = new Int16Array(f32.buffer);
+    var u16 = new Uint16Array(f32.buffer);
+    var i8  = new Int8Array(f32.buffer);
+    var u8  = new Uint8Array(f32.buffer);
+
+    var r;
+    for (var i = 0; i < 150; i++) {
+        assertEqX4(SIMD.float32x4.load(f64, 0), [1,2,3,4]);
+        assertEqX4(SIMD.float32x4.load(f32, 1), [2,3,4,5]);
+        assertEqX4(SIMD.float32x4.load(i32, 2), [3,4,5,6]);
+        assertEqX4(SIMD.float32x4.load(i16, 3 << 1), [4,5,6,7]);
+        assertEqX4(SIMD.float32x4.load(u16, 4 << 1), [5,6,7,8]);
+        assertEqX4(SIMD.float32x4.load(i8 , 5 << 2), [6,7,8,9]);
+        assertEqX4(SIMD.float32x4.load(u8 , 6 << 2), [7,8,9,10]);
+
+        assertEqX4(SIMD.float32x4.load(f64, (16 >> 1) - (4 >> 1)), [13,14,15,16]);
+        assertEqX4(SIMD.float32x4.load(f32, 16 - 4),               [13,14,15,16]);
+        assertEqX4(SIMD.float32x4.load(i32, 16 - 4),               [13,14,15,16]);
+        assertEqX4(SIMD.float32x4.load(i16, (16 << 1) - (4 << 1)), [13,14,15,16]);
+        assertEqX4(SIMD.float32x4.load(u16, (16 << 1) - (4 << 1)), [13,14,15,16]);
+        assertEqX4(SIMD.float32x4.load(i8,  (16 << 2) - (4 << 2)), [13,14,15,16]);
+        assertEqX4(SIMD.float32x4.load(u8,  (16 << 2) - (4 << 2)), [13,14,15,16]);
+
+        var caught = false;
+        try {
+            SIMD.float32x4.load(i8, (i < 149) ? 0 : (16 << 2) - (4 << 2) + 1);
+        } catch (e) {
+            caught = true;
+        }
+        assertEq(i < 149 || caught, true);
+    }
+    return r
+}
+
+f();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/SIMD/store.js
@@ -0,0 +1,62 @@
+load(libdir + 'simd.js');
+
+setJitCompilerOption("ion.warmup.trigger", 40);
+
+function f() {
+    var f32 = new Float32Array(16);
+    for (var i = 0; i < 16; i++)
+        f32[i] = i + 1;
+
+    var f64 = new Float64Array(f32.buffer);
+    var i32 = new Int32Array(f32.buffer);
+    var u32 = new Uint32Array(f32.buffer);
+    var i16 = new Int16Array(f32.buffer);
+    var u16 = new Uint16Array(f32.buffer);
+    var i8  = new Int8Array(f32.buffer);
+    var u8  = new Uint8Array(f32.buffer);
+
+    var f4 = SIMD.float32x4(42, 43, 44, 45);
+
+    function check() {
+        assertEq(f32[0], 42);
+        assertEq(f32[1], 43);
+        assertEq(f32[2], 44);
+        assertEq(f32[3], 45);
+
+        f32[0] = 1;
+        f32[1] = 2;
+        f32[2] = 3;
+        f32[3] = 4;
+    }
+
+    for (var i = 0; i < 150; i++) {
+        SIMD.float32x4.store(f64, 0, f4);
+        check();
+        SIMD.float32x4.store(f32, 0, f4);
+        check();
+        SIMD.float32x4.store(i32, 0, f4);
+        check();
+        SIMD.float32x4.store(u32, 0, f4);
+        check();
+        SIMD.float32x4.store(i16, 0, f4);
+        check();
+        SIMD.float32x4.store(u16, 0, f4);
+        check();
+        SIMD.float32x4.store(i8, 0, f4);
+        check();
+        SIMD.float32x4.store(u8, 0, f4);
+        check();
+
+        var caught = false;
+        try {
+            SIMD.float32x4.store(i8, (i < 149) ? 0 : (16 << 2) - (4 << 2) + 1, f4);
+            check();
+        } catch (e) {
+            caught = true;
+        }
+        assertEq(i < 149 || caught, true);
+    }
+}
+
+f();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1136597.js
@@ -0,0 +1,24 @@
+// |jit-test| error:ReferenceError
+var evalInFrame = (function (global) {
+  var dbgGlobal = newGlobal();
+  var dbg = new dbgGlobal.Debugger();
+  return function evalInFrame(upCount, code) {
+    dbg.addDebuggee(global);
+  };
+})(this);
+var gTestcases = new Array();
+var gTc = gTestcases.length;
+function TestCase()
+  gTestcases[gTc++] = this;
+function checkCollation(extensionCoValue, usageValue) {
+    var collator = new Intl.Collator(["de-DE"]);
+    collator.resolvedOptions().collation;
+}
+checkCollation(undefined, "sort");
+checkCollation();
+for ( addpow = 0; addpow < 33; addpow++ ) {
+    new TestCase();
+}
+evalInFrame(0, "i(true)", true);
+gc(3, 'shrinking')
+eval("gc(); h = g1");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1137341.js
@@ -0,0 +1,8 @@
+if (helperThreadCount() == 0)
+   quit();
+
+schedulegc(this);
+startgc(0, "shrinking");
+var g = newGlobal();
+g.offThreadCompileScript('debugger;', {});
+g.runOffThreadScript();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3436,17 +3436,17 @@ IsCacheableGetPropCall(JSContext *cx, JS
 
 static bool
 IsCacheableSetPropWriteSlot(JSObject *obj, Shape *oldShape, JSObject *holder, Shape *shape)
 {
     if (!shape)
         return false;
 
     // Object shape must not have changed during the property set.
-    if (obj->lastProperty() != oldShape)
+    if (!obj->isNative() || obj->as<NativeObject>().lastProperty() != oldShape)
         return false;
 
     // Currently we only optimize direct writes.
     if (obj != holder)
         return false;
 
     if (!shape->hasSlot() || !shape->hasDefaultSetter() || !shape->writable())
         return false;
@@ -3458,21 +3458,21 @@ static bool
 IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape, uint32_t oldSlots,
                           HandleId id, HandleObject holder, HandleShape shape,
                           size_t *protoChainDepth)
 {
     if (!shape)
         return false;
 
     // Property must be set directly on object, and be last added property of object.
-    if (obj != holder || shape != obj->lastProperty())
+    if (!obj->isNative() || obj != holder || shape != obj->as<NativeObject>().lastProperty())
         return false;
 
     // Object must be extensible, oldShape must be immediate parent of curShape.
-    if (!obj->nonProxyIsExtensible() || obj->lastProperty()->previous() != oldShape)
+    if (!obj->nonProxyIsExtensible() || shape->previous() != oldShape)
         return false;
 
     // Basic shape checks.
     if (shape->inDictionary() || !shape->hasSlot() || !shape->hasDefaultSetter() ||
         !shape->writable())
     {
         return false;
     }
@@ -3557,17 +3557,17 @@ LookupNoSuchMethodHandler(JSContext *cx,
 }
 
 typedef bool (*LookupNoSuchMethodHandlerFn)(JSContext *, HandleObject, HandleValue,
                                             MutableHandleValue);
 static const VMFunction LookupNoSuchMethodHandlerInfo =
     FunctionInfo<LookupNoSuchMethodHandlerFn>(LookupNoSuchMethodHandler);
 
 static bool
-GetElemNativeStubExists(ICGetElem_Fallback *stub, HandleObject obj, HandleObject holder,
+GetElemNativeStubExists(ICGetElem_Fallback *stub, HandleNativeObject obj, HandleNativeObject holder,
                         HandlePropertyName propName, bool needsAtomize)
 {
     bool indirect = (obj.get() != holder.get());
 
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->kind() != ICStub::GetElem_NativeSlot &&
             iter->kind() != ICStub::GetElem_NativePrototypeSlot &&
             iter->kind() != ICStub::GetElem_NativePrototypeCallNative &&
@@ -3621,18 +3621,18 @@ GetElemNativeStubExists(ICGetElem_Fallba
         }
 
         return true;
     }
     return false;
 }
 
 static void
-RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, HandleObject obj,
-                                 HandleObject holder, HandlePropertyName propName,
+RemoveExistingGetElemNativeStubs(JSContext *cx, ICGetElem_Fallback *stub, HandleNativeObject obj,
+                                 HandleNativeObject holder, HandlePropertyName propName,
                                  bool needsAtomize)
 {
     bool indirect = (obj.get() != holder.get());
 
     for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
         switch (iter->kind()) {
           case ICStub::GetElem_NativeSlot:
             if (indirect)
@@ -3699,17 +3699,17 @@ RemoveExistingGetElemNativeStubs(JSConte
 }
 
 static bool
 TypedArrayGetElemStubExists(ICGetElem_Fallback *stub, HandleObject obj)
 {
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (!iter->isGetElem_TypedArray())
             continue;
-        if (obj->lastProperty() == iter->toGetElem_TypedArray()->shape())
+        if (obj->maybeShape() == iter->toGetElem_TypedArray()->shape())
             return true;
     }
     return false;
 }
 
 static bool
 ArgumentsGetElemStubExists(ICGetElem_Fallback *stub, ICGetElem_Arguments::Which which)
 {
@@ -3720,17 +3720,17 @@ ArgumentsGetElemStubExists(ICGetElem_Fal
             return true;
     }
     return false;
 }
 
 
 static bool
 TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc,
-                           ICGetElem_Fallback *stub, HandleObject obj,
+                           ICGetElem_Fallback *stub, HandleNativeObject obj,
                            HandleValue key)
 {
     // Native-object GetElem stubs can't deal with non-string keys.
     if (!key.isString())
         return true;
 
     // Convert to interned property name.
     RootedId id(cx);
@@ -3741,19 +3741,23 @@ TryAttachNativeGetElemStub(JSContext *cx
     if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
         return true;
 
     RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName());
     bool needsAtomize = !key.toString()->isAtom();
     bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM);
 
     RootedShape shape(cx);
-    RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, propName, &holder, &shape))
-        return false;
+    RootedObject baseHolder(cx);
+    if (!EffectlesslyLookupProperty(cx, obj, propName, &baseHolder, &shape))
+        return false;
+    if (!baseHolder || !baseHolder->isNative())
+        return true;
+
+    HandleNativeObject holder = baseHolder.as<NativeObject>();
 
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         // If a suitable stub already exists, nothing else to do.
         if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
             return true;
 
         // Remove any existing stubs that may interfere with the new stub being added.
         RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
@@ -3782,17 +3786,17 @@ TryAttachNativeGetElemStub(JSContext *cx
             return false;
 
         stub->addNewStub(newStub);
         return true;
     }
 
     bool getterIsScripted = false;
     bool isTemporarilyUnoptimizable = false;
-    if (IsCacheableGetPropCall(cx, obj, holder, shape, &getterIsScripted,
+    if (IsCacheableGetPropCall(cx, obj, baseHolder, shape, &getterIsScripted,
                                &isTemporarilyUnoptimizable, /*isDOMProxy=*/false)) {
         RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
 
 #if JS_HAS_NO_SUCH_METHOD
         // It's unlikely that a getter function will be used in callelem locations.
         // Just don't attach stubs in that case to avoid issues with __noSuchMethod__ handling.
         if (isCallElem)
             return true;
@@ -3944,29 +3948,29 @@ TryAttachGetElemStub(JSContext *cx, JSSc
         }
     }
 
     if (obj->isNative()) {
         // Check for NativeObject[int] dense accesses.
         if (rhs.isInt32() && rhs.toInt32() >= 0 && !IsAnyTypedArray(obj.get())) {
             JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native[Int32] dense) stub");
             ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                               obj->lastProperty(), isCallElem);
+                                               obj->as<NativeObject>().lastProperty(), isCallElem);
             ICStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
             if (!denseStub)
                 return false;
 
             stub->addNewStub(denseStub);
             return true;
         }
 
         // Check for NativeObject[id] shape-optimizable accesses.
         if (rhs.isString()) {
             RootedScript rootedScript(cx, script);
-            if (!TryAttachNativeGetElemStub(cx, rootedScript, pc, stub, obj, rhs))
+            if (!TryAttachNativeGetElemStub(cx, rootedScript, pc, stub, obj.as<NativeObject>(), rhs))
                 return false;
             script = rootedScript;
         }
     }
 
     // Check for TypedArray[int] => Number and TypedObject[int] => Number accesses.
     if ((IsAnyTypedArray(obj.get()) || IsPrimitiveArrayTypedObject(obj)) &&
         rhs.isNumber() &&
@@ -3986,18 +3990,17 @@ TryAttachGetElemStub(JSContext *cx, JSSc
         }
 
         // Don't attach typed object stubs if they might be neutered, as the
         // stub will always bail out.
         if (IsPrimitiveArrayTypedObject(obj) && cx->compartment()->neuteredTypedObjects)
             return true;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(TypedArray[Int32]) stub");
-        ICGetElem_TypedArray::Compiler compiler(cx, obj->lastProperty(),
-                                                TypedThingElementType(obj));
+        ICGetElem_TypedArray::Compiler compiler(cx, obj->maybeShape(), TypedThingElementType(obj));
         ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
         if (!typedArrayStub)
             return false;
 
         stub->addNewStub(typedArrayStub);
         return true;
     }
 
@@ -4882,33 +4885,39 @@ ICGetElem_Arguments::Compiler::generateS
     return true;
 }
 
 //
 // SetElem_Fallback
 //
 
 static bool
-SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd *stub, JSObject *obj)
+SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd *stub, NativeObject *obj)
 {
     size_t numShapes = stub->protoChainDepth() + 1;
     for (size_t i = 0; i < numShapes; i++) {
         static const size_t MAX_DEPTH = ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH;
         if (obj->lastProperty() != stub->toImplUnchecked<MAX_DEPTH>()->shape(i))
             return false;
-        obj = obj->getProto();
-        if (!obj && i != numShapes - 1)
-            return false;
-    }
-
-    return true;
-}
-
-static bool
-DenseSetElemStubExists(JSContext *cx, ICStub::Kind kind, ICSetElem_Fallback *stub, HandleObject obj)
+        JSObject *proto = obj->getProto();
+        if (!proto) {
+            if (i != numShapes - 1)
+                return false;
+            break;
+        }
+        if (!proto->isNative())
+            return false;
+        obj = &proto->as<NativeObject>();
+    }
+
+    return true;
+}
+
+static bool
+DenseSetElemStubExists(JSContext *cx, ICStub::Kind kind, ICSetElem_Fallback *stub, HandleNativeObject obj)
 {
     MOZ_ASSERT(kind == ICStub::SetElem_Dense || kind == ICStub::SetElem_DenseAdd);
 
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (kind == ICStub::SetElem_Dense && iter->isSetElem_Dense()) {
             ICSetElem_Dense *dense = iter->toSetElem_Dense();
             if (obj->lastProperty() == dense->shape() && obj->getGroup(cx) == dense->group())
                 return true;
@@ -4925,30 +4934,30 @@ DenseSetElemStubExists(JSContext *cx, IC
 
 static bool
 TypedArraySetElemStubExists(ICSetElem_Fallback *stub, HandleObject obj, bool expectOOB)
 {
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (!iter->isSetElem_TypedArray())
             continue;
         ICSetElem_TypedArray *taStub = iter->toSetElem_TypedArray();
-        if (obj->lastProperty() == taStub->shape() && taStub->expectOutOfBounds() == expectOOB)
+        if (obj->maybeShape() == taStub->shape() && taStub->expectOutOfBounds() == expectOOB)
             return true;
     }
     return false;
 }
 
 static bool
 RemoveExistingTypedArraySetElemStub(JSContext *cx, ICSetElem_Fallback *stub, HandleObject obj)
 {
     for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
         if (!iter->isSetElem_TypedArray())
             continue;
 
-        if (obj->lastProperty() != iter->toSetElem_TypedArray()->shape())
+        if (obj->maybeShape() != iter->toSetElem_TypedArray()->shape())
             continue;
 
         // TypedArraySetElem stubs are only removed using this procedure if
         // being replaced with one that expects out of bounds index.
         MOZ_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds());
         iter.unlink(cx);
         return true;
     }
@@ -5044,17 +5053,17 @@ DoSetElemFallback(JSContext *cx, Baselin
                op == JSOP_INITELEM ||
                op == JSOP_INITELEM_ARRAY ||
                op == JSOP_INITELEM_INC);
 
     RootedObject obj(cx, ToObjectFromStack(cx, objv));
     if (!obj)
         return false;
 
-    RootedShape oldShape(cx, obj->lastProperty());
+    RootedShape oldShape(cx, obj->maybeShape());
 
     // Check the old capacity
     uint32_t oldCapacity = 0;
     uint32_t oldInitLength = 0;
     if (obj->isNative() && index.isInt32() && index.toInt32() >= 0) {
         oldCapacity = obj->as<NativeObject>().getDenseCapacity();
         oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
     }
@@ -5089,47 +5098,49 @@ DoSetElemFallback(JSContext *cx, Baselin
     }
 
     // Try to generate new stubs.
     if (obj->isNative() &&
         !IsAnyTypedArray(obj.get()) &&
         index.isInt32() && index.toInt32() >= 0 &&
         !rhs.isMagic(JS_ELEMENTS_HOLE))
     {
+        HandleNativeObject nobj = obj.as<NativeObject>();
+
         bool addingCase;
         size_t protoDepth;
 
-        if (CanOptimizeDenseSetElem(&obj->as<NativeObject>(), index.toInt32(),
+        if (CanOptimizeDenseSetElem(nobj, index.toInt32(),
                                     oldShape, oldCapacity, oldInitLength,
                                     &addingCase, &protoDepth))
         {
-            RootedShape shape(cx, obj->lastProperty());
+            RootedShape shape(cx, nobj->lastProperty());
             RootedObjectGroup group(cx, obj->getGroup(cx));
             if (!group)
                 return false;
 
-            if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, obj)) {
+            if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, nobj)) {
                 JitSpew(JitSpew_BaselineIC,
                         "  Generating SetElem_DenseAdd stub "
                         "(shape=%p, group=%p, protoDepth=%u)",
-                        obj->lastProperty(), group.get(), protoDepth);
+                        nobj->lastProperty(), group.get(), protoDepth);
                 ICSetElemDenseAddCompiler compiler(cx, obj, protoDepth);
                 ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
                 if (!denseStub)
                     return false;
                 if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs))
                     return false;
 
                 stub->addNewStub(denseStub);
             } else if (!addingCase &&
-                       !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, obj))
+                       !DenseSetElemStubExists(cx, ICStub::SetElem_Dense, stub, nobj))
             {
                 JitSpew(JitSpew_BaselineIC,
                         "  Generating SetElem_Dense stub (shape=%p, group=%p)",
-                        obj->lastProperty(), group.get());
+                        nobj->lastProperty(), group.get());
                 ICSetElem_Dense::Compiler compiler(cx, shape, group);
                 ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
                 if (!denseStub)
                     return false;
                 if (!denseStub->addUpdateStubForValue(cx, script, obj, JSID_VOIDHANDLE, rhs))
                     return false;
 
                 stub->addNewStub(denseStub);
@@ -5166,17 +5177,17 @@ DoSetElemFallback(JSContext *cx, Baselin
                 return true;
         }
 
         if (!TypedArraySetElemStubExists(stub, obj, expectOutOfBounds)) {
             // Remove any existing TypedArraySetElemStub that doesn't handle out-of-bounds
             if (expectOutOfBounds)
                 RemoveExistingTypedArraySetElemStub(cx, stub, obj);
 
-            Shape *shape = obj->lastProperty();
+            Shape *shape = obj->maybeShape();
             Scalar::Type type = TypedThingElementType(obj);
 
             JitSpew(JitSpew_BaselineIC,
                     "  Generating SetElem_TypedArray stub (shape=%p, type=%u, oob=%s)",
                     shape, type, expectOutOfBounds ? "yes" : "no");
             ICSetElem_TypedArray::Compiler compiler(cx, shape, type, expectOutOfBounds);
             ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
             if (!typedArrayStub)
@@ -5368,33 +5379,33 @@ ICSetElem_Dense::Compiler::generateStubC
     return true;
 }
 
 static bool
 GetProtoShapes(JSObject *obj, size_t protoChainDepth, AutoShapeVector *shapes)
 {
     JSObject *curProto = obj->getProto();
     for (size_t i = 0; i < protoChainDepth; i++) {
-        if (!shapes->append(curProto->lastProperty()))
+        if (!shapes->append(curProto->as<NativeObject>().lastProperty()))
             return false;
         curProto = curProto->getProto();
     }
     MOZ_ASSERT(!curProto);
     return true;
 }
 
 //
 // SetElem_DenseAdd
 //
 
 ICUpdatedStub *
 ICSetElemDenseAddCompiler::getStub(ICStubSpace *space)
 {
     AutoShapeVector shapes(cx);
-    if (!shapes.append(obj_->lastProperty()))
+    if (!shapes.append(obj_->as<NativeObject>().lastProperty()))
         return nullptr;
 
     if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
         return nullptr;
 
     JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4);
 
     ICUpdatedStub *stub = nullptr;
@@ -5757,17 +5768,17 @@ ICIn_Fallback::Compiler::generateStubCod
 // given holder in place with a new shape and getter.  fallbackStub can be
 // either an ICGetProp_Fallback or an ICGetName_Fallback.
 //
 // When kind == ICStub::GetProp_CallNative, callers should pass a null receiver,
 // since in that case receiver and holder are the same thing.
 static bool
 UpdateExistingGetPropCallStubs(ICFallbackStub* fallbackStub,
                                ICStub::Kind kind,
-                               HandleObject holder,
+                               HandleNativeObject holder,
                                HandleObject receiver,
                                HandleFunction getter)
 {
     MOZ_ASSERT(kind == ICStub::GetProp_CallScripted ||
                kind == ICStub::GetProp_CallNative ||
                kind == ICStub::GetProp_CallNativePrototype);
     MOZ_ASSERT(fallbackStub->isGetName_Fallback() ||
                fallbackStub->isGetProp_Fallback());
@@ -5810,17 +5821,17 @@ UpdateExistingGetPropCallStubs(ICFallbac
     return foundMatchingStub;
 }
 
 // Try to update existing SetProp setter call stubs for the given holder in
 // place with a new shape and setter.
 static bool
 UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
                                ICStub::Kind kind,
-                               JSObject *holder,
+                               NativeObject *holder,
                                ReceiverGuard::Token receiverGuard,
                                JSFunction *setter)
 {
     MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
                kind == ICStub::SetProp_CallNative);
     bool foundMatchingStub = false;
     for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->kind() == kind) {
@@ -5958,17 +5969,17 @@ TryAttachScopeNameStub(JSContext *cx, Ha
     MOZ_ASSERT(!*attached);
 
     AutoShapeVector shapes(cx);
     RootedId id(cx, NameToId(name));
     RootedObject scopeChain(cx, initialScopeChain);
 
     Shape *shape = nullptr;
     while (scopeChain) {
-        if (!shapes.append(scopeChain->lastProperty()))
+        if (!shapes.append(scopeChain->maybeShape()))
             return false;
 
         if (scopeChain->is<GlobalObject>()) {
             shape = scopeChain->as<GlobalObject>().lookup(cx, id);
             if (shape)
                 break;
             return true;
         }
@@ -6487,17 +6498,17 @@ TryAttachNativeGetPropStub(JSContext *cx
     MOZ_ASSERT(!*attached);
     MOZ_ASSERT(!*isTemporarilyUnoptimizable);
 
     if (!val.isObject())
         return true;
 
     RootedObject obj(cx, &val.toObject());
 
-    if (oldShape != obj->lastProperty()) {
+    if (obj->isNative() && oldShape != obj->as<NativeObject>().lastProperty()) {
         // No point attaching anything, since we know the shape guard will fail
         return true;
     }
 
     bool isDOMProxy;
     bool domProxyHasGeneration;
     DOMProxyShadowsResult domProxyShadowsResult;
     RootedShape shape(cx);
@@ -6560,17 +6571,17 @@ TryAttachNativeGetPropStub(JSContext *cx
         if (obj == holder)
             return true;
 
         RootedFunction callee(cx, &shape->getterObject()->as<JSFunction>());
         MOZ_ASSERT(obj != holder);
         MOZ_ASSERT(callee->hasScript());
 
         if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallScripted,
-                                           holder, obj, callee)) {
+                                           holder.as<NativeObject>(), obj, callee)) {
             *attached = true;
             return true;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetProp(NativeObj/ScriptedGetter %s:%" PRIuSIZE ") stub",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
 
         ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee,
@@ -6666,27 +6677,27 @@ TryAttachNativeGetPropStub(JSContext *cx
             kind = ICStub::GetProp_CallDOMProxyNative;
         }
         Rooted<ProxyObject*> proxy(cx, &obj->as<ProxyObject>());
         ICGetPropCallDOMProxyNativeCompiler compiler(cx, kind, monitorStub, proxy, holder, callee,
                                                      script->pcToOffset(pc));
         newStub = compiler.getStub(compiler.getStubSpace(script));
     } else if (obj == holder) {
         if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNative,
-                                           obj, JS::NullPtr(), callee)) {
+                                           obj.as<NativeObject>(), JS::NullPtr(), callee)) {
             *attached = true;
             return true;
         }
 
         ICGetProp_CallNative::Compiler compiler(cx, monitorStub, obj, callee,
                                                 script->pcToOffset(pc), outerClass);
         newStub = compiler.getStub(compiler.getStubSpace(script));
     } else {
         if (UpdateExistingGetPropCallStubs(stub, ICStub::GetProp_CallNativePrototype,
-                                           holder, obj, callee)) {
+                                           holder.as<NativeObject>(), obj, callee)) {
             *attached = true;
             return true;
         }
 
         ICGetProp_CallNativePrototype::Compiler compiler(cx, monitorStub, obj, holder, callee,
                                                          script->pcToOffset(pc), outerClass);
         newStub = compiler.getStub(compiler.getStubSpace(script));
     }
@@ -6755,17 +6766,17 @@ TryAttachTypedObjectGetPropStub(JSContex
 
     Rooted<TypeDescr *> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
     if (!fieldDescr->is<SimpleTypeDescr>())
         return true;
 
     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
 
-    ICGetProp_TypedObject::Compiler compiler(cx, monitorStub, obj->lastProperty(),
+    ICGetProp_TypedObject::Compiler compiler(cx, monitorStub, obj->maybeShape(),
                                              fieldOffset, &fieldDescr->as<SimpleTypeDescr>());
     ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
     stub->addNewStub(newStub);
 
     *attached = true;
     return true;
@@ -6918,17 +6929,17 @@ DoGetPropFallback(JSContext *cx, Baselin
     JSOp op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetProp(%s)", js_CodeName[op]);
 
     MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH || op == JSOP_GETXPROP);
 
     // Grab our old shape before it goes away.
     RootedShape oldShape(cx);
     if (val.isObject())
-        oldShape = val.toObject().lastProperty();
+        oldShape = val.toObject().maybeShape();
 
     // After the  Genericstub was added, we should never reach the Fallbackstub again.
     MOZ_ASSERT(!stub->hasStub(ICStub::GetProp_Generic));
 
     RootedPropertyName name(cx, frame->script()->getName(pc));
     if (!ComputeGetPropResult(cx, frame, op, name, val, res))
         return false;
 
@@ -7162,24 +7173,24 @@ ICGetProp_Primitive::Compiler::generateS
 }
 
 ICGetPropNativeStub *
 ICGetPropNativeCompiler::getStub(ICStubSpace *space)
 {
     switch (kind) {
       case ICStub::GetProp_Native: {
         MOZ_ASSERT(obj_ == holder_);
-        RootedShape shape(cx, obj_->lastProperty());
+        RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
         return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, shape, offset_);
       }
 
       case ICStub::GetProp_NativePrototype: {
         MOZ_ASSERT(obj_ != holder_);
         ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
-        Shape *holderShape = holder_->lastProperty();
+        Shape *holderShape = holder_->as<NativeObject>().lastProperty();
         return ICStub::New<ICGetProp_NativePrototype>(space, getStubCode(), firstMonitorStub_, guard,
                                                       offset_, holder_, holderShape);
       }
 
       default:
         MOZ_CRASH("Bad stub kind");
     }
 }
@@ -7686,18 +7697,18 @@ ICGetPropCallDOMProxyNativeCompiler::gen
     Address generationAddress(BaselineStubReg,
         ICGetProp_CallDOMProxyWithGenerationNative::offsetOfGeneration());
     return generateStubCode(masm, &internalStructAddress, &generationAddress);
 }
 
 ICStub *
 ICGetPropCallDOMProxyNativeCompiler::getStub(ICStubSpace *space)
 {
-    RootedShape shape(cx, proxy_->lastProperty());
-    RootedShape holderShape(cx, holder_->lastProperty());
+    RootedShape shape(cx, proxy_->maybeShape());
+    RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
 
     Value expandoSlot = GetProxyExtra(proxy_, GetDOMProxyExpandoSlot());
     RootedShape expandoShape(cx, nullptr);
     ExpandoAndGeneration *expandoAndGeneration;
     int32_t generation;
     Value expandoVal;
     if (kind == ICStub::GetProp_CallDOMProxyNative) {
         expandoVal = expandoSlot;
@@ -7707,34 +7718,34 @@ ICGetPropCallDOMProxyNativeCompiler::get
         MOZ_ASSERT(kind == ICStub::GetProp_CallDOMProxyWithGenerationNative);
         MOZ_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
         expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate();
         expandoVal = expandoAndGeneration->expando;
         generation = expandoAndGeneration->generation;
     }
 
     if (expandoVal.isObject())
-        expandoShape = expandoVal.toObject().lastProperty();
+        expandoShape = expandoVal.toObject().as<NativeObject>().lastProperty();
 
     if (kind == ICStub::GetProp_CallDOMProxyNative) {
         return ICStub::New<ICGetProp_CallDOMProxyNative>(
             space, getStubCode(), firstMonitorStub_, shape,
             expandoShape, holder_, holderShape, getter_, pcOffset_);
     }
 
     return ICStub::New<ICGetProp_CallDOMProxyWithGenerationNative>(
         space, getStubCode(), firstMonitorStub_, shape,
         expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_,
         pcOffset_);
 }
 
 ICStub *
 ICGetProp_DOMProxyShadowed::Compiler::getStub(ICStubSpace *space)
 {
-    RootedShape shape(cx, proxy_->lastProperty());
+    RootedShape shape(cx, proxy_->maybeShape());
     return New<ICGetProp_DOMProxyShadowed>(space, getStubCode(), firstMonitorStub_, shape,
                                            proxy_->handler(), name_, pcOffset_);
 }
 
 static bool
 ProxyGet(JSContext *cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp)
 {
     RootedId id(cx, NameToId(name));
@@ -8110,17 +8121,17 @@ TryAttachSetValuePropStub(JSContext *cx,
             return true;
         }
 
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(&obj->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.PROP) stub");
-        MOZ_ASSERT(obj->lastProperty() == oldShape,
+        MOZ_ASSERT(obj->as<NativeObject>().lastProperty() == oldShape,
                    "Should this really be a SetPropWriteSlot?");
         ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
         ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
         if (!newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
             return false;
 
@@ -8162,17 +8173,17 @@ TryAttachSetAccessorPropStub(JSContext *
 
     // Try handling scripted setters.
     if (cacheableCall && isScripted) {
         RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
         MOZ_ASSERT(obj != holder);
         MOZ_ASSERT(callee->hasScript());
 
         if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
-                                           holder, receiverGuard, callee)) {
+                                           &holder->as<NativeObject>(), receiverGuard, callee)) {
             *attached = true;
             return true;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/ScriptedSetter %s:%" PRIuSIZE ") stub",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
 
         ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8187,17 +8198,17 @@ TryAttachSetAccessorPropStub(JSContext *
 
     // Try handling JSNative setters.
     if (cacheableCall && !isScripted) {
         RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
         MOZ_ASSERT(obj != holder);
         MOZ_ASSERT(callee->isNative());
 
         if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
-                                           holder, receiverGuard, callee)) {
+                                           &holder->as<NativeObject>(), receiverGuard, callee)) {
             *attached = true;
             return true;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/NativeSetter %p) stub",
                     callee->native());
 
         ICSetProp_CallNative::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8270,17 +8281,17 @@ TryAttachTypedObjectSetPropStub(JSContex
         return true;
 
     Rooted<TypeDescr *> fieldDescr(cx, &structDescr->fieldDescr(fieldIndex));
     if (!fieldDescr->is<SimpleTypeDescr>())
         return true;
 
     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
 
-    ICSetProp_TypedObject::Compiler compiler(cx, obj->lastProperty(), obj->group(), fieldOffset,
+    ICSetProp_TypedObject::Compiler compiler(cx, obj->maybeShape(), obj->group(), fieldOffset,
                                              &fieldDescr->as<SimpleTypeDescr>());
     ICUpdatedStub *newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
     if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
         return false;
 
     stub->addNewStub(newStub);
@@ -8319,17 +8330,17 @@ DoSetPropFallback(JSContext *cx, Baselin
     else
         name = script->getName(pc);
     RootedId id(cx, NameToId(name));
 
     RootedObject obj(cx, ToObjectFromStack(cx, lhs));
     if (!obj)
         return false;
     ReceiverGuard::Token oldGuard = ReceiverGuard::objectToken(obj);
-    RootedShape oldShape(cx, obj->lastProperty());
+    RootedShape oldShape(cx, obj->maybeShape());
     RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
     if (!oldGroup)
         return false;
     uint32_t oldSlots = obj->isNative() ? obj->as<NativeObject>().numDynamicSlots() : 0;
 
     bool attached = false;
     // There are some reasons we can fail to attach a stub that are temporary.
     // We want to avoid calling noteUnoptimizableAccess() if the reason we
@@ -11978,17 +11989,17 @@ ICSetProp_Native::ICSetProp_Native(JitCo
 
 ICSetProp_Native *
 ICSetProp_Native::Compiler::getStub(ICStubSpace *space)
 {
     RootedObjectGroup group(cx, obj_->getGroup(cx));
     if (!group)
         return nullptr;
 
-    RootedShape shape(cx, obj_->lastProperty());
+    RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
     ICSetProp_Native *stub = ICStub::New<ICSetProp_Native>(space, getStubCode(), group, shape, offset_);
     if (!stub || !stub->initUpdatingChain(cx, space))
         return nullptr;
     return stub;
 }
 
 ICSetProp_NativeAdd::ICSetProp_NativeAdd(JitCode *stubCode, ObjectGroup *group,
                                          size_t protoChainDepth,
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -2916,26 +2916,26 @@ class ICGetElemNativeCompiler : public I
         acctype_(acctype),
         needsAtomize_(needsAtomize),
         offset_(0),
         getter_(getter),
         pcOffset_(pcOffset)
     {}
 
     ICStub *getStub(ICStubSpace *space) {
-        RootedShape shape(cx, obj_->lastProperty());
+        RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
         if (kind == ICStub::GetElem_NativeSlot) {
             MOZ_ASSERT(obj_ == holder_);
             return ICStub::New<ICGetElem_NativeSlot>(
                     space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_,
                     offset_);
         }
 
         MOZ_ASSERT(obj_ != holder_);
-        RootedShape holderShape(cx, holder_->lastProperty());
+        RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
         if (kind == ICStub::GetElem_NativePrototypeSlot) {
             return ICStub::New<ICGetElem_NativePrototypeSlot>(
                     space, getStubCode(), firstMonitorStub_, shape, name_, acctype_, needsAtomize_,
                     offset_, holder_, holderShape);
         }
 
         if (kind == ICStub::GetElem_NativePrototypeCallNative) {
             return ICStub::New<ICGetElem_NativePrototypeCallNative>(
@@ -3807,17 +3807,17 @@ class ICGetProp_Primitive : public ICMon
             firstMonitorStub_(firstMonitorStub),
             primitiveType_(primitiveType),
             prototype_(cx, prototype),
             isFixedSlot_(isFixedSlot),
             offset_(offset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
-            RootedShape protoShape(cx, prototype_->lastProperty());
+            RootedShape protoShape(cx, prototype_->as<NativeObject>().lastProperty());
             return ICStub::New<ICGetProp_Primitive>(space, getStubCode(), firstMonitorStub_,
                                                     protoShape, offset_);
         }
     };
 };
 
 // Stub for accessing a string's length.
 class ICGetProp_StringLength : public ICStub
@@ -3924,17 +3924,17 @@ class ReceiverGuard
         if (!(token & 1))
             return reinterpret_cast<ObjectGroup *>(token);
         return nullptr;
     }
 
     static Token objectToken(JSObject *obj) {
         if (obj->is<UnboxedPlainObject>())
             return groupToken(obj->group());
-        return shapeToken(obj->lastProperty());
+        return shapeToken(obj->maybeShape());
     }
 
     explicit ReceiverGuard(Token token)
       : shape_(tokenShape(token)), group_(tokenGroup(token))
     {
         MOZ_ASSERT(shape_ || group_);
     }
 
@@ -4416,17 +4416,17 @@ class ICGetProp_CallScripted : public IC
                  HandleObject holder, HandleFunction getter, uint32_t pcOffset)
           : ICGetPropCallPrototypeGetter::Compiler(cx, ICStub::GetProp_CallScripted,
                                                    firstMonitorStub, obj, holder,
                                                    getter, pcOffset, /* outerClass = */ nullptr)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
-            Shape *holderShape = holder_->lastProperty();
+            Shape *holderShape = holder_->as<NativeObject>().lastProperty();
             return ICStub::New<ICGetProp_CallScripted>(space, getStubCode(), firstMonitorStub_,
                                                        guard, holder_, holderShape, getter_,
                                                        pcOffset_);
         }
     };
 };
 
 // Stub for calling an own native getter on a native object.
@@ -4462,17 +4462,17 @@ class ICGetProp_CallNative : public ICGe
                  HandleFunction getter, uint32_t pcOffset, const Class *outerClass,
                  bool inputDefinitelyObject = false)
           : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallNative, firstMonitorStub,
                                           obj, getter, pcOffset, outerClass, true),
             inputDefinitelyObject_(inputDefinitelyObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
-            RootedShape shape(cx, holder_->lastProperty());
+            RootedShape shape(cx, holder_->as<NativeObject>().lastProperty());
             return ICStub::New<ICGetProp_CallNative>(space, getStubCode(), firstMonitorStub_,
                                                      holder_, shape, getter_, pcOffset_);
         }
     };
 };
 
 // Stub for calling an native getter on a native object when the getter is kept on the proto-chain.
 class ICGetProp_CallNativePrototype : public ICGetPropCallPrototypeGetter
@@ -4509,17 +4509,17 @@ class ICGetProp_CallNativePrototype : pu
           : ICGetPropCallPrototypeGetter::Compiler(cx, ICStub::GetProp_CallNativePrototype,
                                                    firstMonitorStub, obj, holder,
                                                    getter, pcOffset, outerClass),
             inputDefinitelyObject_(inputDefinitelyObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
-            Shape *holderShape = holder_->lastProperty();
+            Shape *holderShape = holder_->as<NativeObject>().lastProperty();
             return ICStub::New<ICGetProp_CallNativePrototype>(space, getStubCode(), firstMonitorStub_,
                                                               guard, holder_, holderShape,
                                                               getter_, pcOffset_);
         }
     };
 };
 
 class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter
@@ -4959,17 +4959,17 @@ class ICSetPropNativeAddCompiler : publi
             return nullptr;
 
         // Only specify newGroup when the object's group changes due to the
         // object becoming fully initialized per the acquired properties
         // analysis.
         if (newGroup == oldGroup_)
             newGroup = nullptr;
 
-        RootedShape newShape(cx, obj_->lastProperty());
+        RootedShape newShape(cx, obj_->as<NativeObject>().lastProperty());
 
         return ICStub::New<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
                     space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
     }
 
     ICUpdatedStub *getStub(ICStubSpace *space);
 };
 
@@ -5225,17 +5225,17 @@ class ICSetProp_CallScripted : public IC
         Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
                  uint32_t pcOffset)
           : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallScripted,
                                           obj, holder, setter, pcOffset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
-            Shape *holderShape = holder_->lastProperty();
+            Shape *holderShape = holder_->as<NativeObject>().lastProperty();
             return ICStub::New<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
                                                        holderShape, setter_, pcOffset_);
         }
     };
 };
 
 // Stub for calling a native setter on a native object.
 class ICSetProp_CallNative : public ICSetPropCallSetter
@@ -5261,17 +5261,17 @@ class ICSetProp_CallNative : public ICSe
         Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
                  uint32_t pcOffset)
           : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallNative,
                                           obj, holder, setter, pcOffset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
-            Shape *holderShape = holder_->lastProperty();
+            Shape *holderShape = holder_->as<NativeObject>().lastProperty();
             return ICStub::New<ICSetProp_CallNative>(space, getStubCode(), guard, holder_,
                                                      holderShape, setter_, pcOffset_);
         }
     };
 };
 
 // Call
 //      JSOP_CALL
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4594,17 +4594,17 @@ CodeGenerator::visitNewSingletonCallObje
     Register objReg = ToRegister(lir->output());
 
     JSObject *templateObj = lir->mir()->templateObject();
 
     JSScript *script = lir->mir()->block()->info().script();
     uint32_t lexicalBegin = script->bindings.aliasedBodyLevelLexicalBegin();
     OutOfLineCode *ool;
     ool = oolCallVM(NewSingletonCallObjectInfo, lir,
-                    (ArgList(), ImmGCPtr(templateObj->lastProperty()),
+                    (ArgList(), ImmGCPtr(templateObj->as<CallObject>().lastProperty()),
                                 Imm32(lexicalBegin)),
                     StoreRegisterTo(objReg));
 
     // Objects can only be given singleton types in VM calls.  We make the call
     // out of line to not bloat inline code, even if (naively) this seems like
     // extra work.
     masm.jump(ool->entry());
     masm.bind(ool->rejoin());
@@ -8649,27 +8649,28 @@ CodeGenerator::visitUnboxObjectOrNull(LU
 void
 CodeGenerator::visitLoadTypedArrayElement(LLoadTypedArrayElement *lir)
 {
     Register elements = ToRegister(lir->elements());
     Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
     AnyRegister out = ToAnyRegister(lir->output());
 
     Scalar::Type arrayType = lir->mir()->arrayType();
+    Scalar::Type readType  = lir->mir()->readType();
     int width = Scalar::byteSize(arrayType);
 
     Label fail;
     if (lir->index()->isConstant()) {
         Address source(elements, ToInt32(lir->index()) * width + lir->mir()->offsetAdjustment());
-        masm.loadFromTypedArray(arrayType, source, out, temp, &fail,
+        masm.loadFromTypedArray(readType, source, out, temp, &fail,
                                 lir->mir()->canonicalizeDoubles());
     } else {
         BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
                          lir->mir()->offsetAdjustment());
-        masm.loadFromTypedArray(arrayType, source, out, temp, &fail,
+        masm.loadFromTypedArray(readType, source, out, temp, &fail,
                                 lir->mir()->canonicalizeDoubles());
     }
 
     if (fail.used())
         bailoutFrom(&fail, lir->snapshot());
 }
 
 void
@@ -8710,44 +8711,47 @@ CodeGenerator::visitLoadTypedArrayElemen
     if (fail.used())
         bailoutFrom(&fail, lir->snapshot());
 
     masm.bind(&done);
 }
 
 template <typename T>
 static inline void
-StoreToTypedArray(MacroAssembler &masm, Scalar::Type arrayType, const LAllocation *value, const T &dest)
-{
-    if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64) {
-        masm.storeToTypedFloatArray(arrayType, ToFloatRegister(value), dest);
+StoreToTypedArray(MacroAssembler &masm, Scalar::Type writeType, const LAllocation *value, const T &dest)
+{
+    if (Scalar::isSimdType(writeType) ||
+        writeType == Scalar::Float32 ||
+        writeType == Scalar::Float64)
+    {
+        masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest);
     } else {
         if (value->isConstant())
-            masm.storeToTypedIntArray(arrayType, Imm32(ToInt32(value)), dest);
+            masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
         else
-            masm.storeToTypedIntArray(arrayType, ToRegister(value), dest);
+            masm.storeToTypedIntArray(writeType, ToRegister(value), dest);
     }
 }
 
 void
 CodeGenerator::visitStoreTypedArrayElement(LStoreTypedArrayElement *lir)
 {
     Register elements = ToRegister(lir->elements());
     const LAllocation *value = lir->value();
 
-    Scalar::Type arrayType = lir->mir()->arrayType();
-    int width = Scalar::byteSize(arrayType);
+    Scalar::Type writeType = lir->mir()->writeType();
+    int width = Scalar::byteSize(lir->mir()->arrayType());
 
     if (lir->index()->isConstant()) {
         Address dest(elements, ToInt32(lir->index()) * width + lir->mir()->offsetAdjustment());
-        StoreToTypedArray(masm, arrayType, value, dest);
+        StoreToTypedArray(masm, writeType, value, dest);
     } else {
         BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
                        lir->mir()->offsetAdjustment());
-        StoreToTypedArray(masm, arrayType, value, dest);
+        StoreToTypedArray(masm, writeType, value, dest);
     }
 }
 
 void
 CodeGenerator::visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir)
 {
     Register elements = ToRegister(lir->elements());
     const LAllocation *value = lir->value();
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1772,17 +1772,17 @@ OffThreadCompilationAvailable(JSContext 
         && CanUseExtraThreads();
 }
 
 static void
 TrackAllProperties(JSContext *cx, JSObject *obj)
 {
     MOZ_ASSERT(obj->isSingleton());
 
-    for (Shape::Range<NoGC> range(obj->lastProperty()); !range.empty(); range.popFront())
+    for (Shape::Range<NoGC> range(obj->as<NativeObject>().lastProperty()); !range.empty(); range.popFront())
         EnsureTrackPropertyTypes(cx, obj, range.front().propid());
 }
 
 static void
 TrackPropertiesForSingletonScopes(JSContext *cx, JSScript *script, BaselineFrame *baselineFrame)
 {
     // Ensure that all properties of singleton call objects which the script
     // could access are tracked. These are generally accessed through
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3503,16 +3503,18 @@ IonBuilder::improveTypesAtTypeOfCompare(
         filter.addType(TypeSet::SymbolType(), alloc_->lifoAlloc());
     } else if (constant->toString() == TypeName(JSTYPE_OBJECT, names)) {
         filter.addType(TypeSet::NullType(), alloc_->lifoAlloc());
         if (trueBranch)
             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
     } else if (constant->toString() == TypeName(JSTYPE_FUNCTION, names)) {
         if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
+    } else {
+        return true;
     }
 
     TemporaryTypeSet *type;
     if (trueBranch)
         type = TypeSet::intersectSets(&filter, inputTypes, alloc_->lifoAlloc());
     else
         type = TypeSet::removeSet(inputTypes, &filter, alloc_->lifoAlloc());
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -834,16 +834,21 @@ class IonBuilder
     InliningStatus inlineSimdSplat(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type);
     InliningStatus inlineSimdSwizzle(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type);
     InliningStatus inlineSimdCheck(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type);
     InliningStatus inlineSimdConvert(CallInfo &callInfo, JSNative native, bool isCast,
                                      SimdTypeDescr::Type from, SimdTypeDescr::Type to);
     InliningStatus inlineSimdSelect(CallInfo &callInfo, JSNative native, bool isElementWise,
                                     SimdTypeDescr::Type type);
 
+    bool prepareForSimdLoadStore(CallInfo &callInfo, Scalar::Type simdType, MInstruction **elements,
+                                 MDefinition **index, Scalar::Type *arrayType);
+    InliningStatus inlineSimdLoad(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type);
+    InliningStatus inlineSimdStore(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type);
+
     // Utility intrinsics.
     InliningStatus inlineIsCallable(CallInfo &callInfo);
     InliningStatus inlineIsObject(CallInfo &callInfo);
     InliningStatus inlineToObject(CallInfo &callInfo);
     InliningStatus inlineToInteger(CallInfo &callInfo);
     InliningStatus inlineToString(CallInfo &callInfo);
     InliningStatus inlineDump(CallInfo &callInfo);
     InliningStatus inlineHasClass(CallInfo &callInfo, const Class *clasp,
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -770,17 +770,17 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
         MOZ_ASSERT(!expandoVal.toObject().as<NativeObject>().contains(cx, name));
 
         // Reference object has an expando object that doesn't define the name. Check that
         // the incoming object has an expando object with the same shape.
         masm.branchTestObject(Assembler::NotEqual, tempVal, &failDOMProxyCheck);
         masm.extractObject(tempVal, tempVal.scratchReg());
         masm.branchPtr(Assembler::Equal,
                        Address(tempVal.scratchReg(), JSObject::offsetOfShape()),
-                       ImmGCPtr(expandoVal.toObject().lastProperty()),
+                       ImmGCPtr(expandoVal.toObject().as<NativeObject>().lastProperty()),
                        &domProxyOk);
     }
 
     // Failure case: restore the tempVal registers and jump to failures.
     masm.bind(&failDOMProxyCheck);
     masm.popValue(tempVal);
     masm.jump(stubFailure);
 
@@ -805,17 +805,17 @@ GenerateReadSlot(JSContext *cx, IonScrip
     Label failures_;
     if (multipleFailureJumps && !failures)
         failures = &failures_;
 
     // Guard on the shape or type of the object, depending on whether it is native.
     if (obj->isNative()) {
         attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                        Address(object, JSObject::offsetOfShape()),
-                                       ImmGCPtr(obj->lastProperty()),
+                                       ImmGCPtr(obj->as<NativeObject>().lastProperty()),
                                        failures);
     } else {
         attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                        Address(object, JSObject::offsetOfGroup()),
                                        ImmGCPtr(obj->group()),
                                        failures);
     }
 
@@ -869,17 +869,17 @@ GenerateReadSlot(JSContext *cx, IonScrip
             Register lastReg = object;
             MOZ_ASSERT(scratchReg != object);
             while (proto) {
                 masm.loadObjProto(lastReg, scratchReg);
 
                 // Guard the shape of the current prototype.
                 masm.branchPtr(Assembler::NotEqual,
                                Address(scratchReg, JSObject::offsetOfShape()),
-                               ImmGCPtr(proto->lastProperty()),
+                               ImmGCPtr(proto->as<NativeObject>().lastProperty()),
                                &prototypeFailures);
 
                 proto = proto->getProto();
                 lastReg = scratchReg;
             }
 
             holderReg = InvalidReg;
         }
@@ -1093,35 +1093,38 @@ EmitGetterCall(JSContext *cx, MacroAssem
 
         masm.freeStack(masm.framePushed() - framePushedBefore);
     }
 
     masm.icRestoreLive(liveRegs, aic);
     return true;
 }
 
+static void
+TestMatchingReceiver(MacroAssembler &masm, Register object, JSObject *obj, Label *failure)
+{
+    if (Shape *shape = obj->maybeShape())
+        masm.branchTestObjShape(Assembler::NotEqual, object, shape, failure);
+    else
+        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
+}
+
 static bool
 GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
                    IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
                    JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object,
                    TypedOrValueRegister output, void *returnAddr, Label *failures = nullptr)
 {
     MOZ_ASSERT(output.hasValue());
 
     // Use the passed in label if there was one. Otherwise, we'll have to make our own.
     Label stubFailure;
     failures = failures ? failures : &stubFailure;
 
-    // Initial shape/group check.
-    if (obj->isNative())
-        masm.branchTestObjShape(Assembler::NotEqual, object, obj->lastProperty(), failures);
-    else if (obj->is<UnboxedPlainObject>())
-        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failures);
-    else
-        MOZ_CRASH("Unexpected object");
+    TestMatchingReceiver(masm, object, obj, failures);
 
     Register scratchReg = output.valueReg().scratchReg();
     bool spillObjReg = scratchReg == object;
     Label pop1AndFail;
     Label *maybePopAndFail = failures;
 
     // Save off the object register if it aliases the scratchReg
     if (spillObjReg) {
@@ -1133,17 +1136,17 @@ GenerateCallGetter(JSContext *cx, IonScr
     if (obj != holder)
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures);
 
     // Guard on the holder's shape.
     Register holderReg = scratchReg;
     masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
     masm.branchPtr(Assembler::NotEqual,
                    Address(holderReg, JSObject::offsetOfShape()),
-                   ImmGCPtr(holder->lastProperty()),
+                   ImmGCPtr(holder->as<NativeObject>().lastProperty()),
                    maybePopAndFail);
 
     if (spillObjReg)
         masm.pop(object);
 
     // Now we're good to go to invoke the native call.
     if (!EmitGetterCall(cx, masm, attacher, obj, holder, shape, liveRegs, object,
                         output, returnAddr))
@@ -1167,17 +1170,17 @@ static bool
 GenerateArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                     JSObject *obj, Register object, TypedOrValueRegister output)
 {
     MOZ_ASSERT(obj->is<ArrayObject>());
 
     Label failures;
 
     // Guard object is a dense array.
-    RootedShape shape(cx, obj->lastProperty());
+    RootedShape shape(cx, obj->as<ArrayObject>().lastProperty());
     if (!shape)
         return false;
     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
 
     // Load length.
     Register outReg;
     if (output.hasValue()) {
         outReg = output.valueReg().scratchReg();
@@ -1551,17 +1554,17 @@ GetPropertyIC::tryAttachDOMProxyShadowed
 
     Label failures;
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object(), JSObject::offsetOfShape()),
-                                   ImmGCPtr(obj->lastProperty()),
+                                   ImmGCPtr(obj->maybeShape()),
                                    &failures);
 
     // No need for more guards: we know this is a DOM proxy, since the shape
     // guard enforces a given JSClass, so just go ahead and emit the call to
     // ProxyGet.
 
     if (!EmitCallProxyGet(cx, masm, attacher, name(), liveRegs_, object(), output(),
                           pc(), returnAddr))
@@ -1620,17 +1623,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
 
     Label failures;
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object(), JSObject::offsetOfShape()),
-                                   ImmGCPtr(obj->lastProperty()),
+                                   ImmGCPtr(obj->maybeShape()),
                                    &failures);
 
     // Guard that our expando object hasn't started shadowing this property.
     CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, name, object(), &failures);
 
     if (holder) {
         // Found the property on the prototype chain. Treat it like a native
         // getprop.
@@ -2234,17 +2237,17 @@ SetPropertyIC::attachDOMProxyShadowed(JS
 
     Label failures;
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
-                   ImmGCPtr(obj->lastProperty()), &failures);
+                   ImmGCPtr(obj->maybeShape()), &failures);
 
     // No need for more guards: we know this is a DOM proxy, since the shape
     // guard enforces a given JSClass, so just go ahead and emit the call to
     // ProxySet.
 
     RootedId propId(cx, AtomToId(name()));
     if (!EmitCallProxySet(cx, masm, attacher, propId, liveRegs_, object(),
                           value(), returnAddr, strict()))
@@ -2284,17 +2287,17 @@ GenerateCallSetter(JSContext *cx, IonScr
 
         // Generate prototype/shape guards.
         if (obj != holder)
             GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &protoFailure);
 
         masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg);
         masm.branchPtr(Assembler::NotEqual,
                        Address(scratchReg, JSObject::offsetOfShape()),
-                       ImmGCPtr(holder->lastProperty()),
+                       ImmGCPtr(holder->as<NativeObject>().lastProperty()),
                        &protoFailure);
 
         masm.jump(&protoSuccess);
 
         masm.bind(&protoFailure);
         masm.pop(scratchReg);
         masm.jump(failure);
 
@@ -2508,17 +2511,17 @@ SetPropertyIC::attachDOMProxyUnshadowed(
 
     Label failures;
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the object.
     masm.branchPtr(Assembler::NotEqual,
                    Address(object(), JSObject::offsetOfShape()),
-                   ImmGCPtr(obj->lastProperty()), &failures);
+                   ImmGCPtr(obj->maybeShape()), &failures);
 
     // Guard that our expando object hasn't started shadowing this property.
     CheckDOMProxyExpandoDoesNotShadow(cx, masm, obj, name(), object(), &failures);
 
     RootedPropertyName propName(cx, name());
     RootedObject holder(cx);
     RootedShape shape(cx);
     bool isSetter;
@@ -2559,22 +2562,17 @@ bool
 SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                 HandleObject obj, HandleObject holder, HandleShape shape,
                                 void *returnAddr)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Label failure;
-    if (obj->isNative())
-        masm.branchTestObjShape(Assembler::NotEqual, object(), obj->lastProperty(), &failure);
-    else if (obj->is<UnboxedPlainObject>())
-        masm.branchTestObjGroup(Assembler::NotEqual, object(), obj->group(), &failure);
-    else
-        MOZ_CRASH("Unexpected object");
+    TestMatchingReceiver(masm, object(), obj, &failure);
 
     if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
                             object(), value(), &failure, liveRegs_, returnAddr))
     {
         return false;
     }
 
     // Rejoin jump.
@@ -2612,17 +2610,17 @@ GenerateAddSlot(JSContext *cx, MacroAsse
     if (checkTypeset) {
         CheckTypeSetForWrite(masm, obj, obj->lastProperty()->propid(), object, value, &failuresPopObject);
         masm.loadPtr(Address(StackPointer, 0), object);
     }
 
     JSObject *proto = obj->getProto();
     Register protoReg = object;
     while (proto) {
-        Shape *protoShape = proto->lastProperty();
+        Shape *protoShape = proto->as<NativeObject>().lastProperty();
 
         // load next prototype
         masm.loadObjProto(protoReg, protoReg);
 
         // Ensure that its shape matches.
         masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &failuresPopObject);
 
         proto = proto->getProto();
@@ -3021,17 +3019,17 @@ SetPropertyIC::update(JSContext *cx, Han
             {
                 return false;
             }
             addedSetterStub = true;
         }
     }
 
     uint32_t oldSlots = obj->is<NativeObject>() ? obj->as<NativeObject>().numDynamicSlots() : 0;
-    RootedShape oldShape(cx, obj->lastProperty());
+    RootedShape oldShape(cx, obj->maybeShape());
 
     // Set/Add the property on the object, the inlined cache are setup for the next execution.
     if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
         return false;
 
     // The property did not exist before, now we can try to inline the property add.
     bool checkTypeset;
     if (!addedSetterStub && canCache == MaybeCanAttachAddSlot &&
@@ -3190,17 +3188,17 @@ GenerateDenseElement(JSContext *cx, Macr
                      JSObject *obj, const Value &idval, Register object,
                      ConstantOrRegister index, TypedOrValueRegister output)
 {
     MOZ_ASSERT(GetElementIC::canAttachDenseElement(obj, idval));
 
     Label failures;
 
     // Guard object's shape.
-    RootedShape shape(cx, obj->lastProperty());
+    RootedShape shape(cx, obj->as<NativeObject>().lastProperty());
     if (!shape)
         return false;
     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
 
     // Ensure the index is an int32 value.
     Register indexReg = InvalidReg;
 
     if (index.reg().hasValue()) {
@@ -3298,48 +3296,47 @@ GetElementIC::canAttachDenseElementHole(
 }
 
 static bool
 GenerateDenseElementHole(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
                          IonScript *ion, JSObject *obj, const Value &idval,
                          Register object, ConstantOrRegister index, TypedOrValueRegister output)
 {
     MOZ_ASSERT(GetElementIC::canAttachDenseElementHole(obj, idval, output));
-    MOZ_ASSERT(obj->lastProperty());
 
     Register scratchReg = output.valueReg().scratchReg();
 
     // Guard on the shape and group, to prevent non-dense elements from appearing.
     Label failures;
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object, JSObject::offsetOfShape()),
-                                   ImmGCPtr(obj->lastProperty()), &failures);
+                                   ImmGCPtr(obj->as<NativeObject>().lastProperty()), &failures);
 
 
     if (obj->hasUncacheableProto()) {
         masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg);
         Address proto(scratchReg, ObjectGroup::offsetOfProto());
         masm.branchPtr(Assembler::NotEqual, proto,
                        ImmMaybeNurseryPtr(obj->getProto()), &failures);
     }
 
     JSObject *pobj = obj->getProto();
     while (pobj) {
-        MOZ_ASSERT(pobj->lastProperty());
+        MOZ_ASSERT(pobj->as<NativeObject>().lastProperty());
 
         masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg);
         if (pobj->hasUncacheableProto()) {
             MOZ_ASSERT(!pobj->isSingleton());
             Address groupAddr(scratchReg, JSObject::offsetOfGroup());
             masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), &failures);
         }
 
         // Make sure the shape matches, to avoid non-dense elements.
         masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()),
-                       ImmGCPtr(pobj->lastProperty()), &failures);
+                       ImmGCPtr(pobj->as<NativeObject>().lastProperty()), &failures);
 
         // Load elements vector.
         masm.loadPtr(Address(scratchReg, NativeObject::offsetOfElements()), scratchReg);
 
         // Also make sure there are no dense elements.
         Label hole;
         Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
         masm.branch32(Assembler::NotEqual, initLength, Imm32(0), &failures);
@@ -3867,17 +3864,17 @@ GenerateSetDenseElement(JSContext *cx, M
     MOZ_ASSERT(idval.isInt32());
 
     Label failures;
     Label outOfBounds; // index represents a known hole, or an illegal append
 
     Label markElem, storeElement; // used if TI protects us from worrying about holes.
 
     // Guard object is a dense array.
-    Shape *shape = obj->lastProperty();
+    Shape *shape = obj->as<NativeObject>().lastProperty();
     if (!shape)
         return false;
     masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
 
     // Ensure the index is an int32 value.
     masm.branchTestInt32(Assembler::NotEqual, indexVal, &failures);
 
     // Unbox the index.
@@ -4143,17 +4140,18 @@ GenerateScopeChainGuard(MacroAssembler &
         // If this is the last object on the scope walk, and the property we've
         // found is not configurable, then we don't need a shape guard because
         // the shape cannot be removed.
         if (shape && !shape->configurable())
             return;
     }
 
     Address shapeAddr(scopeObjReg, JSObject::offsetOfShape());
-    masm.branchPtr(Assembler::NotEqual, shapeAddr, ImmGCPtr(scopeObj->lastProperty()), failures);
+    masm.branchPtr(Assembler::NotEqual, shapeAddr,
+                   ImmGCPtr(scopeObj->as<NativeObject>().lastProperty()), failures);
 }
 
 static void
 GenerateScopeChainGuards(MacroAssembler &masm, JSObject *scopeChain, JSObject *holder,
                          Register outputReg, Label *failures, bool skipLastGuard = false)
 {
     JSObject *tobj = scopeChain;
 
@@ -4184,17 +4182,17 @@ BindNameIC::attachNonGlobal(JSContext *c
 
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     // Guard on the shape of the scope chain.
     Label failures;
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(scopeChainReg(), JSObject::offsetOfShape()),
-                                   ImmGCPtr(scopeChain->lastProperty()),
+                                   ImmGCPtr(scopeChain->as<NativeObject>().lastProperty()),
                                    holder != scopeChain ? &failures : nullptr);
 
     if (holder != scopeChain) {
         JSObject *parent = &scopeChain->as<ScopeObject>().enclosingScope();
         masm.extractObject(Address(scopeChainReg(), ScopeObject::offsetOfEnclosingScope()), outputReg());
 
         GenerateScopeChainGuards(masm, parent, holder, outputReg(), &failures);
     } else {
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -548,16 +548,42 @@ IsMagicType(MIRType type)
 // SIMD kind. It is the Y part of the name "Foo x Y".
 static inline unsigned
 SimdTypeToLength(MIRType type)
 {
     MOZ_ASSERT(IsSimdType(type));
     return 1 << ((type >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK);
 }
 
+static inline MIRType
+ScalarTypeToMIRType(Scalar::Type type)
+{
+    switch (type) {
+      case Scalar::Int8:
+      case Scalar::Uint8:
+      case Scalar::Int16:
+      case Scalar::Uint16:
+      case Scalar::Int32:
+      case Scalar::Uint32:
+      case Scalar::Uint8Clamped:
+        return MIRType_Int32;
+      case Scalar::Float32:
+        return MIRType_Float32;
+      case Scalar::Float64:
+        return MIRType_Double;
+      case Scalar::Float32x4:
+        return MIRType_Float32x4;
+      case Scalar::Int32x4:
+        return MIRType_Int32x4;
+      case Scalar::MaxTypedArrayViewType:
+        break;
+    }
+    MOZ_CRASH("unexpected SIMD kind");
+}
+
 static inline unsigned
 ScalarTypeToLength(Scalar::Type type)
 {
     switch (type) {
       case Scalar::Int8:
       case Scalar::Uint8:
       case Scalar::Int16:
       case Scalar::Uint16:
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2883,17 +2883,18 @@ void
 LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
 {
     MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
 
-    MOZ_ASSERT(IsNumberType(ins->type()) || ins->type() == MIRType_Boolean);
+    MOZ_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type()) ||
+               ins->type() == MIRType_Boolean);
 
     // We need a temp register for Uint32Array with known double result.
     LDefinition tempDef = LDefinition::BogusTemp();
     if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type()))
         tempDef = temp();
 
     if (ins->requiresMemoryBarrier()) {
         LMemoryBarrier *fence = new(alloc()) LMemoryBarrier(MembarBeforeLoad);
@@ -2975,29 +2976,32 @@ LIRGenerator::visitLoadTypedArrayElement
 }
 
 void
 LIRGenerator::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
 {
     MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
 
-    if (ins->isFloatArray()) {
-        MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType_Float32);
-        MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType_Double);
+    if (ins->isSimdWrite()) {
+        MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32x4, ins->value()->type() == MIRType_Float32x4);
+        MOZ_ASSERT_IF(ins->writeType() == Scalar::Int32x4, ins->value()->type() == MIRType_Int32x4);
+    } else if (ins->isFloatWrite()) {
+        MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32, ins->value()->type() == MIRType_Float32);
+        MOZ_ASSERT_IF(ins->writeType() == Scalar::Float64, ins->value()->type() == MIRType_Double);
     } else {
         MOZ_ASSERT(ins->value()->type() == MIRType_Int32);
     }
 
     LUse elements = useRegister(ins->elements());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
 
     // For byte arrays, the value has to be in a byte register on x86.
-    if (ins->isByteArray())
+    if (ins->isByteWrite())
         value = useByteOpRegisterOrNonDoubleConstant(ins->value());
     else
         value = useRegisterOrNonDoubleConstant(ins->value());
 
     // Optimization opportunity for atomics: on some platforms there
     // is a store instruction that incorporates the necessary
     // barriers, and we could use that instead of separate barrier and
     // store instructions.  See bug #1077027.
@@ -3014,30 +3018,30 @@ LIRGenerator::visitStoreTypedArrayElemen
 
 void
 LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
 {
     MOZ_ASSERT(ins->elements()->type() == MIRType_Elements);
     MOZ_ASSERT(ins->index()->type() == MIRType_Int32);
     MOZ_ASSERT(ins->length()->type() == MIRType_Int32);
 
-    if (ins->isFloatArray()) {
+    if (ins->isFloatWrite()) {
         MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float32, ins->value()->type() == MIRType_Float32);
         MOZ_ASSERT_IF(ins->arrayType() == Scalar::Float64, ins->value()->type() == MIRType_Double);
     } else {
         MOZ_ASSERT(ins->value()->type() == MIRType_Int32);
     }
 
     LUse elements = useRegister(ins->elements());
     LAllocation length = useAnyOrConstant(ins->length());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
 
     // For byte arrays, the value has to be in a byte register on x86.
-    if (ins->isByteArray())
+    if (ins->isByteWrite())
         value = useByteOpRegisterOrNonDoubleConstant(ins->value());
     else
         value = useRegisterOrNonDoubleConstant(ins->value());
     add(new(alloc()) LStoreTypedArrayElementHole(elements, length, index, value), ins);
 }
 
 void
 LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins)
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -349,16 +349,27 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == js::simd_float32x4_select)
         return inlineSimdSelect(callInfo, native, IsElementWise(true), SimdTypeDescr::TYPE_FLOAT32);
     if (native == js::simd_float32x4_bitselect)
         return inlineSimdSelect(callInfo, native, IsElementWise(false), SimdTypeDescr::TYPE_FLOAT32);
 
     if (native == js::simd_int32x4_swizzle)
         return inlineSimdSwizzle(callInfo, native, SimdTypeDescr::TYPE_INT32);
 
+    if (native == js::simd_int32x4_load)
+        return inlineSimdLoad(callInfo, native, SimdTypeDescr::TYPE_INT32);
+    if (native == js::simd_float32x4_load)
+        return inlineSimdLoad(callInfo, native, SimdTypeDescr::TYPE_FLOAT32);
+
+
+    if (native == js::simd_int32x4_store)
+        return inlineSimdStore(callInfo, native, SimdTypeDescr::TYPE_INT32);
+    if (native == js::simd_float32x4_store)
+        return inlineSimdStore(callInfo, native, SimdTypeDescr::TYPE_FLOAT32);
+
     return InliningStatus_NotInlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target)
 {
     MOZ_ASSERT(target->isNative());
     JSNative native = target->native();
@@ -3113,10 +3124,113 @@ IonBuilder::inlineSimdSwizzle(CallInfo &
     for (size_t i = 0; i < 4; i++)
         lanes[i] = callInfo.getArg(1 + i);
 
     MIRType mirType = SimdTypeDescrToMIRType(type);
     MSimdGeneralSwizzle *ins = MSimdGeneralSwizzle::New(alloc(), callInfo.getArg(0), lanes, mirType);
     return boxSimd(callInfo, ins, templateObj);
 }
 
+static Scalar::Type
+SimdTypeToScalarType(SimdTypeDescr::Type type)
+{
+    switch (type) {
+      case SimdTypeDescr::TYPE_FLOAT32: return Scalar::Float32x4;
+      case SimdTypeDescr::TYPE_INT32:   return Scalar::Int32x4;
+      case SimdTypeDescr::TYPE_FLOAT64: break;
+    }
+    MOZ_CRASH("unexpected simd type");
+}
+
+bool
+IonBuilder::prepareForSimdLoadStore(CallInfo &callInfo, Scalar::Type simdType, MInstruction **elements,
+                                    MDefinition **index, Scalar::Type *arrayType)
+{
+    MDefinition *array = callInfo.getArg(0);
+    *index = callInfo.getArg(1);
+
+    if (!ElementAccessIsAnyTypedArray(constraints(), array, *index, arrayType))
+        return false;
+
+    MInstruction *indexAsInt32 = MToInt32::New(alloc(), *index);
+    current->add(indexAsInt32);
+    *index = indexAsInt32;
+
+    MDefinition *indexForBoundsCheck = *index;
+
+    // Artificially make sure the index is in bounds by adding the difference
+    // number of slots needed (e.g. reading from Float32Array we need to make
+    // sure to be in bounds for 4 slots, so add 3, etc.).
+    MOZ_ASSERT(Scalar::byteSize(simdType) % Scalar::byteSize(*arrayType) == 0);
+    int32_t suppSlotsNeeded = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType) - 1;
+    if (suppSlotsNeeded) {
+        MConstant *suppSlots = constant(Int32Value(suppSlotsNeeded));
+        MAdd *addedIndex = MAdd::New(alloc(), *index, suppSlots);
+        // We're fine even with the add overflows, as long as the generated code
+        // for the bounds check uses an unsigned comparison.
+        addedIndex->setInt32();
+        current->add(addedIndex);
+        indexForBoundsCheck = addedIndex;
+    }
+
+    MInstruction *length;
+    addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
+
+    MInstruction *check = MBoundsCheck::New(alloc(), indexForBoundsCheck, length);
+    current->add(check);
+    return true;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineSimdLoad(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type)
+{
+    InlineTypedObject *templateObj = nullptr;
+    if (!checkInlineSimd(callInfo, native, type, 2, &templateObj))
+        return InliningStatus_NotInlined;
+
+    Scalar::Type simdType = SimdTypeToScalarType(type);
+
+    MDefinition *index = nullptr;
+    MInstruction *elements = nullptr;
+    Scalar::Type arrayType;
+    if (!prepareForSimdLoadStore(callInfo, simdType, &elements, &index, &arrayType))
+        return InliningStatus_NotInlined;
+
+    MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, index, arrayType);
+    load->setResultType(SimdTypeDescrToMIRType(type));
+    load->setReadType(simdType);
+
+    return boxSimd(callInfo, load, templateObj);
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineSimdStore(CallInfo &callInfo, JSNative native, SimdTypeDescr::Type type)
+{
+    InlineTypedObject *templateObj = nullptr;
+    if (!checkInlineSimd(callInfo, native, type, 3, &templateObj))
+        return InliningStatus_NotInlined;
+
+    Scalar::Type simdType = SimdTypeToScalarType(type);
+
+    MDefinition *index = nullptr;
+    MInstruction *elements = nullptr;
+    Scalar::Type arrayType;
+    if (!prepareForSimdLoadStore(callInfo, simdType, &elements, &index, &arrayType))
+        return InliningStatus_NotInlined;
+
+    MDefinition *valueToWrite = callInfo.getArg(2);
+    MStoreTypedArrayElement *store = MStoreTypedArrayElement::New(alloc(), elements, index,
+                                                                  valueToWrite, arrayType);
+    store->setWriteType(simdType);
+
+    current->add(store);
+    current->push(valueToWrite);
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    if (!resumeAfter(store))
+        return InliningStatus_Error;
+
+    return InliningStatus_Inlined;
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8828,25 +8828,27 @@ enum MemoryBarrierRequirement
 
 // Also see comments above MMemoryBarrier, below.
 
 class MLoadTypedArrayElement
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     Scalar::Type arrayType_;
+    Scalar::Type readType_;
     bool requiresBarrier_;
     int32_t offsetAdjustment_;
     bool canonicalizeDoubles_;
 
     MLoadTypedArrayElement(MDefinition *elements, MDefinition *index,
                            Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier,
                            int32_t offsetAdjustment, bool canonicalizeDoubles)
       : MBinaryInstruction(elements, index),
         arrayType_(arrayType),
+        readType_(arrayType),
         requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
         offsetAdjustment_(offsetAdjustment),
         canonicalizeDoubles_(canonicalizeDoubles)
     {
         setResultType(MIRType_Value);
         if (requiresBarrier_)
             setGuard();         // Not removable or movable
         else
@@ -8865,16 +8867,23 @@ class MLoadTypedArrayElement
                                        int32_t offsetAdjustment = 0,
                                        bool canonicalizeDoubles = true)
     {
         return new(alloc) MLoadTypedArrayElement(elements, index, arrayType,
                                                  requiresBarrier, offsetAdjustment,
                                                  canonicalizeDoubles);
     }
 
+    void setReadType(Scalar::Type type) {
+        readType_ = type;
+    }
+    Scalar::Type readType() const {
+        return readType_;
+    }
+
     Scalar::Type arrayType() const {
         return arrayType_;
     }
     bool fallible() const {
         // Bailout if the result does not fit in an int32.
         return arrayType_ == Scalar::Uint32 && type() == MIRType_Int32;
     }
     bool requiresMemoryBarrier() const {
@@ -8903,16 +8912,18 @@ class MLoadTypedArrayElement
     bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
         if (requiresBarrier_)
             return false;
         if (!ins->isLoadTypedArrayElement())
             return false;
         const MLoadTypedArrayElement *other = ins->toLoadTypedArrayElement();
         if (arrayType_ != other->arrayType_)
             return false;
+        if (readType_ != other->readType_)
+            return false;
         if (offsetAdjustment() != other->offsetAdjustment())
             return false;
         if (canonicalizeDoubles() != other->canonicalizeDoubles())
             return false;
         return congruentIfOperandsEqual(other);
     }
 
     void printOpcode(FILE *fp) const MOZ_OVERRIDE;
@@ -9047,31 +9058,73 @@ class MLoadTypedArrayElementStatic
     }
 
     void computeRange(TempAllocator &alloc) MOZ_OVERRIDE;
     bool needTruncation(TruncateKind kind) MOZ_OVERRIDE;
     bool canProduceFloat32() const MOZ_OVERRIDE { return accessType() == Scalar::Float32; }
     void collectRangeInfoPreTrunc() MOZ_OVERRIDE;
 };
 
+// Base class for MIR ops that write to typed arrays.
+class StoreTypedArrayBase
+{
+    Scalar::Type writeType_;
+
+  protected:
+    explicit StoreTypedArrayBase(Scalar::Type writeType)
+      : writeType_(writeType)
+    {
+        MOZ_ASSERT(isIntegerWrite() || isFloatWrite() || isSimdWrite());
+    }
+
+  public:
+    void setWriteType(Scalar::Type type) {
+        writeType_ = type;
+    }
+    Scalar::Type writeType() const {
+        return writeType_;
+    }
+    bool isByteWrite() const {
+        return writeType_ == Scalar::Int8 ||
+               writeType_ == Scalar::Uint8 ||
+               writeType_ == Scalar::Uint8Clamped;
+    }
+    bool isIntegerWrite() const {
+        return isByteWrite () ||
+               writeType_ == Scalar::Int16 ||
+               writeType_ == Scalar::Uint16 ||
+               writeType_ == Scalar::Int32 ||
+               writeType_ == Scalar::Uint32;
+    }
+    bool isFloatWrite() const {
+        return writeType_ == Scalar::Float32 ||
+               writeType_ == Scalar::Float64;
+    }
+    bool isSimdWrite() const {
+        return Scalar::isSimdType(writeType());
+    }
+};
+
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
+    public StoreTypedArrayBase,
     public StoreTypedArrayPolicy::Data
 {
     Scalar::Type arrayType_;
     bool requiresBarrier_;
     int32_t offsetAdjustment_;
 
     // See note in MStoreElementCommon.
     bool racy_;
 
     MStoreTypedArrayElement(MDefinition *elements, MDefinition *index, MDefinition *value,
                             Scalar::Type arrayType, MemoryBarrierRequirement requiresBarrier,
                             int32_t offsetAdjustment)
       : MTernaryInstruction(elements, index, value),
+        StoreTypedArrayBase(arrayType),
         arrayType_(arrayType),
         requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
         offsetAdjustment_(offsetAdjustment),
         racy_(false)
     {
         if (requiresBarrier_)
             setGuard();         // Not removable or movable
         else
@@ -9091,25 +9144,16 @@ class MStoreTypedArrayElement
     {
         return new(alloc) MStoreTypedArrayElement(elements, index, value, arrayType,
                                                   requiresBarrier, offsetAdjustment);
     }
 
     Scalar::Type arrayType() const {
         return arrayType_;
     }
-    bool isByteArray() const {
-        return arrayType_ == Scalar::Int8 ||
-               arrayType_ == Scalar::Uint8 ||
-               arrayType_ == Scalar::Uint8Clamped;
-    }
-    bool isFloatArray() const {
-        return arrayType_ == Scalar::Float32 ||
-               arrayType_ == Scalar::Float64;
-    }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     MDefinition *value() const {
         return getOperand(2);
@@ -9135,23 +9179,23 @@ class MStoreTypedArrayElement
         return use == getUseFor(2) && arrayType_ == Scalar::Float32;
     }
 
     ALLOW_CLONE(MStoreTypedArrayElement)
 };
 
 class MStoreTypedArrayElementHole
   : public MAryInstruction<4>,
+    public StoreTypedArrayBase,
     public StoreTypedArrayHolePolicy::Data
 {
-    Scalar::Type arrayType_;
-
     MStoreTypedArrayElementHole(MDefinition *elements, MDefinition *length, MDefinition *index,
                                 MDefinition *value, Scalar::Type arrayType)
-      : MAryInstruction<4>(), arrayType_(arrayType)
+      : MAryInstruction<4>(),
+        StoreTypedArrayBase(arrayType)
     {
         initOperand(0, elements);
         initOperand(1, length);
         initOperand(2, index);
         initOperand(3, value);
         setMovable();
         MOZ_ASSERT(elements->type() == MIRType_Elements);
         MOZ_ASSERT(length->type() == MIRType_Int32);
@@ -9165,26 +9209,19 @@ class MStoreTypedArrayElementHole
     static MStoreTypedArrayElementHole *New(TempAllocator &alloc, MDefinition *elements,
                                             MDefinition *length, MDefinition *index,
                                             MDefinition *value, Scalar::Type arrayType)
     {
         return new(alloc) MStoreTypedArrayElementHole(elements, length, index, value, arrayType);
     }
 
     Scalar::Type arrayType() const {
-        return arrayType_;
-    }
-    bool isByteArray() const {
-        return arrayType_ == Scalar::Int8 ||
-               arrayType_ == Scalar::Uint8 ||
-               arrayType_ == Scalar::Uint8Clamped;
-    }
-    bool isFloatArray() const {
-        return arrayType_ == Scalar::Float32 ||
-               arrayType_ == Scalar::Float64;
+        MOZ_ASSERT(!Scalar::isSimdType(writeType()),
+                   "arrayType == writeType iff the write type isn't SIMD");
+        return writeType();
     }
     MDefinition *elements() const {
         return getOperand(0);
     }
     MDefinition *length() const {
         return getOperand(1);
     }
     MDefinition *index() const {
@@ -9194,34 +9231,38 @@ class MStoreTypedArrayElementHole
         return getOperand(3);
     }
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     TruncateKind operandTruncateKind(size_t index) const MOZ_OVERRIDE;
 
     bool canConsumeFloat32(MUse *use) const MOZ_OVERRIDE {
-        return use == getUseFor(3) && arrayType_ == Scalar::Float32;
+        return use == getUseFor(3) && arrayType() == Scalar::Float32;
     }
 
     ALLOW_CLONE(MStoreTypedArrayElementHole)
 };
 
 // Store a value infallibly to a statically known typed array.
 class MStoreTypedArrayElementStatic :
-    public MBinaryInstruction
-  , public StoreTypedArrayElementStaticPolicy::Data
+    public MBinaryInstruction,
+    public StoreTypedArrayBase,
+    public StoreTypedArrayElementStaticPolicy::Data
 {
     MStoreTypedArrayElementStatic(JSObject *someTypedArray, MDefinition *ptr, MDefinition *v,
                                   int32_t offset, bool needsBoundsCheck)
-        : MBinaryInstruction(ptr, v), someTypedArray_(someTypedArray),
+        : MBinaryInstruction(ptr, v),
+          StoreTypedArrayBase(AnyTypedArrayType(someTypedArray)),
+          someTypedArray_(someTypedArray),
           offset_(offset), needsBoundsCheck_(needsBoundsCheck)
     {}
 
     AlwaysTenured<JSObject*> someTypedArray_;
+
     // An offset to be encoded in the store instruction - taking advantage of the
     // addressing modes. This is only non-zero when the access is proven to be
     // within bounds.
     int32_t offset_;
     bool needsBoundsCheck_;
 
   public:
     INSTRUCTION_HEADER(StoreTypedArrayElementStatic)
@@ -9231,21 +9272,17 @@ class MStoreTypedArrayElementStatic :
                                               int32_t offset = 0,
                                               bool needsBoundsCheck = true)
     {
         return new(alloc) MStoreTypedArrayElementStatic(someTypedArray, ptr, v,
                                                         offset, needsBoundsCheck);
     }
 
     Scalar::Type accessType() const {
-        return AnyTypedArrayType(someTypedArray_);
-    }
-    bool isFloatArray() const {
-        return accessType() == Scalar::Float32 ||
-               accessType() == Scalar::Float64;
+        return writeType();
     }
 
     void *base() const;
     size_t length() const;
 
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
     bool needsBoundsCheck() const { return needsBoundsCheck_; }
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -289,16 +289,22 @@ StoreToTypedFloatArray(MacroAssembler &m
         break;
       case Scalar::Float64:
 #ifdef JS_MORE_DETERMINISTIC
         // See the comment in TypedArrayObjectTemplate::doubleToNative.
         masm.canonicalizeDouble(value);
 #endif
         masm.storeDouble(value, dest);
         break;
+      case Scalar::Float32x4:
+        masm.storeUnalignedFloat32x4(value, dest);
+        break;
+      case Scalar::Int32x4:
+        masm.storeUnalignedInt32x4(value, dest);
+        break;
       default:
         MOZ_CRASH("Invalid typed array type");
     }
 }
 
 void
 MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value,
                                        const BaseIndex &dest)
@@ -351,16 +357,22 @@ MacroAssembler::loadFromTypedArray(Scala
         loadFloat32(src, dest.fpu());
         canonicalizeFloat(dest.fpu());
         break;
       case Scalar::Float64:
         loadDouble(src, dest.fpu());
         if (canonicalizeDoubles)
             canonicalizeDouble(dest.fpu());
         break;
+      case Scalar::Int32x4:
+        loadUnalignedInt32x4(src, dest.fpu());
+        break;
+      case Scalar::Float32x4:
+        loadUnalignedFloat32x4(src, dest.fpu());
+        break;
       default:
         MOZ_CRASH("Invalid typed array type");
     }
 }
 
 template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const Address &src, AnyRegister dest,
                                                  Register temp, Label *fail, bool canonicalizeDoubles);
 template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex &src, AnyRegister dest,
@@ -1202,19 +1214,21 @@ MacroAssembler::initGCSlots(Register obj
 }
 
 void
 MacroAssembler::initGCThing(Register obj, Register slots, JSObject *templateObj,
                             bool initFixedSlots)
 {
     // Fast initialization of an empty object returned by allocateObject().
 
-    storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape()));
     storePtr(ImmGCPtr(templateObj->group()), Address(obj, JSObject::offsetOfGroup()));
 
+    if (Shape *shape = templateObj->maybeShape())
+        storePtr(ImmGCPtr(shape), Address(obj, JSObject::offsetOfShape()));
+
     if (templateObj->isNative()) {
         NativeObject *ntemplate = &templateObj->as<NativeObject>();
         MOZ_ASSERT_IF(!ntemplate->denseElementsAreCopyOnWrite(), !ntemplate->hasDynamicElements());
 
         if (ntemplate->hasDynamicSlots())
             storePtr(slots, Address(obj, NativeObject::offsetOfSlots()));
         else
             storePtr(ImmPtr(nullptr), Address(obj, NativeObject::offsetOfSlots()));
@@ -1263,16 +1277,18 @@ MacroAssembler::initGCThing(Register obj
             storePtr(ImmWord(value),
                      Address(obj, InlineTypedObject::offsetOfDataStart() + offset));
             nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
             offset += sizeof(uintptr_t);
         }
     } else if (templateObj->is<UnboxedPlainObject>()) {
         const UnboxedLayout &layout = templateObj->as<UnboxedPlainObject>().layout();
 
+        storePtr(ImmWord(0), Address(obj, JSObject::offsetOfShape()));
+
         // Initialize reference fields of the object, per UnboxedPlainObject::create.
         if (const int32_t *list = layout.traceList()) {
             while (*list != -1) {
                 storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
                          Address(obj, UnboxedPlainObject::offsetOfData() + *list));
                 list++;
             }
             list++;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1643,17 +1643,18 @@ MToInt32::computeRange(TempAllocator &al
 
 void
 MLimitedTruncate::computeRange(TempAllocator &alloc)
 {
     Range *output = new(alloc) Range(input());
     setRange(output);
 }
 
-static Range *GetTypedArrayRange(TempAllocator &alloc, int type)
+static Range *
+GetTypedArrayRange(TempAllocator &alloc, Scalar::Type type)
 {
     switch (type) {
       case Scalar::Uint8Clamped:
       case Scalar::Uint8:
         return Range::NewUInt32Range(alloc, 0, UINT8_MAX);
       case Scalar::Uint16:
         return Range::NewUInt32Range(alloc, 0, UINT16_MAX);
       case Scalar::Uint32:
@@ -1663,28 +1664,30 @@ static Range *GetTypedArrayRange(TempAll
         return Range::NewInt32Range(alloc, INT8_MIN, INT8_MAX);
       case Scalar::Int16:
         return Range::NewInt32Range(alloc, INT16_MIN, INT16_MAX);
       case Scalar::Int32:
         return Range::NewInt32Range(alloc, INT32_MIN, INT32_MAX);
 
       case Scalar::Float32:
       case Scalar::Float64:
+      case Scalar::Float32x4:
+      case Scalar::Int32x4:
+      case Scalar::MaxTypedArrayViewType:
         break;
     }
-
-  return nullptr;
+    return nullptr;
 }
 
 void
 MLoadTypedArrayElement::computeRange(TempAllocator &alloc)
 {
     // We have an Int32 type and if this is a UInt32 load it may produce a value
     // outside of our range, but we have a bailout to handle those cases.
-    setRange(GetTypedArrayRange(alloc, arrayType()));
+    setRange(GetTypedArrayRange(alloc, readType()));
 }
 
 void
 MLoadTypedArrayElementStatic::computeRange(TempAllocator &alloc)
 {
     // We don't currently use MLoadTypedArrayElementStatic for uint32, so we
     // don't have to worry about it returning a value outside our type.
     MOZ_ASSERT(AnyTypedArrayType(someTypedArray_) != Scalar::Uint32);
@@ -2637,31 +2640,31 @@ MToDouble::operandTruncateKind(size_t in
     // MToDouble propagates its truncate kind to its operand.
     return truncateKind();
 }
 
 MDefinition::TruncateKind
 MStoreTypedArrayElement::operandTruncateKind(size_t index) const
 {
     // An integer store truncates the stored value.
-    return index == 2 && !isFloatArray() ? Truncate : NoTruncate;
+    return index == 2 && isIntegerWrite() ? Truncate : NoTruncate;
 }
 
 MDefinition::TruncateKind
 MStoreTypedArrayElementHole::operandTruncateKind(size_t index) const
 {
     // An integer store truncates the stored value.
-    return index == 3 && !isFloatArray() ? Truncate : NoTruncate;
+    return index == 3 && isIntegerWrite() ? Truncate : NoTruncate;
 }
 
 MDefinition::TruncateKind
 MStoreTypedArrayElementStatic::operandTruncateKind(size_t index) const
 {
     // An integer store truncates the stored value.
-    return index == 1 && !isFloatArray() ? Truncate : NoTruncate;
+    return index == 1 && isIntegerWrite() ? Truncate : NoTruncate;
 }
 
 MDefinition::TruncateKind
 MDiv::operandTruncateKind(size_t index) const
 {
     return Min(truncateKind(), TruncateAfterBailouts);
 }
 
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -163,17 +163,17 @@ IsObjectEscaped(MInstruction *ins, JSObj
             }
 #endif
             break;
           }
 
           case MDefinition::Op_GuardShape: {
             MGuardShape *guard = def->toGuardShape();
             MOZ_ASSERT(!ins->isGuardShape());
-            if (obj->lastProperty() != guard->shape()) {
+            if (obj->as<NativeObject>().lastProperty() != guard->shape()) {
                 JitSpewDef(JitSpew_Escape, "Object ", ins);
                 JitSpewDef(JitSpew_Escape, "  has a non-matching guard shape\n", guard);
                 return true;
             }
             if (IsObjectEscaped(def->toInstruction(), obj))
                 return true;
             break;
           }
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -871,19 +871,23 @@ InstanceOfPolicy::adjustInputs(TempAlloc
     // Box first operand if it isn't object
     if (def->getOperand(0)->type() != MIRType_Object)
         BoxPolicy<0>::staticAdjustInputs(alloc, def);
 
     return true;
 }
 
 bool
-StoreTypedArrayPolicy::adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType,
-                                        MDefinition *value, int valueOperand)
+StoreTypedArrayPolicy::adjustValueInput(TempAllocator &alloc, MInstruction *ins,
+                                        Scalar::Type writeType, MDefinition *value, int valueOperand)
 {
+    // Storing a SIMD value just implies that we might need a SimdUnbox.
+    if (Scalar::isSimdType(writeType))
+        return MaybeSimdUnbox(alloc, ins, ScalarTypeToMIRType(writeType), valueOperand);
+
     MDefinition *curValue = value;
     // First, ensure the value is int32, boolean, double or Value.
     // The conversion is based on TypedArrayObjectTemplate::setElementTail.
     switch (value->type()) {
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
       case MIRType_Boolean:
@@ -914,17 +918,17 @@ StoreTypedArrayPolicy::adjustValueInput(
     }
 
     MOZ_ASSERT(value->type() == MIRType_Int32 ||
                value->type() == MIRType_Boolean ||
                value->type() == MIRType_Double ||
                value->type() == MIRType_Float32 ||
                value->type() == MIRType_Value);
 
-    switch (arrayType) {
+    switch (writeType) {
       case Scalar::Int8:
       case Scalar::Uint8:
       case Scalar::Int16:
       case Scalar::Uint16:
       case Scalar::Int32:
       case Scalar::Uint32:
         if (value->type() != MIRType_Int32) {
             value = MTruncateToInt32::New(alloc, value);
@@ -961,17 +965,17 @@ bool
 StoreTypedArrayPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     SingleObjectPolicy::staticAdjustInputs(alloc, ins);
 
     MStoreTypedArrayElement *store = ins->toStoreTypedArrayElement();
     MOZ_ASSERT(IsValidElementsType(store->elements(), store->offsetAdjustment()));
     MOZ_ASSERT(store->index()->type() == MIRType_Int32);
 
-    return adjustValueInput(alloc, ins, store->arrayType(), store->value(), 2);
+    return adjustValueInput(alloc, store, store->writeType(), store->value(), 2);
 }
 
 bool
 StoreTypedArrayHolePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MStoreTypedArrayElementHole *store = ins->toStoreTypedArrayElementHole();
     MOZ_ASSERT(store->elements()->type() == MIRType_Elements);
     MOZ_ASSERT(store->index()->type() == MIRType_Int32);
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -437,17 +437,18 @@ class InstanceOfPolicy MOZ_FINAL : publi
 };
 
 class StoreTypedArrayHolePolicy;
 class StoreTypedArrayElementStaticPolicy;
 
 class StoreTypedArrayPolicy : public TypePolicy
 {
   private:
-    static bool adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType, MDefinition *value, int valueOperand);
+    static bool adjustValueInput(TempAllocator &alloc, MInstruction *ins, Scalar::Type arrayType,
+                                 MDefinition *value, int valueOperand);
 
     friend class StoreTypedArrayHolePolicy;
     friend class StoreTypedArrayElementStaticPolicy;
 
   public:
     EMPTY_DATA_;
     virtual bool adjustInputs(TempAllocator &alloc, MInstruction *ins) MOZ_OVERRIDE;
 };
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1138,18 +1138,18 @@ AutoDetectInvalidation::setReturnOverrid
 void
 AssertValidObjectPtr(JSContext *cx, JSObject *obj)
 {
     // Check what we can, so that we'll hopefully assert/crash if we get a
     // bogus object (pointer).
     MOZ_ASSERT(obj->compartment() == cx->compartment());
     MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
 
-    MOZ_ASSERT_IF(!obj->hasLazyGroup(),
-                  obj->group()->clasp() == obj->lastProperty()->getObjectClass());
+    MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
+                  obj->group()->clasp() == obj->maybeShape()->getObjectClass());
 
     if (obj->isTenured()) {
         MOZ_ASSERT(obj->isAligned());
         gc::AllocKind kind = obj->asTenured().getAllocKind();
         MOZ_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
         MOZ_ASSERT(obj->asTenured().zone() == cx->zone());
     }
 }
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1393,22 +1393,26 @@ class MacroAssemblerARMCompat : public M
     void loadPtr(AbsoluteAddress address, Register dest);
     void loadPtr(AsmJSAbsoluteAddress address, Register dest);
 
     void loadPrivate(const Address &address, Register dest);
 
     void loadAlignedInt32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeAlignedInt32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
     void loadUnalignedInt32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
+    void loadUnalignedInt32x4(const BaseIndex &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeUnalignedInt32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
+    void storeUnalignedInt32x4(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
 
     void loadAlignedFloat32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeAlignedFloat32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
     void loadUnalignedFloat32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
+    void loadUnalignedFloat32x4(const BaseIndex &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeUnalignedFloat32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
+    void storeUnalignedFloat32x4(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
 
     void loadDouble(const Address &addr, FloatRegister dest);
     void loadDouble(const BaseIndex &src, FloatRegister dest);
 
     // Load a float value into a register, then expand it to a double.
     void loadFloatAsDouble(const Address &addr, FloatRegister dest);
     void loadFloatAsDouble(const BaseIndex &src, FloatRegister dest);
 
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -1254,22 +1254,26 @@ public:
     void loadPtr(AbsoluteAddress address, Register dest);
     void loadPtr(AsmJSAbsoluteAddress address, Register dest);
 
     void loadPrivate(const Address &address, Register dest);
 
     void loadAlignedInt32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeAlignedInt32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
     void loadUnalignedInt32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
+    void loadUnalignedInt32x4(const BaseIndex &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeUnalignedInt32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
+    void storeUnalignedInt32x4(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
 
     void loadAlignedFloat32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeAlignedFloat32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
     void loadUnalignedFloat32x4(const Address &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
+    void loadUnalignedFloat32x4(const BaseIndex &addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
     void storeUnalignedFloat32x4(FloatRegister src, Address addr) { MOZ_CRASH("NYI"); }
+    void storeUnalignedFloat32x4(FloatRegister src, BaseIndex addr) { MOZ_CRASH("NYI"); }
 
     void loadDouble(const Address &addr, FloatRegister dest);
     void loadDouble(const BaseIndex &src, FloatRegister dest);
 
     // Load a float value into a register, then expand it to a double.
     void loadFloatAsDouble(const Address &addr, FloatRegister dest);
     void loadFloatAsDouble(const BaseIndex &src, FloatRegister dest);
 
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -2165,16 +2165,20 @@ class AssemblerX86Shared : public Assemb
             break;
           case Operand::MEM_ADDRESS32:
             masm.vpshufd_imr(mask, src1.address(), dest.encoding());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
+    void vmovddup(FloatRegister src, FloatRegister dest) {
+        MOZ_ASSERT(HasSSE3());
+        masm.vmovddup_rr(src.encoding(), dest.encoding());
+    }
     void vmovhlps(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vmovhlps_rr(src1.encoding(), src0.encoding(), dest.encoding());
     }
     void vmovlhps(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         masm.vmovlhps_rr(src1.encoding(), src0.encoding(), dest.encoding());
     }
--- a/js/src/jit/shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/shared/BaseAssembler-x86-shared.h
@@ -2679,16 +2679,21 @@ public:
     {
         twoByteOpImmSimd("vshufps", VEX_PS, OP2_SHUFPS_VpsWpsIb, mask, offset, base, src0, dst);
     }
     void vshufps_imr(uint32_t mask, const void *address, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpImmSimd("vshufps", VEX_PS, OP2_SHUFPS_VpsWpsIb, mask, address, src0, dst);
     }
 
+    void vmovddup_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        twoByteOpSimd("vmovddup", VEX_SD, OP2_MOVDDUP_VqWq, src, invalid_xmm, dst);
+    }
+
     void vmovhlps_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vmovhlps", VEX_PS, OP2_MOVHLPS_VqUq, src1, src0, dst);
     }
 
     void vmovlhps_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vmovlhps", VEX_PS, OP2_MOVLHPS_VqUq, src1, src0, dst);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -2507,16 +2507,20 @@ CodeGeneratorX86Shared::visitSimdSwizzle
     // and use defineReuseInput to avoid this move (bug 1084404)
     if (ins->lanesMatch(2, 3, 2, 3)) {
         FloatRegister inputCopy = masm.reusedInputFloat32x4(input, output);
         masm.vmovhlps(input, inputCopy, output);
         return;
     }
 
     if (ins->lanesMatch(0, 1, 0, 1)) {
+        if (AssemblerX86Shared::HasSSE3() && !AssemblerX86Shared::HasAVX()) {
+            masm.vmovddup(input, output);
+            return;
+        }
         FloatRegister inputCopy = masm.reusedInputFloat32x4(input, output);
         masm.vmovlhps(input, inputCopy, output);
         return;
     }
 
     if (ins->lanesMatch(0, 0, 1, 1)) {
         FloatRegister inputCopy = masm.reusedInputFloat32x4(input, output);
         masm.vunpcklps(input, inputCopy, output);
--- a/js/src/jit/shared/Encoding-x86-shared.h
+++ b/js/src/jit/shared/Encoding-x86-shared.h
@@ -125,16 +125,17 @@ enum class ShiftID {
 };
 
 enum TwoByteOpcodeID {
     OP2_UD2             = 0x0B,
     OP2_MOVSD_VsdWsd    = 0x10,
     OP2_MOVPS_VpsWps    = 0x10,
     OP2_MOVSD_WsdVsd    = 0x11,
     OP2_MOVPS_WpsVps    = 0x11,
+    OP2_MOVDDUP_VqWq    = 0x12,
     OP2_MOVHLPS_VqUq    = 0x12,
     OP2_MOVSLDUP_VpsWps = 0x12,
     OP2_UNPCKLPS_VsdWsd = 0x14,
     OP2_UNPCKHPS_VsdWsd = 0x15,
     OP2_MOVLHPS_VqUq    = 0x16,
     OP2_MOVSHDUP_VpsWps = 0x16,
     OP2_MOVAPD_VsdWsd   = 0x28,
     OP2_MOVAPS_VsdWsd   = 0x28,
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -925,22 +925,28 @@ class MacroAssemblerX86Shared : public A
         if (HasAVX() && src.kind() == Operand::FPREG)
             return FloatRegister::FromCode(src.fpu());
         loadAlignedInt32x4(src, dest);
         return dest;
     }
     void loadUnalignedInt32x4(const Address &src, FloatRegister dest) {
         vmovdqu(Operand(src), dest);
     }
+    void loadUnalignedInt32x4(const BaseIndex &src, FloatRegister dest) {
+        vmovdqu(Operand(src), dest);
+    }
     void loadUnalignedInt32x4(const Operand &src, FloatRegister dest) {
         vmovdqu(src, dest);
     }
     void storeUnalignedInt32x4(FloatRegister src, const Address &dest) {
         vmovdqu(src, Operand(dest));
     }
+    void storeUnalignedInt32x4(FloatRegister src, const BaseIndex &dest) {
+        vmovdqu(src, Operand(dest));
+    }
     void storeUnalignedInt32x4(FloatRegister src, const Operand &dest) {
         vmovdqu(src, dest);
     }
     void packedEqualInt32x4(const Operand &src, FloatRegister dest) {
         vpcmpeqd(src, dest, dest);
     }
     void packedGreaterThanInt32x4(const Operand &src, FloatRegister dest) {
         vpcmpgtd(src, dest, dest);
@@ -1006,22 +1012,28 @@ class MacroAssemblerX86Shared : public A
         if (HasAVX() && src.kind() == Operand::FPREG)
             return FloatRegister::FromCode(src.fpu());
         loadAlignedFloat32x4(src, dest);
         return dest;
     }
     void loadUnalignedFloat32x4(const Address &src, FloatRegister dest) {
         vmovups(Operand(src), dest);
     }
+    void loadUnalignedFloat32x4(const BaseIndex &src, FloatRegister dest) {
+        vmovdqu(Operand(src), dest);
+    }
     void loadUnalignedFloat32x4(const Operand &src, FloatRegister dest) {
         vmovups(src, dest);
     }
     void storeUnalignedFloat32x4(FloatRegister src, const Address &dest) {
         vmovups(src, Operand(dest));
     }
+    void storeUnalignedFloat32x4(FloatRegister src, const BaseIndex &dest) {
+        vmovups(src, Operand(dest));
+    }
     void storeUnalignedFloat32x4(FloatRegister src, const Operand &dest) {
         vmovups(src, dest);
     }
     void packedAddFloat32(const Operand &src, FloatRegister dest) {
         vaddps(src, dest, dest);
     }
     void packedSubFloat32(const Operand &src, FloatRegister dest) {
         vsubps(src, dest, dest);
--- a/js/src/jsapi-tests/testGCAllocator.cpp
+++ b/js/src/jsapi-tests/testGCAllocator.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include <cstdlib>
+
+#include "gc/GCInternals.h"
 #include "gc/Memory.h"
 #include "jsapi-tests/tests.h"
 
 #if defined(XP_WIN)
 #include "jswin.h"
 #include <psapi.h>
 #elif defined(SOLARIS)
 // This test doesn't apply to Solaris.
@@ -33,34 +36,69 @@ BEGIN_TEST(testGCAllocator)
     const size_t PageSize = sysinfo.dwPageSize;
 #elif defined(SOLARIS)
     return true;
 #elif defined(XP_UNIX)
     const size_t PageSize = size_t(sysconf(_SC_PAGESIZE));
 #else
     return true;
 #endif
-    if (addressesGrowUp())
+
+    /* Finish any ongoing background free activity. */
+    js::gc::AutoFinishGC finishGC(rt);
+
+    bool growUp;
+    CHECK(addressesGrowUp(&growUp));
+
+    if (growUp)
         return testGCAllocatorUp(PageSize);
     return testGCAllocatorDown(PageSize);
 }
 
 static const size_t Chunk = 512 * 1024;
 static const size_t Alignment = 2 * Chunk;
 static const int MaxTempChunks = 4096;
 static const size_t StagingSize = 16 * Chunk;
 
 bool
-addressesGrowUp()
+addressesGrowUp(bool *resultOut)
 {
-    void *p1 = mapMemory(2 * Chunk);
-    void *p2 = mapMemory(2 * Chunk);
-    unmapPages(p1, 2 * Chunk);
-    unmapPages(p2, 2 * Chunk);
-    return p1 < p2;
+    /*
+     * Try to detect whether the OS allocates memory in increasing or decreasing
+     * address order by making several allocations and comparing the addresses.
+     */
+
+    static const unsigned ChunksToTest = 20;
+    static const int ThresholdCount = 15;
+
+    void *chunks[ChunksToTest];
+    for (unsigned i = 0; i < ChunksToTest; i++) {
+        chunks[i] = mapMemory(2 * Chunk);
+        CHECK(chunks[i]);
+    }
+
+    int upCount = 0;
+    int downCount = 0;
+
+    for (unsigned i = 0; i < ChunksToTest - 1; i++) {
+        if (chunks[i] < chunks[i + 1])
+            upCount++;
+        else
+            downCount++;
+    }
+
+    for (unsigned i = 0; i < ChunksToTest; i++)
+        unmapPages(chunks[i], 2 * Chunk);
+
+    /* Check results were mostly consistent. */
+    CHECK(abs(upCount - downCount) >= ThresholdCount);
+
+    *resultOut = upCount > downCount;
+
+    return true;
 }
 
 size_t
 offsetFromAligned(void *p)
 {
     return uintptr_t(p) % Alignment;
 }
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2749,17 +2749,17 @@ GetIndexedPropertiesInRange(JSContext *c
             for (uint32_t i = begin; i < len && i < end; i++) {
                 if (!indexes.append(i))
                     return false;
             }
         }
 
         // Append sparse elements.
         if (pobj->isIndexed()) {
-            Shape::Range<NoGC> r(pobj->lastProperty());
+            Shape::Range<NoGC> r(pobj->as<NativeObject>().lastProperty());
             for (; !r.empty(); r.popFront()) {
                 Shape &shape = r.front();
                 jsid id = shape.propid();
                 if (!JSID_IS_INT(id))
                     continue;
 
                 uint32_t i = uint32_t(JSID_TO_INT(id));
                 if (!(begin <= i && i < end))
@@ -3530,17 +3530,17 @@ js::NewDenseCopiedArray(JSContext *cx, u
 ArrayObject *
 js::NewDenseFullyAllocatedArrayWithTemplate(JSContext *cx, uint32_t length, JSObject *templateObject)
 {
     gc::AllocKind allocKind = GuessArrayGCKind(length);
     MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &ArrayObject::class_));
     allocKind = GetBackgroundAllocKind(allocKind);
 
     RootedObjectGroup group(cx, templateObject->group());
-    RootedShape shape(cx, templateObject->lastProperty());
+    RootedShape shape(cx, templateObject->as<ArrayObject>().lastProperty());
 
     gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
     Rooted<ArrayObject *> arr(cx, ArrayObject::createArray(cx, allocKind,
                                                            heap, shape, group, length));
     if (!arr)
         return nullptr;
 
     if (!EnsureNewArrayElements(cx, arr, length))
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -316,20 +316,20 @@ js::IsScopeObject(JSObject *obj)
 }
 
 JS_FRIEND_API(bool)
 js::IsCallObject(JSObject *obj)
 {
     return obj->is<CallObject>();
 }
 
-JS_FRIEND_API(JSObject *)
-js::GetObjectParentMaybeScope(JSObject *obj)
+JS_FRIEND_API(bool)
+js::CanAccessObjectShape(JSObject *obj)
 {
-    return obj->enclosingScope();
+    return obj->maybeShape() != nullptr;
 }
 
 JS_FRIEND_API(JSObject *)
 js::GetGlobalForObjectCrossCompartment(JSObject *obj)
 {
     return &obj->global();
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -558,18 +558,19 @@ class Shape {
 public:
     shadow::BaseShape *base;
     jsid              _1;
     uint32_t          slotInfo;
 
     static const uint32_t FIXED_SLOTS_SHIFT = 27;
 };
 
-// This layout is shared by all objects except for Typed Objects (which still
-// have a shape and group).
+// This layout is shared by all native objects. For non-native objects, the
+// group may always be accessed safely, and other members may be as well,
+// depending on the object's specific layout.
 struct Object {
     shadow::ObjectGroup *group;
     shadow::Shape       *shape;
     JS::Value           *slots;
     void                *_1;
 
     size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; }
     JS::Value *fixedSlots() const {
@@ -678,33 +679,34 @@ JS_FRIEND_API(bool)
 IsFunctionObject(JSObject *obj);
 
 JS_FRIEND_API(bool)
 IsScopeObject(JSObject *obj);
 
 JS_FRIEND_API(bool)
 IsCallObject(JSObject *obj);
 
+JS_FRIEND_API(bool)
+CanAccessObjectShape(JSObject *obj);
+
 inline JSObject *
 GetObjectParent(JSObject *obj)
 {
     MOZ_ASSERT(!IsScopeObject(obj));
+    MOZ_ASSERT(CanAccessObjectShape(obj));
     return reinterpret_cast<shadow::Object*>(obj)->shape->base->parent;
 }
 
 static MOZ_ALWAYS_INLINE JSCompartment *
 GetObjectCompartment(JSObject *obj)
 {
     return reinterpret_cast<shadow::Object*>(obj)->group->compartment;
 }
 
 JS_FRIEND_API(JSObject *)
-GetObjectParentMaybeScope(JSObject *obj);
-
-JS_FRIEND_API(JSObject *)
 GetGlobalForObjectCrossCompartment(JSObject *obj);
 
 JS_FRIEND_API(JSObject *)
 GetPrototypeNoProxy(JSObject *obj);
 
 // Sidestep the activeContext checking implicitly performed in
 // JS_SetPendingException.
 JS_FRIEND_API(void)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2627,17 +2627,17 @@ GCRuntime::updatePointersToRelocatedCell
         updateAllCellPointersSerial(&trc);
 
     // Mark roots to update them.
     {
         markRuntime(&trc, MarkRuntime);
 
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
-        Debugger::markAllCrossCompartmentEdges(&trc);
+        Debugger::markIncomingCrossCompartmentEdges(&trc);
 
         for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
             WeakMapBase::markAll(c, &trc);
             if (c->watchpointMap)
                 c->watchpointMap->markAll(&trc);
         }
 
         // Mark all gray roots, making sure we call the trace callback to get the
@@ -6399,16 +6399,19 @@ GCRuntime::shrinkBuffers()
 }
 
 void
 GCRuntime::onOutOfMallocMemory()
 {
     // Stop allocating new chunks.
     allocTask.cancel(GCParallelTask::CancelAndWait);
 
+    // Wait for background free of nursery huge slots to finish.
+    nursery.waitBackgroundFreeEnd();
+
     AutoLockGC lock(rt);
     onOutOfMallocMemory(lock);
 }
 
 void
 GCRuntime::onOutOfMallocMemory(const AutoLockGC &lock)
 {
     // Release any relocated arenas we may be holding on to, without releasing
@@ -6501,16 +6504,17 @@ GCRuntime::gcIfRequested(JSContext *cx /
 AutoFinishGC::AutoFinishGC(JSRuntime *rt)
 {
     if (JS::IsIncrementalGCInProgress(rt)) {
         JS::PrepareForIncrementalGC(rt);
         JS::FinishIncrementalGC(rt, JS::gcreason::API);
     }
 
     rt->gc.waitBackgroundSweepEnd();
+    rt->gc.nursery.waitBackgroundFreeEnd();
 }
 
 AutoPrepareForTracing::AutoPrepareForTracing(JSRuntime *rt, ZoneSelector selector)
   : finish(rt),
     session(rt),
     copy(rt, selector)
 {
 }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -990,16 +990,17 @@ class GCParallelTask
   protected:
     // A flag to signal a request for early completion of the off-thread task.
     mozilla::Atomic<bool> cancel_;
 
     virtual void run() = 0;
 
   public:
     GCParallelTask() : state(NotStarted), duration_(0) {}
+    virtual ~GCParallelTask();
 
     // Time spent in the most recent invocation of this task.
     int64_t duration() const { return duration_; }
 
     // The simple interface to a parallel task works exactly like pthreads.
     bool start();
     void join();
 
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -505,18 +505,18 @@ CheckIncrementalZoneState(ExclusiveConte
 template <AllowGC allowGC>
 inline JSObject *
 AllocateObject(ExclusiveContext *cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap,
                const js::Class *clasp)
 {
     size_t thingSize = Arena::thingSize(kind);
 
     MOZ_ASSERT(thingSize == Arena::thingSize(kind));
-    MOZ_ASSERT(thingSize >= sizeof(JSObject));
-    static_assert(sizeof(JSObject) >= CellSize,
+    MOZ_ASSERT(thingSize >= sizeof(JSObject_Slots0));
+    static_assert(sizeof(JSObject_Slots0) >= CellSize,
                   "All allocations must be at least the allocator-imposed minimum size.");
 
     if (!CheckAllocatorState<allowGC>(cx, kind))
         return nullptr;
 
     if (cx->isJSContext() &&
         ShouldNurseryAllocateObject(cx->asJSContext()->nursery(), heap))
     {
@@ -574,41 +574,43 @@ AllocateNonObject(ExclusiveContext *cx)
 /*
  * When allocating for initialization from a cached object copy, we will
  * potentially destroy the cache entry we want to copy if we allow GC. On the
  * other hand, since these allocations are extremely common, we don't want to
  * delay GC from these allocation sites. Instead we allow the GC, but still
  * fail the allocation, forcing the non-cached path.
  */
 template <AllowGC allowGC>
-inline JSObject *
+inline NativeObject *
 AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap, const js::Class *clasp)
 {
+    MOZ_ASSERT(clasp->isNative());
+
     if (ShouldNurseryAllocateObject(cx->nursery(), heap)) {
         size_t thingSize = Arena::thingSize(kind);
 
         MOZ_ASSERT(thingSize == Arena::thingSize(kind));
         if (!CheckAllocatorState<NoGC>(cx, kind))
             return nullptr;
 
         JSObject *obj = TryNewNurseryObject<NoGC>(cx, thingSize, 0, clasp);
         if (!obj && allowGC) {
             cx->minorGC(JS::gcreason::OUT_OF_NURSERY);
             return nullptr;
         }
-        return obj;
+        return reinterpret_cast<NativeObject *>(obj);
     }
 
     JSObject *obj = AllocateObject<NoGC>(cx, kind, 0, heap, clasp);
     if (!obj && allowGC) {
         cx->runtime()->gc.maybeGC(cx->zone());
         return nullptr;
     }
 
-    return obj;
+    return reinterpret_cast<NativeObject *>(obj);
 }
 
 inline bool
 IsInsideGGCNursery(const js::gc::Cell *cell)
 {
     if (!cell)
         return false;
     uintptr_t addr = uintptr_t(cell);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -595,17 +595,17 @@ VectorToKeyIterator(JSContext *cx, Handl
          * computed for the cache lookup earlier, as constructing iterobj could
          * have triggered a shape-regenerating GC.  Don't bother with regenerating
          * the shape key; if such a GC *does* occur, we can only get hits through
          * the one-slot lastNativeIterator cache.
          */
         JSObject *pobj = obj;
         size_t ind = 0;
         do {
-            ni->shapes_array[ind++] = pobj->lastProperty();
+            ni->shapes_array[ind++] = pobj->as<NativeObject>().lastProperty();
             pobj = pobj->getProto();
         } while (pobj);
         MOZ_ASSERT(ind == slength);
     }
 
     iterobj->setNativeIterator(ni);
     objp.set(iterobj);
 
@@ -706,22 +706,22 @@ js::GetIterator(JSContext *cx, HandleObj
          * will result in a miss.
          */
         PropertyIteratorObject *last = cx->runtime()->nativeIterCache.last;
         if (last) {
             NativeIterator *lastni = last->getNativeIterator();
             if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
                 obj->isNative() &&
                 obj->as<NativeObject>().hasEmptyElements() &&
-                obj->lastProperty() == lastni->shapes_array[0])
+                obj->as<NativeObject>().lastProperty() == lastni->shapes_array[0])
             {
                 JSObject *proto = obj->getProto();
                 if (proto->isNative() &&
                     proto->as<NativeObject>().hasEmptyElements() &&
-                    proto->lastProperty() == lastni->shapes_array[1] &&
+                    proto->as<NativeObject>().lastProperty() == lastni->shapes_array[1] &&
                     !proto->getProto())
                 {
                     objp.set(last);
                     UpdateNativeIterator(lastni, obj);
                     RegisterEnumerator(cx, last, lastni);
                     return true;
                 }
             }
@@ -742,17 +742,17 @@ js::GetIterator(JSContext *cx, HandleObj
                     pobj->hasUncacheableProto() ||
                     pobj->getOps()->enumerate ||
                     pobj->getClass()->enumerate ||
                     pobj->as<NativeObject>().containsPure(cx->names().iteratorIntrinsic))
                 {
                     shapes.clear();
                     goto miss;
                 }
-                Shape *shape = pobj->lastProperty();
+                Shape *shape = pobj->as<NativeObject>().lastProperty();
                 key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
                 if (!shapes.append(shape))
                     return false;
                 pobj = pobj->getProto();
             } while (pobj);
         }
 
         PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2208,17 +2208,17 @@ js::CloneObjectLiteral(JSContext *cx, Ha
         if (!group)
             return nullptr;
 
         RootedPlainObject res(cx, NewObjectWithGroup<PlainObject>(cx, group, parent, kind,
                                                                   MaybeSingletonObject));
         if (!res)
             return nullptr;
 
-        RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->lastProperty(),
+        RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->as<PlainObject>().lastProperty(),
                                                               TaggedProto(proto), parent, kind));
         if (!newShape || !res->setLastProperty(cx, newShape))
             return nullptr;
 
         return res;
     }
 
     RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
@@ -2289,17 +2289,17 @@ NativeObject::fillInAfterSwap(JSContext 
 }
 
 void
 JSObject::fixDictionaryShapeAfterSwap()
 {
     // Dictionary shapes can point back to their containing objects, so after
     // swapping the guts of those objects fix the pointers up.
     if (isNative() && as<NativeObject>().inDictionaryMode())
-        shape_->listp = &shape_;
+        as<NativeObject>().shape_->listp = &as<NativeObject>().shape_;
 }
 
 /* Use this method with extreme caution. It trades the guts of two objects. */
 bool
 JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
 {
     // Ensure swap doesn't cause a finalizer to not be run.
     MOZ_ASSERT(IsBackgroundFinalized(a->asTenured().getAllocKind()) ==
@@ -3655,17 +3655,17 @@ js::GetObjectSlotName(JSTracer *trc, cha
 {
     MOZ_ASSERT(trc->debugPrinter() == GetObjectSlotName);
 
     JSObject *obj = (JSObject *)trc->debugPrintArg();
     uint32_t slot = uint32_t(trc->debugPrintIndex());
 
     Shape *shape;
     if (obj->isNative()) {
-        shape = obj->lastProperty();
+        shape = obj->as<NativeObject>().lastProperty();
         while (shape && (!shape->hasSlot() || shape->slot() != slot))
             shape = shape->previous();
     } else {
         shape = nullptr;
     }
 
     if (!shape) {
         do {
@@ -3932,17 +3932,17 @@ JSObject::dump()
         fprintf(stderr, "= ");
         dumpValue(obj->as<NativeObject>().getSlot(i));
         fputc('\n', stderr);
     }
 
     if (obj->isNative()) {
         fprintf(stderr, "properties:\n");
         Vector<Shape *, 8, SystemAllocPolicy> props;
-        for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
+        for (Shape::Range<NoGC> r(obj->as<NativeObject>().lastProperty()); !r.empty(); r.popFront())
             props.append(&r.front());
         for (size_t i = props.length(); i-- != 0;)
             DumpProperty(&obj->as<NativeObject>(), *props[i]);
     }
     fputc('\n', stderr);
 }
 
 static void
@@ -4112,24 +4112,25 @@ JSObject::addSizeOfExcludingThis(mozilla
     }
 }
 
 void
 JSObject::markChildren(JSTracer *trc)
 {
     MarkObjectGroup(trc, &group_, "group");
 
-    MarkShape(trc, &shape_, "shape");
-
     const Class *clasp = group_->clasp();
     if (clasp->trace)
         clasp->trace(trc, this);
 
-    if (shape_->isNative()) {
+    if (clasp->isNative()) {
         NativeObject *nobj = &as<NativeObject>();
+
+        MarkShape(trc, &nobj->shape_, "shape");
+
         MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
 
         do {
             if (nobj->denseElementsAreCopyOnWrite()) {
                 HeapPtrNativeObject &owner = nobj->getElementsHeader()->ownerObject();
                 if (owner != nobj) {
                     MarkObject(trc, &owner, "objectElementsOwner");
                     break;
@@ -4138,8 +4139,21 @@ JSObject::markChildren(JSTracer *trc)
 
             gc::MarkArraySlots(trc,
                                nobj->getDenseInitializedLength(),
                                nobj->getDenseElementsAllowCopyOnWrite(),
                                "objectElements");
         } while (false);
     }
 }
+
+JSObject *
+JSObject::getParent() const
+{
+    if (Shape *shape = maybeShape())
+        return shape->getObjectParent();
+
+    // Avoid the parent-link checking in JSObject::global. Unboxed plain
+    // objects keep their compartment's global alive through their layout, and
+    // don't need a read barrier here.
+    MOZ_ASSERT(is<UnboxedPlainObject>());
+    return compartment()->unsafeUnbarrieredMaybeGlobal();
+}
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -87,52 +87,48 @@ class StrictArgumentsObject;
 bool PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded);
 bool SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded);
 
 }  /* namespace js */
 
 /*
  * A JavaScript object. The members common to all objects are as follows:
  *
- * - The |shape_| member stores the shape of the object, which includes the
- *   object's class and the layout of all its properties.
- *
  * - The |group_| member stores the group of the object, which contains its
- *   prototype object and the possible types of its properties.
+ *   prototype object, its class and the possible types of its properties.
  *
  * Subclasses of JSObject --- mainly NativeObject and JSFunction --- add more
- * members.
+ * members. Notable among these is the object's shape, which stores flags and
+ * some other state, and, for native objects, the layout of all its properties.
+ * The second word of a JSObject generally stores its shape; if the second word
+ * stores anything else, the value stored cannot be a valid Shape* pointer, so
+ * that shape guards can be performed on objects without regard to the specific
+ * layout in use.
  */
 class JSObject : public js::gc::Cell
 {
   protected:
     js::HeapPtrObjectGroup group_;
-    js::HeapPtrShape shape_;
 
   private:
     friend class js::Shape;
     friend class js::GCMarker;
     friend class js::NewObjectCache;
     friend class js::Nursery;
     friend class js::gc::RelocationOverlay;
     friend bool js::PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded);
     friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj,
                                           bool *succeeded);
 
     // Make a new group to use for a singleton object.
     static js::ObjectGroup *makeLazyGroup(JSContext *cx, js::HandleObject obj);
 
   public:
-    js::Shape * lastProperty() const {
-        MOZ_ASSERT(shape_);
-        return shape_;
-    }
-
     bool isNative() const {
-        return lastProperty()->isNative();
+        return getClass()->isNative();
     }
 
     const js::Class *getClass() const {
         return group_->clasp();
     }
     const JSClass *getJSClass() const {
         return Jsvalify(getClass());
     }
@@ -167,67 +163,70 @@ class JSObject : public js::gc::Cell
     bool hasLazyGroup() const {
         return group_->lazy();
     }
 
     JSCompartment *compartment() const {
         return group_->compartment();
     }
 
+    inline js::Shape *maybeShape() const;
+    inline js::Shape *ensureShape(js::ExclusiveContext *cx);
+
     /*
      * Make a non-array object with the specified initial state. This method
      * takes ownership of any extantSlots it is passed.
      */
     static inline JSObject *create(js::ExclusiveContext *cx,
                                    js::gc::AllocKind kind,
                                    js::gc::InitialHeap heap,
                                    js::HandleShape shape,
                                    js::HandleObjectGroup group);
 
+    // Set the shape of an object. This pointer is valid for native objects and
+    // some non-native objects. After creating an object, tobjects for which
+    // the shape pointer is invalid need to overwrite this pointer before a GC
+    // can occur.
+    inline void setInitialShapeMaybeNonNative(js::Shape *shape);
+    inline void setShapeMaybeNonNative(js::Shape *shape);
+
     // Set the initial slots and elements of an object. These pointers are only
     // valid for native objects, but during initialization are set for all
     // objects. For non-native objects, these must not be dynamically allocated
     // pointers which leak when the non-native object finishes initialization.
     inline void setInitialSlotsMaybeNonNative(js::HeapSlot *slots);
     inline void setInitialElementsMaybeNonNative(js::HeapSlot *elements);
 
     enum GenerateShape {
         GENERATE_NONE,
         GENERATE_SHAPE
     };
 
-    bool setFlags(js::ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flags,
+    bool setFlags(js::ExclusiveContext *cx, js::BaseShape::Flag flags,
                   GenerateShape generateShape = GENERATE_NONE);
+    inline bool hasAllFlags(js::BaseShape::Flag flags) const;
 
     /*
      * An object is a delegate if it is on another object's prototype or scope
      * chain, and therefore the delegate might be asked implicitly to get or
      * set a property on behalf of another object. Delegates may be accessed
      * directly too, as may any object, but only those objects linked after the
      * head of any prototype or scope chain are flagged as delegates. This
      * definition helps to optimize shape-based property cache invalidation
      * (see Purge{Scope,Proto}Chain in jsobj.cpp).
      */
-    bool isDelegate() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::DELEGATE);
-    }
-
+    inline bool isDelegate() const;
     bool setDelegate(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
     }
 
-    bool isBoundFunction() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION);
-    }
-
+    inline bool isBoundFunction() const;
     inline bool hasSpecialEquality() const;
 
-    bool watched() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED);
-    }
+    inline bool watched() const;
     bool setWatched(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::WATCHED, GENERATE_SHAPE);
     }
 
     /* See InterpreterFrame::varObj. */
     inline bool isQualifiedVarObj();
     bool setQualifiedVarObj(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::QUALIFIED_VAROBJ);
@@ -239,49 +238,35 @@ class JSObject : public js::gc::Cell
     }
 
     /*
      * Objects with an uncacheable proto can have their prototype mutated
      * without inducing a shape change on the object. Property cache entries
      * and JIT inline caches should not be filled for lookups across prototype
      * lookups on the object.
      */
-    bool hasUncacheableProto() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::UNCACHEABLE_PROTO);
-    }
+    inline bool hasUncacheableProto() const;
     bool setUncacheableProto(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
     }
 
     /*
      * Whether SETLELEM was used to access this object. See also the comment near
      * PropertyTree::MAX_HEIGHT.
      */
-    bool hadElementsAccess() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::HAD_ELEMENTS_ACCESS);
-    }
+    inline bool hadElementsAccess() const;
     bool setHadElementsAccess(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::HAD_ELEMENTS_ACCESS);
     }
 
     /*
      * Whether there may be indexed properties on this object, excluding any in
      * the object's elements.
      */
-    bool isIndexed() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED);
-    }
-
-    uint32_t propertyCount() const {
-        return lastProperty()->entryCount();
-    }
-
-    bool hasShapeTable() const {
-        return lastProperty()->hasTable();
-    }
+    inline bool isIndexed() const;
 
     /* GC support. */
 
     void markChildren(JSTracer *trc);
 
     void fixupAfterMovingGC();
 
     static js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
@@ -290,17 +275,17 @@ class JSObject : public js::gc::Cell
 
     MOZ_ALWAYS_INLINE JS::Zone *zone() const {
         return group_->zone();
     }
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZone() const {
         return JS::shadow::Zone::asShadowZone(zone());
     }
     MOZ_ALWAYS_INLINE JS::Zone *zoneFromAnyThread() const {
-        return shape_->zoneFromAnyThread();
+        return group_->zoneFromAnyThread();
     }
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
     static MOZ_ALWAYS_INLINE void readBarrier(JSObject *obj);
     static MOZ_ALWAYS_INLINE void writeBarrierPre(JSObject *obj);
     static MOZ_ALWAYS_INLINE void writeBarrierPost(JSObject *obj, void *cellp);
     static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(JSObject *obj, void *cellp);
@@ -372,48 +357,39 @@ class JSObject : public js::gc::Cell
     bool hasLazyPrototype() const {
         bool lazy = getTaggedProto().isLazy();
         MOZ_ASSERT_IF(lazy, uninlinedIsProxy());
         return lazy;
     }
 
     // True iff this object's [[Prototype]] is immutable.  Must not be called
     // on proxies with lazy [[Prototype]]!
-    bool nonLazyPrototypeIsImmutable() const {
-        MOZ_ASSERT(!hasLazyPrototype());
-        return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE);
-    }
+    inline bool nonLazyPrototypeIsImmutable() const;
 
     inline void setGroup(js::ObjectGroup *group);
 
     /*
      * Mark an object that has been iterated over and is a singleton. We need
      * to recover this information in the object's type information after it
      * is purged on GC.
      */
-    bool isIteratedSingleton() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::ITERATED_SINGLETON);
-    }
+    inline bool isIteratedSingleton() const;
     bool setIteratedSingleton(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::ITERATED_SINGLETON);
     }
 
     /*
      * Mark an object as requiring its default 'new' type to have unknown
      * properties.
      */
-    bool isNewGroupUnknown() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::NEW_GROUP_UNKNOWN);
-    }
+    inline bool isNewGroupUnknown() const;
     static bool setNewGroupUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj);
 
     // Mark an object as having its 'new' script information cleared.
-    bool wasNewScriptCleared() const {
-        return lastProperty()->hasObjectFlag(js::BaseShape::NEW_SCRIPT_CLEARED);
-    }
+    inline bool wasNewScriptCleared() const;
     bool setNewScriptCleared(js::ExclusiveContext *cx) {
         return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED);
     }
 
     /* Set a new prototype for an object with a singleton type. */
     bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle<js::TaggedProto> proto);
 
     /*
@@ -444,52 +420,43 @@ class JSObject : public js::gc::Cell
      * In compileAndGo code, scope chains can contain only internal scope
      * objects with a global object at the root as the scope of the outermost
      * non-function script. In non-compileAndGo code, the scope of the
      * outermost non-function script might not be a global object, and can have
      * a mix of other objects above it before the global object is reached.
      */
 
     /* Access the parent link of an object. */
-    JSObject *getParent() const {
-        return lastProperty()->getObjectParent();
-    }
+    JSObject *getParent() const;
     static bool setParent(JSContext *cx, js::HandleObject obj, js::HandleObject newParent);
 
     /*
      * Get the enclosing scope of an object. When called on non-scope object,
      * this will just be the global (the name "enclosing scope" still applies
      * in this situation because non-scope objects can be on the scope chain).
      */
     inline JSObject *enclosingScope();
 
     /* Access the metadata on an object. */
-    inline JSObject *getMetadata() const {
-        return lastProperty()->getObjectMetadata();
-    }
+    inline JSObject *getMetadata() const;
     static bool setMetadata(JSContext *cx, js::HandleObject obj, js::HandleObject newMetadata);
 
     inline js::GlobalObject &global() const;
     inline bool isOwnGlobal() const;
 
     /*
      * ES5 meta-object properties and operations.
      */
 
   public:
     // Indicates whether a non-proxy is extensible.  Don't call on proxies!
     // This method really shouldn't exist -- but there are a few internal
     // places that want it (JITs and the like), and it'd be a pain to mark them
     // all as friends.
-    bool nonProxyIsExtensible() const {
-        MOZ_ASSERT(!uninlinedIsProxy());
-
-        // [[Extensible]] for ordinary non-proxy objects is an object flag.
-        return !lastProperty()->hasObjectFlag(js::BaseShape::NOT_EXTENSIBLE);
-    }
+    inline bool nonProxyIsExtensible() const;
 
   public:
     /*
      * Iterator-specific getters and setters.
      */
 
     static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
 
@@ -571,18 +538,18 @@ class JSObject : public js::gc::Cell
     }
 
 #ifdef DEBUG
     void dump();
 #endif
 
     /* JIT Accessors */
 
-    static size_t offsetOfShape() { return offsetof(JSObject, shape_); }
     static size_t offsetOfGroup() { return offsetof(JSObject, group_); }
+    static size_t offsetOfShape() { return sizeof(JSObject); }
 
     // Maximum size in bytes of a JSObject.
     static const size_t MAX_BYTE_SIZE = 4 * sizeof(void *) + 16 * sizeof(JS::Value);
 
   private:
     JSObject() = delete;
     JSObject(const JSObject &other) = delete;
     void operator=(const JSObject &other) = delete;
@@ -619,22 +586,22 @@ operator==(const JSObject &lhs, const JS
 
 static MOZ_ALWAYS_INLINE bool
 operator!=(const JSObject &lhs, const JSObject &rhs)
 {
     return &lhs != &rhs;
 }
 
 // Size of the various GC thing allocation sizes used for objects.
-struct JSObject_Slots0 : JSObject { void *data[2]; };
-struct JSObject_Slots2 : JSObject { void *data[2]; js::Value fslots[2]; };
-struct JSObject_Slots4 : JSObject { void *data[2]; js::Value fslots[4]; };
-struct JSObject_Slots8 : JSObject { void *data[2]; js::Value fslots[8]; };
-struct JSObject_Slots12 : JSObject { void *data[2]; js::Value fslots[12]; };
-struct JSObject_Slots16 : JSObject { void *data[2]; js::Value fslots[16]; };
+struct JSObject_Slots0 : JSObject { void *data[3]; };
+struct JSObject_Slots2 : JSObject { void *data[3]; js::Value fslots[2]; };
+struct JSObject_Slots4 : JSObject { void *data[3]; js::Value fslots[4]; };
+struct JSObject_Slots8 : JSObject { void *data[3]; js::Value fslots[8]; };
+struct JSObject_Slots12 : JSObject { void *data[3]; js::Value fslots[12]; };
+struct JSObject_Slots16 : JSObject { void *data[3]; js::Value fslots[16]; };
 
 /* static */ MOZ_ALWAYS_INLINE void
 JSObject::readBarrier(JSObject *obj)
 {
     if (!isNullLike(obj) && obj->isTenured())
         obj->asTenured().readBarrier(&obj->asTenured());
 }
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -20,16 +20,34 @@
 #include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
 
 #include "vm/TypeInference-inl.h"
 
+inline js::Shape *
+JSObject::maybeShape() const
+{
+    if (is<js::UnboxedPlainObject>())
+        return nullptr;
+    return *reinterpret_cast<js::Shape **>(uintptr_t(this) + offsetOfShape());
+}
+
+inline js::Shape *
+JSObject::ensureShape(js::ExclusiveContext *cx)
+{
+    if (is<js::UnboxedPlainObject>() && !js::UnboxedPlainObject::convertToNative(cx->asJSContext(), this))
+        return nullptr;
+    js::Shape *shape = maybeShape();
+    MOZ_ASSERT(shape);
+    return shape;
+}
+
 inline void
 JSObject::finalize(js::FreeOp *fop)
 {
     js::probes::FinalizeObject(this);
 
 #ifdef DEBUG
     MOZ_ASSERT(isTenured());
     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
@@ -62,18 +80,18 @@ JSObject::finalize(js::FreeOp *fop)
             fop->free_(elements);
         }
     }
 
     // For dictionary objects (which must be native), it's possible that
     // unreachable shapes may be marked whose listp points into this object.
     // In case this happens, null out the shape's pointer here so that a moving
     // GC will not try to access the dead object.
-    if (shape_->listp == &shape_)
-        shape_->listp = nullptr;
+    if (nobj->shape_->listp == &nobj->shape_)
+        nobj->shape_->listp = nullptr;
 }
 
 /* static */ inline bool
 JSObject::setSingleton(js::ExclusiveContext *cx, js::HandleObject obj)
 {
     MOZ_ASSERT_IF(cx->isJSContext(), !IsInsideNursery(obj));
 
     js::ObjectGroup *group = js::ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
@@ -183,25 +201,25 @@ js::DeleteElement(JSContext *cx, HandleO
 
 /* * */
 
 inline bool
 JSObject::isQualifiedVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isQualifiedVarObj();
-    return lastProperty()->hasObjectFlag(js::BaseShape::QUALIFIED_VAROBJ);
+    return hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
 }
 
 inline bool
 JSObject::isUnqualifiedVarObj()
 {
     if (is<js::DebugScopeObject>())
         return as<js::DebugScopeObject>().scope().isUnqualifiedVarObj();
-    return lastProperty()->hasObjectFlag(js::BaseShape::UNQUALIFIED_VAROBJ);
+    return hasAllFlags(js::BaseShape::UNQUALIFIED_VAROBJ);
 }
 
 namespace js {
 
 inline bool
 ClassCanHaveFixedData(const Class *clasp)
 {
     // Normally, the number of fixed slots given an object is the maximum
@@ -242,19 +260,20 @@ JSObject::create(js::ExclusiveContext *c
     const js::Class *clasp = group->clasp();
     size_t nDynamicSlots =
         js::NativeObject::dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp);
 
     JSObject *obj = js::NewGCObject<js::CanGC>(cx, kind, nDynamicSlots, heap, clasp);
     if (!obj)
         return nullptr;
 
-    obj->shape_.init(shape);
     obj->group_.init(group);
 
+    obj->setInitialShapeMaybeNonNative(shape);
+
     // Note: slots are created and assigned internally by NewGCObject.
     obj->setInitialElementsMaybeNonNative(js::emptyObjectElements);
 
     if (clasp->hasPrivate())
         obj->as<js::NativeObject>().privateRef(shape->numFixedSlots()) = nullptr;
 
     if (size_t span = shape->slotSpan())
         obj->as<js::NativeObject>().initializeSlotRange(0, span);
@@ -264,27 +283,48 @@ JSObject::create(js::ExclusiveContext *c
         memset(obj->as<JSFunction>().fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind));
 
     js::gc::TraceCreateObject(obj);
 
     return obj;
 }
 
 inline void
+JSObject::setInitialShapeMaybeNonNative(js::Shape *shape)
+{
+    static_cast<js::NativeObject *>(this)->shape_.init(shape);
+}
+
+inline void
+JSObject::setShapeMaybeNonNative(js::Shape *shape)
+{
+    MOZ_ASSERT(!is<js::UnboxedPlainObject>());
+    static_cast<js::NativeObject *>(this)->shape_ = shape;
+}
+
+inline void
 JSObject::setInitialSlotsMaybeNonNative(js::HeapSlot *slots)
 {
     static_cast<js::NativeObject *>(this)->slots_ = slots;
 }
 
 inline void
 JSObject::setInitialElementsMaybeNonNative(js::HeapSlot *elements)
 {
     static_cast<js::NativeObject *>(this)->elements_ = elements;
 }
 
+inline JSObject *
+JSObject::getMetadata() const
+{
+    if (js::Shape *shape = maybeShape())
+        return shape->getObjectMetadata();
+    return nullptr;
+}
+
 inline js::GlobalObject &
 JSObject::global() const
 {
 #ifdef DEBUG
     JSObject *obj = const_cast<JSObject *>(this);
     while (JSObject *parent = obj->getParent())
         obj = parent;
 #endif
@@ -298,16 +338,95 @@ JSObject::global() const
 }
 
 inline bool
 JSObject::isOwnGlobal() const
 {
     return &global() == this;
 }
 
+inline bool
+JSObject::hasAllFlags(js::BaseShape::Flag flags) const
+{
+    MOZ_ASSERT(flags);
+    if (js::Shape *shape = maybeShape())
+        return shape->hasAllObjectFlags(flags);
+    return false;
+}
+
+inline bool
+JSObject::nonProxyIsExtensible() const
+{
+    MOZ_ASSERT(!uninlinedIsProxy());
+
+    // [[Extensible]] for ordinary non-proxy objects is an object flag.
+    return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
+}
+
+inline bool
+JSObject::isBoundFunction() const
+{
+    return hasAllFlags(js::BaseShape::BOUND_FUNCTION);
+}
+
+inline bool
+JSObject::watched() const
+{
+    return hasAllFlags(js::BaseShape::WATCHED);
+}
+
+inline bool
+JSObject::isDelegate() const
+{
+    return hasAllFlags(js::BaseShape::DELEGATE);
+}
+
+inline bool
+JSObject::hasUncacheableProto() const
+{
+    return hasAllFlags(js::BaseShape::UNCACHEABLE_PROTO);
+}
+
+inline bool
+JSObject::hadElementsAccess() const
+{
+    return hasAllFlags(js::BaseShape::HAD_ELEMENTS_ACCESS);
+}
+
+inline bool
+JSObject::isIndexed() const
+{
+    return hasAllFlags(js::BaseShape::INDEXED);
+}
+
+inline bool
+JSObject::nonLazyPrototypeIsImmutable() const
+{
+    MOZ_ASSERT(!hasLazyPrototype());
+    return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE);
+}
+
+inline bool
+JSObject::isIteratedSingleton() const
+{
+    return hasAllFlags(js::BaseShape::ITERATED_SINGLETON);
+}
+
+inline bool
+JSObject::isNewGroupUnknown() const
+{
+    return hasAllFlags(js::BaseShape::NEW_GROUP_UNKNOWN);
+}
+
+inline bool
+JSObject::wasNewScriptCleared() const
+{
+    return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
+}
+
 namespace js {
 
 PropDesc::PropDesc(const Value &getter, const Value &setter,
                    Enumerability enumerable, Configurability configurable)
   : value_(UndefinedValue()),
     get_(getter), set_(setter),
     attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
           (enumerable ? JSPROP_ENUMERATE : 0) |
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -248,18 +248,18 @@ Shape::fixupDictionaryShapeAfterMovingGC
     if (kind == FINALIZE_SHAPE || kind == FINALIZE_ACCESSOR_SHAPE) {
         // listp points to the parent field of the next shape.
         Shape *next = reinterpret_cast<Shape *>(uintptr_t(listp) -
                                                 offsetof(Shape, parent));
         listp = &gc::MaybeForwarded(next)->parent;
     } else {
         // listp points to the shape_ field of an object.
         JSObject *last = reinterpret_cast<JSObject *>(uintptr_t(listp) -
-                                                      offsetof(JSObject, shape_));
-        listp = &gc::MaybeForwarded(last)->shape_;
+                                                      JSObject::offsetOfShape());
+        listp = &gc::MaybeForwarded(last)->as<NativeObject>().shape_;
     }
 }
 
 void
 Shape::fixupShapeTreeAfterMovingGC()
 {
     if (kids.isNull())
         return;
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -606,16 +606,18 @@ js::proxy_Trace(JSTracer *trc, JSObject 
     ProxyObject::trace(trc, obj);
 }
 
 /* static */ void
 ProxyObject::trace(JSTracer *trc, JSObject *obj)
 {
     ProxyObject *proxy = &obj->as<ProxyObject>();
 
+    MarkShape(trc, &proxy->shape, "ProxyObject_shape");
+
 #ifdef DEBUG
     if (trc->runtime()->gc.isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) {
         JSObject *referent = MaybeForwarded(&proxy->private_().toObject());
         if (referent->compartment() != proxy->compartment()) {
             /*
              * Assert that this proxy is tracked in the wrapper map. We maintain
              * the invariant that the wrapped object is the key in the wrapper map.
              */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2879,17 +2879,17 @@ static bool
 ShapeOf(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isObject()) {
         JS_ReportError(cx, "shapeOf: object expected");
         return false;
     }
     JSObject *obj = &args[0].toObject();
-    args.rval().set(JS_NumberValue(double(uintptr_t(obj->lastProperty()) >> 3)));
+    args.rval().set(JS_NumberValue(double(uintptr_t(obj->maybeShape()) >> 3)));
     return true;
 }
 
 /*
  * Check that t1 comes strictly before t2. The function correctly deals with
  * wrap-around between t2 and t1 assuming that t2 and t1 stays within INT32_MAX
  * from each other. We use MAX_TIMEOUT_INTERVAL to enforce this restriction.
  */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2043,17 +2043,17 @@ Debugger::observesAsmJS() const
 // hookObservesAllExecution.
 bool
 Debugger::updateObservesAllExecutionOnDebuggees(JSContext *cx, IsObserving observing)
 {
     ExecutionObservableCompartments obs(cx);
     if (!obs.init())
         return false;
 
-    for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
+    for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject *global = r.front();
         JSCompartment *comp = global->compartment();
 
         if (comp->debuggerObservesAllExecution() == observing)
             continue;
 
         // It's expensive to eagerly invalidate and recompile a compartment,
         // so add the compartment to the set only if we are observing.
@@ -2064,17 +2064,17 @@ Debugger::updateObservesAllExecutionOnDe
     }
 
     return updateExecutionObservability(cx, obs, observing);
 }
 
 void
 Debugger::updateObservesAsmJSOnDebuggees(IsObserving observing)
 {
-    for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
+    for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject *global = r.front();
         JSCompartment *comp = global->compartment();
 
         if (comp->debuggerObservesAsmJS() == observing)
             continue;
 
         comp->updateDebuggerObservesAsmJS();
     }
@@ -2108,25 +2108,34 @@ Debugger::markCrossCompartmentEdges(JSTr
  * to mark the keys and the private pointer in the wrapper object.
  *
  * We must scan all Debugger objects regardless of whether they *currently* have
  * any debuggees in a compartment being GC'd, because the WeakMap entries
  * persist even when debuggees are removed.
  *
  * This happens during the initial mark phase, not iterative marking, because
  * all the edges being reported here are strong references.
+ *
+ * This method is also used during compacting GC to update cross compartment
+ * pointers in zones that are not currently being compacted.
  */
 /* static */ void
-Debugger::markAllCrossCompartmentEdges(JSTracer *trc)
+Debugger::markIncomingCrossCompartmentEdges(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime();
+    gc::State state = rt->gc.state();
+    MOZ_ASSERT(state == gc::MARK_ROOTS || state == gc::COMPACT);
 
     for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
-        if (!dbg->object->zone()->isCollecting())
+        Zone *zone = dbg->object->zone();
+        if ((state == gc::MARK_ROOTS && !zone->isCollecting()) ||
+            (state == gc::COMPACT && !zone->isGCCompacting()))
+        {
             dbg->markCrossCompartmentEdges(trc);
+        }
     }
 }
 
 /*
  * This method has two tasks:
  *   1. Mark Debugger objects that are unreachable except for debugger hooks that
  *      may yet be called.
  *   2. Mark breakpoint handlers.
@@ -2207,22 +2216,22 @@ Debugger::markAllIteratively(GCMarker *t
  * GC: the minor GC cannot apply the weak constraints of the full GC because it
  * visits only part of the heap.
  */
 /* static */ void
 Debugger::markAll(JSTracer *trc)
 {
     JSRuntime *rt = trc->runtime();
     for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
-        GlobalObjectSet &debuggees = dbg->debuggees;
-        for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
+        WeakGlobalObjectSet &debuggees = dbg->debuggees;
+        for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
             GlobalObject *global = e.front();
             MarkObjectUnbarriered(trc, &global, "Global Object");
             if (global != e.front())
-                e.rekeyFront(global);
+                e.rekeyFront(ReadBarrieredGlobalObject(global));
         }
 
         HeapPtrNativeObject &dbgobj = dbg->toJSObjectRef();
         MarkObject(trc, &dbgobj, "Debugger Object");
 
         dbg->scripts.trace(trc);
         dbg->sources.trace(trc);
         dbg->objects.trace(trc);
@@ -2290,17 +2299,17 @@ Debugger::sweepAll(FreeOp *fop)
 
     for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
         if (IsObjectAboutToBeFinalized(&dbg->object)) {
             /*
              * dbg is being GC'd. Detach it from its debuggees. The debuggee
              * might be GC'd too. Since detaching requires access to both
              * objects, this must be done before finalize time.
              */
-            for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
+            for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
                 dbg->removeDebuggeeGlobal(fop, e.front(), &e);
         }
     }
 }
 
 /* static */ void
 Debugger::detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global)
 {
@@ -2620,17 +2629,17 @@ Debugger::getAllowUnobservedAsmJS(JSCont
 /* static */ bool
 Debugger::setAllowUnobservedAsmJS(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "set allowUnobservedAsmJS", args, dbg);
     if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1))
         return false;
     dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
 
-    for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
+    for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject *global = r.front();
         JSCompartment *comp = global->compartment();
         comp->updateDebuggerObservesAsmJS();
     }
 
     args.rval().setUndefined();
     return true;
 }
@@ -2778,17 +2787,17 @@ Debugger::removeDebuggee(JSContext *cx, 
 Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
 
     ExecutionObservableCompartments obs(cx);
     if (!obs.init())
         return false;
 
-    for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
+    for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
         Rooted<GlobalObject *> global(cx, e.front());
         dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e);
 
         // See note about adding to the observable set in removeDebuggee.
         if (global->getDebuggers()->empty() && !obs.add(global->compartment()))
             return false;
     }
 
@@ -2821,18 +2830,18 @@ Debugger::getDebuggees(JSContext *cx, un
     // update the debuggees set while we are iterating it.
     unsigned count = dbg->debuggees.count();
     AutoValueVector debuggees(cx);
     if (!debuggees.resize(count))
         return false;
     unsigned i = 0;
     {
         JS::AutoCheckCannotGC nogc;
-        for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
-            debuggees[i++].setObject(*e.front());
+        for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
+            debuggees[i++].setObject(*e.front().get());
     }
 
     RootedArrayObject arrobj(cx, NewDenseFullyAllocatedArray(cx, count));
     if (!arrobj)
         return false;
     arrobj->ensureDenseInitializedLength(cx, 0, count);
     for (i = 0; i < count; i++) {
         RootedValue v(cx, debuggees[i]);
@@ -2867,17 +2876,17 @@ Debugger::getNewestFrame(JSContext *cx, 
     args.rval().setNull();
     return true;
 }
 
 /* static */ bool
 Debugger::clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
-    for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
+    for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
         r.front()->compartment()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
                                                      dbg, NullPtr());
     return true;
 }
 
 /* static */ bool
 Debugger::construct(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -3035,17 +3044,18 @@ Debugger::addDebuggeeGlobal(JSContext *c
     /* Don't leave the object metadata hook set if we OOM'd. */
     if (setMetadataCallback)
         debuggeeCompartment->forgetObjectMetadataCallback();
 
     return false;
 }
 
 void
-Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, GlobalObjectSet::Enum *debugEnum)
+Debugger::removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
+                               WeakGlobalObjectSet::Enum *debugEnum)
 {
     /*
      * The caller might have found global by enumerating this->debuggees; if
      * so, use HashSet::Enum::removeFront rather than HashSet::remove below,
      * to avoid invalidating the live enumerator.
      */
     MOZ_ASSERT(debuggees.has(global));
     MOZ_ASSERT_IF(debugEnum, debugEnum->front() == global);
@@ -3389,17 +3399,17 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
 
     /*
      * Arrange for this ScriptQuery to match all scripts running in debuggee
      * globals.
      */
     bool matchAllDebuggeeGlobals() {
         MOZ_ASSERT(compartments.count() == 0);
         /* Build our compartment set from the debugger's set of debuggee globals. */
-        for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
+        for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
             if (!addCompartment(r.front()->compartment())) {
                 ReportOutOfMemory(cx);
                 return false;
             }
         }
         return true;
     }
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -30,16 +30,20 @@ enum JSTrapStatus {
     JSTRAP_LIMIT
 };
 
 namespace js {
 
 class Breakpoint;
 class DebuggerMemory;
 
+typedef HashSet<ReadBarrieredGlobalObject,
+                DefaultHasher<ReadBarrieredGlobalObject>,
+                SystemAllocPolicy> WeakGlobalObjectSet;
+
 /*
  * A weakmap from GC thing keys to JSObject values that supports the keys being
  * in different compartments to the values. All values must be in the same
  * compartment.
  *
  * The purpose of this is to allow the garbage collector to easily find edges
  * from debugee object compartments to debugger compartments when calculating
  * the compartment groups.  Note that these edges are the inverse of the edges
@@ -233,17 +237,17 @@ class Debugger : private mozilla::Linked
     };
 
     // Return true if the given compartment is a debuggee of this debugger,
     // false otherwise.
     bool isDebuggee(const JSCompartment *compartment) const;
 
   private:
     HeapPtrNativeObject object;         /* The Debugger object. Strong reference. */
-    GlobalObjectSet debuggees;          /* Debuggee globals. Cross-compartment weak references. */
+    WeakGlobalObjectSet debuggees;      /* Debuggee globals. Cross-compartment weak references. */
     js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
     bool enabled;
     JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
 
     struct AllocationSite : public mozilla::LinkedListElement<AllocationSite>
     {
         AllocationSite(HandleObject frame, int64_t when) : frame(frame), when(when) {
             MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
@@ -320,17 +324,18 @@ class Debugger : private mozilla::Linked
     uint32_t traceLoggerScriptedCallsLastDrainedId;
     uint32_t traceLoggerScriptedCallsLastDrainedIteration;
 
     class FrameRange;
     class ScriptQuery;
     class ObjectQuery;
 
     bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj);
-    void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global, GlobalObjectSet::Enum *debugEnum);
+    void removeDebuggeeGlobal(FreeOp *fop, GlobalObject *global,
+                              WeakGlobalObjectSet::Enum *debugEnum);
 
     /*
      * Cope with an error or exception in a debugger hook.
      *
      * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
      * addition, vp is given, then parse the value returned by
      * uncaughtExceptionHook as a resumption value.
      *
@@ -524,17 +529,17 @@ class Debugger : private mozilla::Linked
     inline const js::HeapPtrNativeObject &toJSObject() const;
     inline js::HeapPtrNativeObject &toJSObjectRef();
     static inline Debugger *fromJSObject(JSObject *obj);
     static Debugger *fromChildJSObject(JSObject *obj);
 
     bool hasMemory() const;
     DebuggerMemory &memory() const;
 
-    GlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
+    WeakGlobalObjectSet::Range allDebuggees() const { return debuggees.all(); }
 
     /*********************************** Methods for interaction with the GC. */
 
     /*
      * A Debugger object is live if:
      *   * the Debugger JSObject is live (Debugger::trace handles this case); OR
      *   * it is in the middle of dispatching an event (the event dispatching
      *     code roots it in this case); OR
@@ -543,17 +548,17 @@ class Debugger : private mozilla::Linked
      *       - it has a debugger hook installed
      *       - it has a breakpoint set on a live script
      *       - it has a watchpoint set on a live object.
      *
      * Debugger::markAllIteratively handles the last case. If it finds any
      * Debugger objects that are definitely live but not yet marked, it marks
      * them and returns true. If not, it returns false.
      */
-    static void markAllCrossCompartmentEdges(JSTracer *tracer);
+    static void markIncomingCrossCompartmentEdges(JSTracer *tracer);
     static bool markAllIteratively(GCMarker *trc);
     static void markAll(JSTracer *trc);
     static void sweepAll(FreeOp *fop);
     static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global);
     static void findCompartmentEdges(JS::Zone *v, gc::ComponentFinder<JS::Zone> &finder);
 
     /*
      * JSTrapStatus Overview
@@ -884,17 +889,18 @@ bool
 Debugger::observesNewGlobalObject() const
 {
     return enabled && getHook(OnNewGlobalObject);
 }
 
 bool
 Debugger::observesGlobal(GlobalObject *global) const
 {
-    return debuggees.has(global);
+    ReadBarriered<GlobalObject*> debuggee(global);
+    return debuggees.has(debuggee);
 }
 
 /* static */ void
 Debugger::onNewScript(JSContext *cx, HandleScript script)
 {
     // We early return in slowPathOnNewScript for self-hosted scripts, so we can
     // ignore those in our assertion here.
     MOZ_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() &&
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -134,27 +134,27 @@ DebuggerMemory::setTrackingAllocationSit
 
     if (enabling == dbg->trackingAllocationSites) {
         // Nothing to do here...
         args.rval().setUndefined();
         return true;
     }
 
     if (enabling) {
-        for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
+        for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
             JSCompartment *compartment = r.front()->compartment();
             if (compartment->hasObjectMetadataCallback()) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                                      JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
                 return false;
             }
         }
     }
 
-    for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
+    for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
         if (enabling) {
             r.front()->compartment()->setObjectMetadataCallback(SavedStacksMetadataCallback);
         } else {
             r.front()->compartment()->forgetObjectMetadataCallback();
         }
     }
 
     if (!enabling)
@@ -773,17 +773,17 @@ DebuggerMemory::takeCensus(JSContext *cx
     dbg::DefaultCensusHandler handler(census);
     if (!handler.init(census))
         return false;
 
     Debugger *dbg = memory->getDebugger();
     RootedObject dbgObj(cx, dbg->object);
 
     // Populate our target set of debuggee zones.
-    for (GlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
+    for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
         if (!census.debuggeeZones.put(r.front()->zone()))
             return false;
     }
 
     {
         Maybe<JS::AutoCheckCannotGC> maybeNoGC;
         JS::ubi::RootList rootList(cx, maybeNoGC);
         if (!rootList.init(dbgObj))
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnativestack.h"
 #include "jsnum.h" // For FIX_FPU()
 #include "prmjtime.h"
 
 #include "frontend/BytecodeCompiler.h"
+#include "gc/GCInternals.h"
 #include "jit/IonBuilder.h"
 #include "vm/Debugger.h"
 #include "vm/TraceLogging.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
@@ -734,16 +735,21 @@ GlobalHelperThreadState::canStartGCHelpe
 }
 
 bool
 GlobalHelperThreadState::canStartGCParallelTask()
 {
     return !gcParallelWorklist().empty();
 }
 
+js::GCParallelTask::~GCParallelTask()
+{
+    join();
+}
+
 bool
 js::GCParallelTask::startWithLockHeld()
 {
     MOZ_ASSERT(HelperThreadState().isLocked());
 
     // Tasks cannot be started twice.
     MOZ_ASSERT(state == NotStarted);
 
@@ -895,21 +901,25 @@ GlobalHelperThreadState::finishParseTask
         return nullptr;
     }
 
     LeaveParseTaskZone(rt, parseTask);
 
     // Point the prototypes of any objects in the script's compartment to refer
     // to the corresponding prototype in the new compartment. This will briefly
     // create cross compartment pointers, which will be fixed by the
-    // MergeCompartments call below.
+    // MergeCompartments call below.  It's not safe for a GC to observe this
+    // state, so finish any ongoing GC first and assert that we can't trigger
+    // another one.
+    gc::AutoFinishGC finishGC(rt);
     for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_OBJECT_GROUP);
          !iter.done();
          iter.next())
     {
+        JS::AutoAssertNoAlloc noAlloc(rt);
         ObjectGroup *group = iter.get<ObjectGroup>();
         TaggedProto proto(group->proto());
         if (!proto.isObject())
             continue;
 
         JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject());
         if (key == JSProto_Null)
             continue;
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -381,23 +381,24 @@ NativeObject::setLastPropertyMakeNonNati
 
     shape_ = shape;
 }
 
 void
 NativeObject::setLastPropertyMakeNative(ExclusiveContext *cx, Shape *shape)
 {
     MOZ_ASSERT(getClass()->isNative());
-    MOZ_ASSERT(!lastProperty()->isNative());
     MOZ_ASSERT(shape->isNative());
-    MOZ_ASSERT(!inDictionaryMode());
     MOZ_ASSERT(!shape->inDictionary());
-    MOZ_ASSERT(shape->compartment() == compartment());
 
-    shape_ = shape;
+    // This method is used to convert unboxed objects into native objects. In
+    // this case, the shape_ field was previously used to store other data and
+    // this should be treated as an initialization.
+    shape_.init(shape);
+
     slots_ = nullptr;
     elements_ = emptyObjectElements;
 
     size_t oldSpan = shape->numFixedSlots();
     size_t newSpan = shape->slotSpan();
 
     // A failure at this point will leave the object as a mutant, and we
     // can't recover.
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -336,16 +336,19 @@ DenseRangeWriteBarrierPost(JSRuntime *rt
  * in this case numFixedSlots() is zero) or to a dynamically allocated array.
  *
  * Slots and elements may both be non-empty. The slots may be either names or
  * indexes; no indexed property will be in both the slots and elements.
  */
 class NativeObject : public JSObject
 {
   protected:
+    // Property layout description and other state.
+    HeapPtrShape shape_;
+
     /* Slots for object properties. */
     js::HeapSlot *slots_;
 
     /* Slots for object dense elements. */
     js::HeapSlot *elements_;
 
     friend bool
     ArraySetLength(JSContext *cx, Handle<ArrayObject*> obj, HandleId id, unsigned attrs,
@@ -373,16 +376,29 @@ class NativeObject : public JSObject
 
         static_assert(MAX_FIXED_SLOTS <= Shape::FIXED_SLOTS_MAX,
                       "verify numFixedSlots() bitfield is big enough");
         static_assert(sizeof(NativeObject) + MAX_FIXED_SLOTS * sizeof(Value) == JSObject::MAX_BYTE_SIZE,
                       "inconsistent maximum object size");
     }
 
   public:
+    Shape *lastProperty() const {
+        MOZ_ASSERT(shape_);
+        return shape_;
+    }
+
+    uint32_t propertyCount() const {
+        return lastProperty()->entryCount();
+    }
+
+    bool hasShapeTable() const {
+        return lastProperty()->hasTable();
+    }
+
     HeapSlotArray getDenseElements() {
         return HeapSlotArray(elements_, !getElementsHeader()->isCopyOnWrite());
     }
     HeapSlotArray getDenseElementsAllowCopyOnWrite() {
         // Backdoor allowing direct access to copy on write elements.
         return HeapSlotArray(elements_, true);
     }
     const Value &getDenseElement(uint32_t idx) {
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -307,17 +307,17 @@ JSObject::makeLazyGroup(JSContext *cx, H
         if (!fun->getOrCreateScript(cx))
             return nullptr;
     }
 
     // Find flags which need to be specified immediately on the object.
     // Don't track whether singletons are packed.
     ObjectGroupFlags initialFlags = OBJECT_FLAG_SINGLETON | OBJECT_FLAG_NON_PACKED;
 
-    if (obj->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
+    if (obj->isIteratedSingleton())
         initialFlags |= OBJECT_FLAG_ITERATED;
 
     if (obj->isIndexed())
         initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
 
     if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
         initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
 
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -165,17 +165,17 @@ js::ForOfPIC::Stub *
 js::ForOfPIC::Chain::getMatchingStub(JSObject *obj)
 {
     // Ensure PIC is initialized and not disabled.
     if (!initialized_ || disabled_)
         return nullptr;
 
     // Check if there is a matching stub.
     for (Stub *stub = stubs(); stub != nullptr; stub = stub->next()) {
-        if (stub->shape() == obj->lastProperty())
+        if (stub->shape() == obj->maybeShape())
             return stub;
     }
 
     return nullptr;
 }
 
 bool
 js::ForOfPIC::Chain::isOptimizableArray(JSObject *obj)
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -11,16 +11,18 @@
 #include "vm/NativeObject.h"
 
 namespace js {
 
 // This is the base class for the various kinds of proxy objects.  It's never
 // instantiated.
 class ProxyObject : public JSObject
 {
+    HeapPtrShape shape;
+
     // GetProxyDataLayout computes the address of this field.
     ProxyDataLayout data;
 
     void static_asserts() {
         static_assert(sizeof(ProxyObject) == sizeof(JSObject_Slots0),
                       "proxy object size must match GC thing size");
         static_assert(offsetof(ProxyObject, data) == ProxyDataOffset,
                       "proxy object layout must match shadow interface");
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -33,38 +33,38 @@ NewObjectCache::lookupGlobal(const Class
 inline void
 NewObjectCache::fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global,
                            gc::AllocKind kind, NativeObject *obj)
 {
     //MOZ_ASSERT(global == obj->getGlobal());
     return fill(entry, clasp, global, kind, obj);
 }
 
-inline JSObject *
+inline NativeObject *
 NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entryIndex, js::gc::InitialHeap heap)
 {
     // The new object cache does not account for metadata attached via callbacks.
     MOZ_ASSERT(!cx->compartment()->hasObjectMetadataCallback());
 
     MOZ_ASSERT(unsigned(entryIndex) < mozilla::ArrayLength(entries));
     Entry *entry = &entries[entryIndex];
 
-    JSObject *templateObj = reinterpret_cast<JSObject *>(&entry->templateObject);
+    NativeObject *templateObj = reinterpret_cast<NativeObject *>(&entry->templateObject);
 
     // Do an end run around JSObject::group() to avoid doing AutoUnprotectCell
     // on the templateObj, which is not a GC thing and can't use runtimeFromAnyThread.
     ObjectGroup *group = templateObj->group_;
 
     if (group->shouldPreTenure())
         heap = gc::TenuredHeap;
 
     if (cx->runtime()->gc.upcomingZealousGC())
         return nullptr;
 
-    JSObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap, group->clasp());
+    NativeObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap, group->clasp());
     if (obj) {
         copyCachedToObject(obj, templateObj, entry->kind);
         probes::CreateObject(cx, obj);
         js::gc::TraceCreateObject(obj);
         return obj;
     }
 
     // Trigger an identical allocation to the one that notified us of OOM so
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -292,17 +292,17 @@ class NewObjectCache
         return lookup(group->clasp(), group, kind, pentry);
     }
 
     /*
      * Return a new object from a cache hit produced by a lookup method, or
      * nullptr if returning the object could possibly trigger GC (does not
      * indicate failure).
      */
-    inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap);
+    inline NativeObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap);
 
     /* Fill an entry after a cache miss. */
     void fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto,
                    gc::AllocKind kind, NativeObject *obj);
 
     inline void fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObject *global,
                            gc::AllocKind kind, NativeObject *obj);
 
@@ -339,17 +339,17 @@ class NewObjectCache
         entry->clasp = clasp;
         entry->key = key;
         entry->kind = kind;
 
         entry->nbytes = gc::Arena::thingSize(kind);
         js_memcpy(&entry->templateObject, obj, entry->nbytes);
     }
 
-    static void copyCachedToObject(JSObject *dst, JSObject *src, gc::AllocKind kind) {
+    static void copyCachedToObject(NativeObject *dst, NativeObject *src, gc::AllocKind kind) {
         js_memcpy(dst, src, gc::Arena::thingSize(kind));
         Shape::writeBarrierPost(dst->shape_, &dst->shape_);
         ObjectGroup::writeBarrierPost(dst->group_, &dst->group_);
     }
 };
 
 /*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -494,17 +494,17 @@ NativeObject::addProperty(ExclusiveConte
     if (obj->inDictionaryMode())
         entry = &obj->lastProperty()->table().search(id, true);
 
     return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, entry,
                                allowDictionary);
 }
 
 static bool
-ShouldConvertToDictionary(JSObject *obj)
+ShouldConvertToDictionary(NativeObject *obj)
 {
     /*
      * Use a lower limit if this object is likely a hashmap (SETELEM was used
      * to set properties).
      */
     if (obj->hadElementsAccess())
         return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT_WITH_ELEMENTS_ACCESS;
     return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT;
@@ -1133,31 +1133,35 @@ NativeObject::shadowingShapeChange(Exclu
 
 /* static */ bool
 JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent)
 {
     if (parent && !parent->setDelegate(cx))
         return false;
 
     if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) {
-        StackBaseShape base(obj->lastProperty());
+        StackBaseShape base(obj->as<NativeObject>().lastProperty());
         base.parent = parent;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
-        obj->lastProperty()->base()->adoptUnowned(nbase);
+        obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), obj->shape_);
+    Shape *existingShape = obj->ensureShape(cx);
+    if (!existingShape)
+        return false;
+
+    Shape *newShape = Shape::setObjectParent(cx, parent, obj->getTaggedProto(), existingShape);
     if (!newShape)
         return false;
 
-    obj->shape_ = newShape;
+    obj->setShapeMaybeNonNative(newShape);
     return true;
 }
 
 /* static */ Shape *
 Shape::setObjectParent(ExclusiveContext *cx, JSObject *parent, TaggedProto proto, Shape *last)
 {
     if (last->getObjectParent() == parent)
         return last;
@@ -1168,31 +1172,35 @@ Shape::setObjectParent(ExclusiveContext 
     RootedShape lastRoot(cx, last);
     return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
 /* static */ bool
 JSObject::setMetadata(JSContext *cx, HandleObject obj, HandleObject metadata)
 {
     if (obj->isNative() && obj->as<NativeObject>().inDictionaryMode()) {
-        StackBaseShape base(obj->lastProperty());
+        StackBaseShape base(obj->as<NativeObject>().lastProperty());
         base.metadata = metadata;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
-        obj->lastProperty()->base()->adoptUnowned(nbase);
+        obj->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), obj->shape_);
+    Shape *existingShape = obj->ensureShape(cx);
+    if (!existingShape)
+        return false;
+
+    Shape *newShape = Shape::setObjectMetadata(cx, metadata, obj->getTaggedProto(), existingShape);
     if (!newShape)
         return false;
 
-    obj->shape_ = newShape;
+    obj->setShapeMaybeNonNative(newShape);
     return true;
 }
 
 /* static */ Shape *
 Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, Shape *last)
 {
     if (last->getObjectMetadata() == metadata)
         return last;
@@ -1200,55 +1208,55 @@ Shape::setObjectMetadata(JSContext *cx, 
     StackBaseShape base(last);
     base.metadata = metadata;
 
     RootedShape lastRoot(cx, last);
     return replaceLastProperty(cx, base, proto, lastRoot);
 }
 
 bool
-JSObject::setFlags(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flags_,
-                   GenerateShape generateShape)
+JSObject::setFlags(ExclusiveContext *cx, BaseShape::Flag flags, GenerateShape generateShape)
 {
-    BaseShape::Flag flags = (BaseShape::Flag) flags_;
-
-    if ((lastProperty()->getObjectFlags() & flags) == flags)
+    if (hasAllFlags(flags))
         return true;
 
     RootedObject self(cx, this);
 
     if (isNative() && as<NativeObject>().inDictionaryMode()) {
         if (generateShape == GENERATE_SHAPE && !as<NativeObject>().generateOwnShape(cx))
             return false;
-        StackBaseShape base(self->lastProperty());
+        StackBaseShape base(self->as<NativeObject>().lastProperty());
         base.flags |= flags;
         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return false;
 
-        self->lastProperty()->base()->adoptUnowned(nbase);
+        self->as<NativeObject>().lastProperty()->base()->adoptUnowned(nbase);
         return true;
     }
 
-    Shape *newShape =
-        Shape::setObjectFlags(cx, flags, self->getTaggedProto(), self->lastProperty());
+    Shape *existingShape = self->ensureShape(cx);
+    if (!existingShape)
+        return false;
+
+    Shape *newShape = Shape::setObjectFlags(cx, flags, self->getTaggedProto(), existingShape);
     if (!newShape)
         return false;
 
-    self->shape_ = newShape;
+    self->setShapeMaybeNonNative(newShape);
     return true;
 }
 
 bool
 NativeObject::clearFlag(ExclusiveContext *cx, BaseShape::Flag flag)
 {
     MOZ_ASSERT(inDictionaryMode());
-    MOZ_ASSERT(lastProperty()->getObjectFlags() & flag);
 
-    RootedObject self(cx, this);
+    RootedNativeObject self(cx, &as<NativeObject>());
+    MOZ_ASSERT(self->lastProperty()->getObjectFlags() & flag);
 
     StackBaseShape base(self->lastProperty());
     base.flags &= ~flag;
     UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return false;
 
     self->lastProperty()->base()->adoptUnowned(nbase);
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -829,19 +829,20 @@ class Shape : public gc::TenuredCell
     static Shape *setObjectParent(ExclusiveContext *cx,
                                   JSObject *obj, TaggedProto proto, Shape *last);
     static Shape *setObjectMetadata(JSContext *cx,
                                     JSObject *metadata, TaggedProto proto, Shape *last);
     static Shape *setObjectFlags(ExclusiveContext *cx,
                                  BaseShape::Flag flag, TaggedProto proto, Shape *last);
 
     uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
-    bool hasObjectFlag(BaseShape::Flag flag) const {
-        MOZ_ASSERT(!(flag & ~BaseShape::OBJECT_FLAG_MASK));
-        return !!(base()->flags & flag);
+    bool hasAllObjectFlags(BaseShape::Flag flags) const {
+        MOZ_ASSERT(flags);
+        MOZ_ASSERT(!(flags & ~BaseShape::OBJECT_FLAG_MASK));
+        return (base()->flags & flags) == flags;
     }
 
   protected:
     /*
      * Implementation-private bits stored in shape->flags. See public: enum {}
      * flags further below, which were allocated FCFS over time, so interleave
      * with these bits.
      */
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2571,17 +2571,17 @@ ObjectGroup::matchDefiniteProperties(Han
     for (unsigned i = 0; i < count; i++) {
         Property *prop = getProperty(i);
         if (!prop)
             continue;
         if (prop->types.definiteProperty()) {
             unsigned slot = prop->types.definiteSlot();
 
             bool found = false;
-            Shape *shape = obj->lastProperty();
+            Shape *shape = obj->as<NativeObject>().lastProperty();
             while (!shape->isEmptyShape()) {
                 if (shape->slot() == slot && shape->propid() == prop->id) {
                     found = true;
                     break;
                 }
                 shape = shape->previous();
             }
             if (!found)
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -312,26 +312,26 @@ RootList::init(HandleObject debuggees)
 {
     MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(ObjectValue(*debuggees)));
     js::Debugger *dbg = js::Debugger::fromJSObject(debuggees);
 
     ZoneSet debuggeeZones;
     if (!debuggeeZones.init())
         return false;
 
-    for (js::GlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
+    for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
         if (!debuggeeZones.put(r.front()->zone()))
             return false;
     }
 
     if (!init(debuggeeZones))
         return false;
 
     // Ensure that each of our debuggee globals are in the root list.
-    for (js::GlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
+    for (js::WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront()) {
         if (!addRoot(JS::ubi::Node(static_cast<JSObject *>(r.front())),
                      MOZ_UTF16("debuggee global")))
         {
             return false;
         }
     }
 
     return true;
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -283,54 +283,40 @@ UnboxedPlainObject::convertToNative(JSCo
     }
 
     AutoValueVector values(cx);
     for (size_t i = 0; i < layout.properties().length(); i++) {
         if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i])))
             return false;
     }
 
-    uint32_t objectFlags = obj->lastProperty()->getObjectFlags();
-    RootedObject metadata(cx, obj->getMetadata());
-
     obj->setGroup(layout.nativeGroup());
     obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
 
     for (size_t i = 0; i < values.length(); i++)
         obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
 
-    if (objectFlags) {
-        RootedObject objRoot(cx, obj);
-        if (!obj->setFlags(cx, objectFlags))
-            return false;
-        obj = objRoot;
-    }
-
-    if (metadata) {
-        RootedObject objRoot(cx, obj);
-        RootedObject metadataRoot(cx, metadata);
-        if (!setMetadata(cx, objRoot, metadataRoot))
-            return false;
-    }
-
     return true;
 }
 
 /* static */
 UnboxedPlainObject *
 UnboxedPlainObject::create(JSContext *cx, HandleObjectGroup group, NewObjectKind newKind)
 {
     MOZ_ASSERT(group->clasp() == &class_);
     gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
 
     UnboxedPlainObject *res = NewObjectWithGroup<UnboxedPlainObject>(cx, group, cx->global(),
                                                                      allocKind, newKind);
     if (!res)
         return nullptr;
 
+    // Avoid spurious shape guard hits.
+    res->dummy_ = nullptr;
+
     // Initialize reference fields of the object. All fields in the object will
     // be overwritten shortly, but references need to be safe for the GC.
     const int32_t *list = res->layout().traceList();
     if (list) {
         uint8_t *data = res->data();
         while (*list != -1) {
             HeapPtrString *heap = reinterpret_cast<HeapPtrString *>(data + *list);
             heap->init(cx->names().empty);
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -146,17 +146,20 @@ class UnboxedLayout : public mozilla::Li
 };
 
 // Class for a plain object using an unboxed representation. The physical
 // layout of these objects is identical to that of an InlineTypedObject, though
 // these objects use an UnboxedLayout instead of a TypeDescr to keep track of
 // how their properties are stored.
 class UnboxedPlainObject : public JSObject
 {
-    // Start of the inline data, which immediately follows the shape and type.
+    // Placeholder for extra properties. See bug 1137180.
+    void *dummy_;
+
+    // Start of the inline data, which immediately follows the group and extra properties.
     uint8_t data_[1];
 
   public:
     static const Class class_;
 
     static bool obj_lookupProperty(JSContext *cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
                                    MutableHandleShape propp);
--- a/layout/base/tests/marionette/test_selectioncarets.py
+++ b/layout/base/tests/marionette/test_selectioncarets.py
@@ -1,16 +1,16 @@
 # -*- coding: utf-8 -*-
 # 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/.
 
 from marionette_driver.by import By
 from marionette_driver.marionette import Actions
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.selection import SelectionManager
 from marionette_driver.gestures import long_press_without_contextmenu
 
 from math import ceil, floor
 
 
 class SelectionCaretsTest(MarionetteTestCase):
     _long_press_time = 1        # 1 second
--- a/layout/base/tests/marionette/test_selectioncarets_multiplerange.py
+++ b/layout/base/tests/marionette/test_selectioncarets_multiplerange.py
@@ -1,16 +1,16 @@
 # -*- coding: utf-8 -*-
 # 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/.
 
 from marionette_driver.by import By
 from marionette_driver.marionette import Actions
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.selection import SelectionManager
 from marionette_driver.gestures import long_press_without_contextmenu
 
 
 class SelectionCaretsMultipleRangeTest(MarionetteTestCase):
     _long_press_time = 1        # 1 second
 
     def setUp(self):
--- a/layout/base/tests/marionette/test_touchcaret.py
+++ b/layout/base/tests/marionette/test_touchcaret.py
@@ -1,17 +1,17 @@
 # -*- coding: utf-8 -*-
 # 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/.
 
 from marionette_driver.by import By
 from marionette_driver.marionette import Actions
 from marionette_driver.selection import SelectionManager
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TouchCaretTest(MarionetteTestCase):
     _input_selector = (By.ID, 'input')
     _textarea_selector = (By.ID, 'textarea')
     _contenteditable_selector = (By.ID, 'contenteditable')
     _large_expiration_time = 3000 * 20  # 60 seconds
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -75,16 +75,31 @@ BuildScrollContainerLayers()
     Preferences::AddBoolVarCache(&sContainerlessScrollingEnabled,
                                  "layout.async-containerless-scrolling.enabled",
                                  true);
   }
 
   return !sContainerlessScrollingEnabled;
 }
 
+static uint32_t
+GetOverflowChange(const nsRect& aCurScrolledRect, const nsRect& aPrevScrolledRect)
+{
+  uint32_t result = 0;
+  if (aPrevScrolledRect.x != aCurScrolledRect.x ||
+      aPrevScrolledRect.width != aCurScrolledRect.width) {
+    result |= nsIScrollableFrame::HORIZONTAL;
+  }
+  if (aPrevScrolledRect.y != aCurScrolledRect.y ||
+      aPrevScrolledRect.height != aCurScrolledRect.height) {
+    result |= nsIScrollableFrame::VERTICAL;
+  }
+  return result;
+}
+
 //----------------------------------------------------------------------
 
 //----------nsHTMLScrollFrame-------------------------------------------
 
 nsHTMLScrollFrame*
 NS_NewHTMLScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
 {
   return new (aPresShell) nsHTMLScrollFrame(aContext, aIsRoot);
@@ -909,16 +924,18 @@ nsHTMLScrollFrame::Reflow(nsPresContext*
   if (!InInitialReflow() && !mHelper.mHadNonInitialReflow) {
     mHelper.mHadNonInitialReflow = true;
   }
 
   if (mHelper.mIsRoot && !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
     mHelper.PostScrolledAreaEvent();
   }
 
+  mHelper.UpdatePrevScrolledRect();
+
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   mHelper.PostOverflowEvent();
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -4520,16 +4537,18 @@ nsXULScrollFrame::Layout(nsBoxLayoutStat
     clippedRect.MoveBy(mHelper.mScrollPort.TopLeft());
     clippedRect.IntersectRect(clippedRect, mHelper.mScrollPort);
     nsOverflowAreas overflow = f->GetOverflowAreas();
     f->FinishAndStoreOverflow(overflow, clippedRect.Size());
     clippedRect.MoveTo(origRect.TopLeft());
     f->SetRect(clippedRect);
   }
 
+  mHelper.UpdatePrevScrolledRect();
+
   mHelper.PostOverflowEvent();
   return NS_OK;
 }
 
 void
 ScrollFrameHelper::FinishReflowForScrollbar(nsIContent* aContent,
                                                 nscoord aMinXY, nscoord aMaxXY,
                                                 nscoord aCurPosXY,
@@ -4663,19 +4682,37 @@ ScrollFrameHelper::ReflowCallbackCancele
 }
 
 bool
 ScrollFrameHelper::UpdateOverflow()
 {
   nsIScrollableFrame* sf = do_QueryFrame(mOuter);
   ScrollbarStyles ss = sf->GetScrollbarStyles();
 
-  if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN ||
-      ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN ||
-      GetScrollPosition() != nsPoint()) {
+  // Reflow when the change in overflow leads to one of our scrollbars
+  // changing or might require repositioning the scrolled content due to
+  // reduced extents.
+  nsRect scrolledRect = GetScrolledRect();
+  uint32_t overflowChange = GetOverflowChange(scrolledRect, mPrevScrolledRect);
+  mPrevScrolledRect = scrolledRect;
+
+  bool needReflow = false;
+  nsPoint scrollPosition = GetScrollPosition();
+  if (overflowChange & nsIScrollableFrame::HORIZONTAL) {
+    if (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN || scrollPosition.x) {
+      needReflow = true;
+    }
+  }
+  if (overflowChange & nsIScrollableFrame::VERTICAL) {
+    if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN || scrollPosition.y) {
+      needReflow = true;
+    }
+  }
+
+  if (needReflow) {
     // If there are scrollbars, or we're not at the beginning of the pane,
     // the scroll position may change. In this case, mark the frame as
     // needing reflow. Don't use NS_FRAME_IS_DIRTY as dirty as that means
     // we have to reflow the frame and all its descendants, and we don't
     // have to do that here. Only this frame needs to be reflowed.
     mOuter->PresContext()->PresShell()->FrameNeedsReflow(
       mOuter, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
     // Ensure that next time nsHTMLScrollFrame::Reflow runs, we don't skip
@@ -4695,16 +4732,22 @@ ScrollFrameHelper::UpdateSticky()
     GetStickyScrollContainerForScrollFrame(mOuter);
   if (ssc) {
     nsIScrollableFrame* scrollFrame = do_QueryFrame(mOuter);
     ssc->UpdatePositions(scrollFrame->GetScrollPosition(), mOuter);
   }
 }
 
 void
+ScrollFrameHelper::UpdatePrevScrolledRect()
+{
+  mPrevScrolledRect = GetScrolledRect();
+}
+
+void
 ScrollFrameHelper::AdjustScrollbarRectForResizer(
                          nsIFrame* aFrame, nsPresContext* aPresContext,
                          nsRect& aRect, bool aHasResizer, bool aVertical)
 {
   if ((aVertical ? aRect.width : aRect.height) == 0)
     return;
 
   // if a content resizer is present, use its size. Otherwise, check if the
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -295,16 +295,18 @@ public:
   {
     mScrollPosForLayerPixelAlignment = GetScrollPosition();
   }
 
   bool UpdateOverflow();
 
   void UpdateSticky();
 
+  void UpdatePrevScrolledRect();
+
   bool IsRectNearlyVisible(const nsRect& aRect) const;
   nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const;
 
   // adjust the scrollbar rectangle aRect to account for any visible resizer.
   // aHasResizer specifies if there is a content resizer, however this method
   // will also check if a widget resizer is present as well.
   void AdjustScrollbarRectForResizer(nsIFrame* aFrame, nsPresContext* aPresContext,
                                      nsRect& aRect, bool aHasResizer, bool aVertical);
@@ -417,16 +419,18 @@ public:
   nsExpirationState mActivityExpirationState;
 
   nsCOMPtr<nsITimer> mScrollActivityTimer;
   nsPoint mScrollPosForLayerPixelAlignment;
 
   // The scroll position where we last updated image visibility.
   nsPoint mLastUpdateImagesPos;
 
+  nsRect mPrevScrolledRect;
+
   FrameMetrics::ViewID mScrollParentID;
 
   bool mNeverHasVerticalScrollbar:1;
   bool mNeverHasHorizontalScrollbar:1;
   bool mHasVerticalScrollbar:1;
   bool mHasHorizontalScrollbar:1;
   bool mFrameIsUpdatingScrollbar:1;
   bool mDidHistoryRestore:1;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/scrolling/move-item-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      #a_box {
+        overflow:auto;
+        width:100px;
+        height:100px;
+        border:1px solid black;
+      }
+      #item {
+        transform: translate(80px, 80px);
+        width:40px;
+        height:40px;
+        position:relative;
+        background-color:#666;
+      }
+    </style>
+</head>
+<body>
+  <div id="a_box">
+    <div id="item"></div>
+  </div>
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/scrolling/move-item.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      #a_box {
+        overflow:auto;
+        width:100px;
+        height:100px;
+        border:1px solid black;
+      }
+      #item {
+        width:40px;
+        height:40px;
+        position:relative;
+        background-color:#666;
+      }
+    </style>
+</head>
+<body onload="runTest();">
+  <div id="a_box">
+    <div id="item"></div>
+  </div>
+<script>
+  function runTest() {
+    document.getElementById("item").style.transform = "translate(80px, 80px)";
+    document.documentElement.removeAttribute("class");
+  }
+</script>
+</body>
+</html>
--- a/layout/reftests/scrolling/reftest.list
+++ b/layout/reftests/scrolling/reftest.list
@@ -28,8 +28,9 @@ fuzzy-if(Android,5,20000) == uncovering-
 fuzzy-if(Android,5,20000) == uncovering-2.html uncovering-2-ref.html
 skip-if(B2G) == less-than-scrollbar-height.html less-than-scrollbar-height-ref.html
 skip-if(B2G) == huge-horizontal-overflow.html huge-horizontal-overflow-ref.html
 skip-if(B2G) == huge-vertical-overflow.html huge-vertical-overflow-ref.html
 == iframe-scrolling-attr-1.html iframe-scrolling-attr-ref.html
 skip-if(B2G&&browserIsRemote) == iframe-scrolling-attr-2.html iframe-scrolling-attr-ref.html
 == frame-scrolling-attr-1.html frame-scrolling-attr-ref.html
 == frame-scrolling-attr-2.html frame-scrolling-attr-ref.html
+== move-item.html move-item-ref.html # bug 1125750
--- a/layout/tools/reftest/b2g_desktop.py
+++ b/layout/tools/reftest/b2g_desktop.py
@@ -36,17 +36,17 @@ class B2GDesktopReftest(RefTest):
         self.test_script = os.path.join(here, 'b2g_start_script.js')
         self.timeout = None
 
     def run_marionette_script(self):
         self.marionette = Marionette(**self.marionette_args)
         assert(self.marionette.wait_for_port())
         self.marionette.start_session()
         if self.build_type == "mulet":
-            self._wait_for_homescreen(timeout=15)
+            self._wait_for_homescreen(timeout=300)
             self._unlockScreen()
         self.marionette.set_context(self.marionette.CONTEXT_CHROME)
 
         if os.path.isfile(self.test_script):
             f = open(self.test_script, 'r')
             self.test_script = f.read()
             f.close()
         self.marionette.execute_script(self.test_script)
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -55,16 +55,17 @@ const FOCUS_FILTER_NON_NEEDS_FOCUS_TESTS
 var gFocusFilterMode = FOCUS_FILTER_ALL_TESTS;
 
 // "<!--CLEAR-->"
 const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
 
 var gBrowser;
 // Are we testing web content loaded in a separate process?
 var gBrowserIsRemote;           // bool
+var gB2GisMulet;                // bool
 // Are we using <iframe mozbrowser>?
 var gBrowserIsIframe;           // bool
 var gBrowserMessageManager;
 var gCanvas1, gCanvas2;
 // gCurrentCanvas is non-null between InitCurrentCanvasWithSnapshot and the next
 // RecordResult.
 var gCurrentCanvas = null;
 var gURLs;
@@ -244,16 +245,22 @@ this.OnRefTestLoad = function OnRefTestL
                 getService(Components.interfaces.nsIPrefBranch);
     try {
         gBrowserIsRemote = prefs.getBoolPref("browser.tabs.remote.autostart");
     } catch (e) {
         gBrowserIsRemote = false;
     }
 
     try {
+        gB2GisMulet = prefs.getBoolPref("b2g.is_mulet");
+    } catch (e) {
+        gB2GisMulet = false;
+    }
+
+    try {
       gBrowserIsIframe = prefs.getBoolPref("reftest.browser.iframe.enabled");
     } catch (e) {
       gBrowserIsIframe = false;
     }
 
     if (win === undefined || win == null) {
       win = window;
     }
@@ -725,16 +732,17 @@ function BuildConditionSandbox(aURL) {
         }
 
         return testPluginIsOOP;
     };
 
     // Tests shouldn't care about this except for when they need to
     // crash the content process
     sandbox.browserIsRemote = gBrowserIsRemote;
+    sandbox.Mulet = gB2GisMulet;
 
     try {
         sandbox.asyncPanZoom = prefs.getBoolPref("layers.async-pan-zoom.enabled");
     } catch (e) {
         sandbox.asyncPanZoom = false;
     }
 
     if (!gDumpedConditionSandbox) {
--- a/media/gmp-clearkey/0.1/AudioDecoder.cpp
+++ b/media/gmp-clearkey/0.1/AudioDecoder.cpp
@@ -224,31 +224,29 @@ AudioDecoder::Reset()
 {
   mDecoder->Reset();
   mCallback->ResetComplete();
 }
 
 void
 AudioDecoder::DrainTask()
 {
-  if (FAILED(mDecoder->Drain())) {
-    GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
-  }
+  mDecoder->Drain();
 
   // Return any pending output.
   HRESULT hr = S_OK;
   while (hr == S_OK) {
     CComPtr<IMFSample> output;
     hr = mDecoder->Output(&output);
     SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
     if (hr == S_OK) {
       ReturnOutput(output);
     }
   }
-  GetPlatform()->syncrunonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
+  GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
 }
 
 void
 AudioDecoder::Drain()
 {
   EnsureWorker();
   mWorkerThread->Post(WrapTask(this,
                                &AudioDecoder::DrainTask));
--- a/media/mtransport/test/transport_unittests.cpp
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -963,23 +963,23 @@ TEST_F(TransportTest, TestConnectTwoDige
   SetDtlsPeer(2, 0);
 
   ConnectSocket();
 }
 
 TEST_F(TransportTest, TestConnectTwoDigestsFirstBad) {
   SetDtlsPeer(2, 1);
 
-  ConnectSocketExpectFail();
+  ConnectSocket();
 }
 
 TEST_F(TransportTest, TestConnectTwoDigestsSecondBad) {
   SetDtlsPeer(2, 2);
 
-  ConnectSocketExpectFail();
+  ConnectSocket();
 }
 
 TEST_F(TransportTest, TestConnectTwoDigestsBothBad) {
   SetDtlsPeer(2, 3);
 
   ConnectSocketExpectFail();
 }
 
--- a/media/mtransport/transportlayerdtls.cpp
+++ b/media/mtransport/transportlayerdtls.cpp
@@ -1040,25 +1040,22 @@ SECStatus TransportLayerDtls::AuthCertif
         // Check all the provided digests
 
         // Checking functions call PR_SetError()
         SECStatus rv = SECFailure;
         for (size_t i = 0; i < digests_.size(); i++) {
           RefPtr<VerificationDigest> digest = digests_[i];
           rv = CheckDigest(digest, peer_cert);
 
-          if (rv != SECSuccess)
-            break;
-        }
-
-        if (rv == SECSuccess) {
-          // Matches all digests, we are good to go
-          cert_ok_ = true;
-          peer_cert = peer_cert.forget();
-          return SECSuccess;
+          // Matches a digest, we are good to go
+          if (rv == SECSuccess) {
+            cert_ok_ = true;
+            peer_cert = peer_cert.forget();
+            return SECSuccess;
+          }
         }
       }
       break;
     default:
       MOZ_CRASH();  // Can't happen
   }
 
   return SECFailure;
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -83,8 +83,18 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
     ]
 
 DEFINES['IMPL_MFBT'] = True
 
 LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS']
 
 if not CONFIG['_MSC_VER']:
     FAIL_ON_WARNINGS = True
+
+if CONFIG['OS_TARGET'] == 'Darwin':
+    # On OSX 10.10.3, a dead lock happens in some cases involving dynamic
+    # symbol resolution for symbols that jemalloc itself uses. While it
+    # might be possible to find a way to avoid all such symbol resolutions,
+    # it's currently not possible because at the very least there's a call
+    # to pthread_self from tsd_init_check_recursion, which is necessary
+    # because somehow clang doesn't want to accept the __thread keyword
+    # for TLS.
+    LDFLAGS += ['-Wl,-bind_at_load']
--- a/netwerk/test/unit/test_udp_multicast.js
+++ b/netwerk/test/unit/test_udp_multicast.js
@@ -2,17 +2,21 @@
 
 const { Constructor: CC } = Components;
 
 const UDPSocket = CC("@mozilla.org/network/udp-socket;1",
                      "nsIUDPSocket",
                      "init");
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
-const ADDRESS = "224.0.0.255";
+const ADDRESS_TEST1 = "224.0.0.200";
+const ADDRESS_TEST2 = "224.0.0.201";
+const ADDRESS_TEST3 = "224.0.0.202";
+const ADDRESS_TEST4 = "224.0.0.203";
+
 const TIMEOUT = 2000;
 
 const ua = Cc["@mozilla.org/network/protocol;1?name=http"]
            .getService(Ci.nsIHttpProtocolHandler).userAgent;
 const isWinXP = ua.indexOf("Windows NT 5.1") != -1;
 
 let gConverter;
 
@@ -22,87 +26,87 @@ function run_test() {
 }
 
 function setup() {
   gConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                createInstance(Ci.nsIScriptableUnicodeConverter);
   gConverter.charset = "utf8";
 }
 
-function createSocketAndJoin() {
+function createSocketAndJoin(addr) {
   let socket = new UDPSocket(-1, false);
-  socket.joinMulticast(ADDRESS);
+  socket.joinMulticast(addr);
   return socket;
 }
 
-function sendPing(socket) {
+function sendPing(socket, addr) {
   let ping = "ping";
   let rawPing = gConverter.convertToByteArray(ping);
 
   let deferred = promise.defer();
 
   socket.asyncListen({
     onPacketReceived: function(s, message) {
       do_print("Received on port " + socket.port);
       do_check_eq(message.data, ping);
       socket.close();
       deferred.resolve(message.data);
     },
     onStopListening: function(socket, status) {}
   });
 
   do_print("Multicast send to port " + socket.port);
-  socket.send(ADDRESS, socket.port, rawPing, rawPing.length);
+  socket.send(addr, socket.port, rawPing, rawPing.length);
 
   // Timers are bad, but it seems like the only way to test *not* getting a
   // packet.
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback(() => {
     socket.close();
     deferred.reject();
   }, TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
 
   return deferred.promise;
 }
 
 add_test(() => {
   do_print("Joining multicast group");
-  let socket = createSocketAndJoin();
-  sendPing(socket).then(
+  let socket = createSocketAndJoin(ADDRESS_TEST1);
+  sendPing(socket, ADDRESS_TEST1).then(
     run_next_test,
     () => do_throw("Joined group, but no packet received")
   );
 });
 
 add_test(() => {
   do_print("Disabling multicast loopback");
-  let socket = createSocketAndJoin();
+  let socket = createSocketAndJoin(ADDRESS_TEST2);
   socket.multicastLoopback = false;
-  sendPing(socket).then(
+  sendPing(socket, ADDRESS_TEST2).then(
     () => do_throw("Loopback disabled, but still got a packet"),
     run_next_test
   );
 });
 
 // The following multicast interface test doesn't work on Windows XP, as it
 // appears to allow packets no matter what address is given, so we'll skip the
 // test there.
 if (!isWinXP) {
   add_test(() => {
     do_print("Changing multicast interface");
-    let socket = createSocketAndJoin();
+    let socket = createSocketAndJoin(ADDRESS_TEST3);
     socket.multicastInterface = "127.0.0.1";
-    sendPing(socket).then(
+    sendPing(socket, ADDRESS_TEST3).then(
       () => do_throw("Changed interface, but still got a packet"),
       run_next_test
     );
   });
-}
 
 add_test(() => {
   do_print("Leaving multicast group");
-  let socket = createSocketAndJoin();
-  socket.leaveMulticast(ADDRESS);
-  sendPing(socket).then(
+  let socket = createSocketAndJoin(ADDRESS_TEST4);
+  socket.leaveMulticast(ADDRESS_TEST4);
+  sendPing(socket, ADDRESS_TEST4).then(
     () => do_throw("Left group, but still got a packet"),
     run_next_test
   );
 });
+}
--- a/testing/config/mozharness/linux_mulet_config.py
+++ b/testing/config/mozharness/linux_mulet_config.py
@@ -1,11 +1,16 @@
 # This is used by mozharness' mulet_unittest.py
 config = {
     # testsuite options
     "reftest_options": [
-        "--mulet", "--profile=%(gaia_profile)s",
-        "--appname=%(application)s", "%(test_manifest)s"
+        "--mulet",
+        "--profile=%(gaia_profile)s",
+        "--appname=%(application)s",
+        "--total-chunks=%(total_chunks)s",
+        "--this-chunk=%(this_chunk)s",
+        "--enable-oop",
+        "%(test_manifest)s"
     ],
     "run_file_names": {
         "reftest": "runreftestb2g.py",
     },
 }
--- a/testing/marionette/client/marionette/tests/unit/test_accessibility.py
+++ b/testing/marionette/client/marionette/tests/unit/test_accessibility.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import (ElementNotAccessibleException,
                                      ElementNotVisibleException)
 
 
 class TestAccessibility(MarionetteTestCase):
 
     # Elements that are accessible with and without the accessibliity API
     valid_elementIDs = [
--- a/testing/marionette/client/marionette/tests/unit/test_anonymous_content.py
+++ b/testing/marionette/client/marionette/tests/unit/test_anonymous_content.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 from marionette_driver.errors import NoSuchElementException
 from marionette_driver.expected import element_present
 from marionette_driver.marionette import HTMLElement
 from marionette_driver.wait import Wait
 
 class TestAnonymousContent(MarionetteTestCase):
     def setUp(self):
--- a/testing/marionette/client/marionette/tests/unit/test_appcache.py
+++ b/testing/marionette/client/marionette/tests/unit/test_appcache.py
@@ -9,17 +9,17 @@
 #
 #Unless required by applicable law or agreed to in writing, software
 #distributed under the License is distributed on an "AS IS" BASIS,
 #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #See the License for the specific language governing permissions and
 #limitations under the License.
 
 from marionette_driver.application_cache import ApplicationCache
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class AppCacheTests(MarionetteTestCase):
 
     def testWeCanGetTheStatusOfTheAppCache(self):
         test_url = self.marionette.absolute_url('html5Page')
         self.marionette.navigate(test_url)
         app_cache = self.marionette.application_cache
--- a/testing/marionette/client/marionette/tests/unit/test_capabilities.py
+++ b/testing/marionette/client/marionette/tests/unit/test_capabilities.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import SessionNotCreatedException
 
 class TestCapabilities(MarionetteTestCase):
     def setUp(self):
         super(TestCapabilities, self).setUp()
         self.caps = self.marionette.session_capabilities
         self.marionette.set_context("chrome")
         self.appinfo = self.marionette.execute_script(
@@ -75,9 +75,9 @@ class TestCapabilities(MarionetteTestCas
             self.marionette.start_session(capabilities)
             self.fail("Marionette Should have throw an exception")
         except SessionNotCreatedException as e:
             # We want an exception
             self.assertIn("CookiesAndCream does not equal", str(e))
 
         # Start a new session just to make sure we leave the browser in the
         # same state it was before it started the test
-        self.marionette.start_session()
\ No newline at end of file
+        self.marionette.start_session()
--- a/testing/marionette/client/marionette/tests/unit/test_clearing.py
+++ b/testing/marionette/client/marionette/tests/unit/test_clearing.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import InvalidElementStateException
 
 class TestClear(MarionetteTestCase):
     def testWriteableTextInputShouldClear(self):
         test_html = self.marionette.absolute_url("test_clearing.html")
         self.marionette.navigate(test_html)
         element = self.marionette.find_element("id", "writableTextInput")
         element.clear()
--- a/testing/marionette/client/marionette/tests/unit/test_click.py
+++ b/testing/marionette/client/marionette/tests/unit/test_click.py
@@ -1,15 +1,15 @@
 # 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/.
 
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException, ElementNotVisibleException
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.marionette import Actions
 from marionette_driver.keys import Keys
 from marionette_driver.wait import Wait
 
 
 class TestClick(MarionetteTestCase):
     def test_click(self):
         test_html = self.marionette.absolute_url("test.html")
--- a/testing/marionette/client/marionette/tests/unit/test_click_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_click_chrome.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.by import By
 
 
 class TestClickChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
--- a/testing/marionette/client/marionette/tests/unit/test_click_scrolling.py
+++ b/testing/marionette/client/marionette/tests/unit/test_click_scrolling.py
@@ -1,15 +1,15 @@
 # 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/.
 
 from marionette_driver.by import By
 from marionette_driver.errors import MoveTargetOutOfBoundsException
-from marionette_test import MarionetteTestCase, skip
+from marionette import MarionetteTestCase, skip
 
 
 class TestClickScrolling(MarionetteTestCase):
 
     def test_clicking_on_anchor_scrolls_page(self):
         scrollScript = """
             var pageY;
             if (typeof(window.pageYOffset) == 'number') {
--- a/testing/marionette/client/marionette/tests/unit/test_cookies.py
+++ b/testing/marionette/client/marionette/tests/unit/test_cookies.py
@@ -1,12 +1,12 @@
 import calendar
 import time
 import random
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class CookieTest(MarionetteTestCase):
 
     def setUp(self):
         MarionetteTestCase.setUp(self)
         test_url = self.marionette.absolute_url('test.html')
         self.marionette.navigate(test_url)
--- a/testing/marionette/client/marionette/tests/unit/test_data_driven.py
+++ b/testing/marionette/client/marionette/tests/unit/test_data_driven.py
@@ -1,10 +1,18 @@
-from marionette_test import parameterized, with_parameters, MetaParameterized, \
-                            MarionetteTestCase
+# 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/.
+
+from marionette.marionette_test import (
+    parameterized,
+    with_parameters,
+    MetaParameterized,
+    MarionetteTestCase
+)
 
 class Parameterizable(object):
     __metaclass__ = MetaParameterized
 
 class TestDataDriven(MarionetteTestCase):
     def test_parameterized(self):
         class Test(Parameterizable):
             def __init__(self):
--- a/testing/marionette/client/marionette/tests/unit/test_date_time_value.py
+++ b/testing/marionette/client/marionette/tests/unit/test_date_time_value.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from datetime import datetime
 from marionette_driver.date_time_value import DateTimeValue
 
 class TestDateTime(MarionetteTestCase):
 
     def test_set_date(self):
         test_html = self.marionette.absolute_url("datetimePage.html")
         self.marionette.navigate(test_html)
--- a/testing/marionette/client/marionette/tests/unit/test_elementState.py
+++ b/testing/marionette/client/marionette/tests/unit/test_elementState.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestState(MarionetteTestCase):
     def test_isEnabled(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         l = self.marionette.find_element("name", "myCheckBox")
         self.assertTrue(l.is_enabled())
--- a/testing/marionette/client/marionette/tests/unit/test_elementState_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_elementState_chrome.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestStateChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
--- a/testing/marionette/client/marionette/tests/unit/test_element_touch.py
+++ b/testing/marionette/client/marionette/tests/unit/test_element_touch.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import MarionetteException
 
 class testElementTouch(MarionetteTestCase):
     def test_touch(self):
       testAction = self.marionette.absolute_url("testAction.html")
       self.marionette.navigate(testAction)
       button = self.marionette.find_element("id", "button1")
       button.tap()
--- a/testing/marionette/client/marionette/tests/unit/test_elementsize.py
+++ b/testing/marionette/client/marionette/tests/unit/test_elementsize.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestElementSize(MarionetteTestCase):
     def testShouldReturnTheSizeOfALink(self):
         test_html = self.marionette.absolute_url("testSize.html")
         self.marionette.navigate(test_html)
         shrinko = self.marionette.find_element('id', 'linkId')
         size = shrinko.rect
         self.assertTrue(size['width'] > 0)
--- a/testing/marionette/client/marionette/tests/unit/test_emulator.py
+++ b/testing/marionette/client/marionette/tests/unit/test_emulator.py
@@ -1,15 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase
-from marionette_driver.errors import (JavascriptException,
-                                      MarionetteException)
+from marionette import MarionetteTestCase
+from marionette_driver.errors import MarionetteException
 
 
 class TestEmulatorContent(MarionetteTestCase):
 
     def test_emulator_cmd(self):
         self.marionette.set_script_timeout(10000)
         expected = ["<build>",
                     "OK"]
--- a/testing/marionette/client/marionette/tests/unit/test_errors.py
+++ b/testing/marionette/client/marionette/tests/unit/test_errors.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import sys
 
-import marionette_test
+from marionette import marionette_test
 from marionette_driver import errors
 from marionette_driver.errors import ErrorCodes
 
 def fake_cause():
     try:
         raise ValueError("bar")
     except ValueError as e:
         return sys.exc_info()
--- a/testing/marionette/client/marionette/tests/unit/test_execute_async_script.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_async_script.py
@@ -1,17 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import ( JavascriptException,
                                        MarionetteException,
                                        ScriptTimeoutException )
-import time
 
 
 class TestExecuteAsyncContent(MarionetteTestCase):
     def setUp(self):
         super(TestExecuteAsyncContent, self).setUp()
         self.marionette.set_script_timeout(1000)
 
     def test_execute_async_simple(self):
--- a/testing/marionette/client/marionette/tests/unit/test_execute_isolate.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_isolate.py
@@ -1,15 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase, skip_if_b2g
-from marionette_driver.errors import (JavascriptException,
-                                      MarionetteException,
+from marionette import MarionetteTestCase
+from marionette_driver.errors import (MarionetteException,
                                       ScriptTimeoutException)
 
 class TestExecuteIsolationContent(MarionetteTestCase):
     def setUp(self):
         super(TestExecuteIsolationContent, self).setUp()
         self.content = True
 
     def test_execute_async_isolate(self):
--- a/testing/marionette/client/marionette/tests/unit/test_execute_script.py
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_script.py
@@ -1,17 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import urllib
 
 from marionette_driver.by import By
-from marionette_driver.errors import JavascriptException, MarionetteException
-from marionette_test import MarionetteTestCase
+from marionette_driver.errors import JavascriptException
+from marionette import MarionetteTestCase
 
 def inline(doc):
     return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
 
 elements = inline("<p>foo</p> <p>bar</p>")
 
 class TestExecuteContent(MarionetteTestCase):
     def test_stack_trace(self):
--- a/testing/marionette/client/marionette/tests/unit/test_expected.py
+++ b/testing/marionette/client/marionette/tests/unit/test_expected.py
@@ -2,17 +2,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import urllib
 
 from marionette_driver import expected
 from marionette_driver.by import By
 
-import marionette_test
+from marionette import marionette_test
 
 
 def inline(doc):
     return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
 
 static_element = inline("""<p>foo</p>""")
 static_elements = static_element + static_element
 
--- a/testing/marionette/client/marionette/tests/unit/test_expectedfail.py
+++ b/testing/marionette/client/marionette/tests/unit/test_expectedfail.py
@@ -1,10 +1,10 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestFail(MarionetteTestCase):
     def test_fails(self):
         # this test is supposed to fail!
         self.assertEquals(True, False)
--- a/testing/marionette/client/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.marionette import HTMLElement
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException, InvalidSelectorException
 
 
 class TestElements(MarionetteTestCase):
     def test_id(self):
         test_html = self.marionette.absolute_url("test.html")
--- a/testing/marionette/client/marionette/tests/unit/test_findelement_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement_chrome.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.marionette import HTMLElement
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException
 
 
 class TestElementsChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
--- a/testing/marionette/client/marionette/tests/unit/test_gesture.py
+++ b/testing/marionette/client/marionette/tests/unit/test_gesture.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase
-from gestures import smooth_scroll, pinch
+from marionette import MarionetteTestCase
+from marionette.gestures import smooth_scroll
 
 class testGestures(MarionetteTestCase):
     check_in_viewport = """
         function elementInViewport(el) {
           let rect = el.getBoundingClientRect();
           return (rect.top >= window.pageYOffset &&
                  rect.left >= window.pageXOffset &&
                  rect.bottom <= (window.pageYOffset + window.innerHeight) &&
--- a/testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
+++ b/testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestGetActiveFrameOOP(MarionetteTestCase):
     def setUp(self):
         super(TestGetActiveFrameOOP, self).setUp()
         self.oop_by_default = self.marionette.execute_script("""
             try {
               return SpecialPowers.getBoolPref('dom.ipc.browser_frames.oop_by_default');
--- a/testing/marionette/client/marionette/tests/unit/test_getattr.py
+++ b/testing/marionette/client/marionette/tests/unit/test_getattr.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestGetAttribute(MarionetteTestCase):
     def test_getAttribute(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         l = self.marionette.find_element("id", "mozLink")
         self.assertEqual("mozLink", l.get_attribute("id"))
--- a/testing/marionette/client/marionette/tests/unit/test_getattr_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_getattr_chrome.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestGetAttributeChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
--- a/testing/marionette/client/marionette/tests/unit/test_implicit_waits.py
+++ b/testing/marionette/client/marionette/tests/unit/test_implicit_waits.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import NoSuchElementException
 
 class TestImplicitWaits(MarionetteTestCase):
     def testShouldImplicitlyWaitForASingleElement(self):
         test_html = self.marionette.absolute_url("test_dynamic.html")
         self.marionette.navigate(test_html)
         add = self.marionette.find_element("id", "adder")
         self.marionette.set_search_timeout("30000")
--- a/testing/marionette/client/marionette/tests/unit/test_import_script.py
+++ b/testing/marionette/client/marionette/tests/unit/test_import_script.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import JavascriptException
 
 class TestImportScript(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
 
     def clear_other_context(self):
         self.marionette.set_context("chrome")
--- a/testing/marionette/client/marionette/tests/unit/test_import_script_reuse_window.py
+++ b/testing/marionette/client/marionette/tests/unit/test_import_script_reuse_window.py
@@ -1,15 +1,15 @@
 
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestImportScriptContent(MarionetteTestCase):
 
     def test_importing_script_then_reusing_it(self):
         test_html = self.marionette.absolute_url("test_windows.html")
         self.marionette.navigate(test_html)
         js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
         self.current_window = self.marionette.current_window_handle
--- a/testing/marionette/client/marionette/tests/unit/test_key_actions.py
+++ b/testing/marionette/client/marionette/tests/unit/test_key_actions.py
@@ -1,15 +1,15 @@
 # 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/.
 
-from marionette_test import (MarionetteTestCase,
-                             skip_if_b2g,
-                             skip_if_e10s)
+from marionette.marionette_test import (MarionetteTestCase,
+                                        skip_if_b2g,
+                                        skip_if_e10s)
 from marionette_driver.keys import Keys
 from marionette_driver.marionette import Actions
 
 class TestKeyActions(MarionetteTestCase):
 
     def setUp(self):
         MarionetteTestCase.setUp(self)
         if self.marionette.session_capabilities['platformName'] == 'DARWIN':
--- a/testing/marionette/client/marionette/tests/unit/test_log.py
+++ b/testing/marionette/client/marionette/tests/unit/test_log.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-import os
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
+
 
 class TestLog(MarionetteTestCase):
     def test_log_basic(self):
         # clear any previous data
         self.marionette.get_logs()
 
         self.marionette.log("I am info")
         self.assertTrue("I am info" in self.marionette.get_logs()[0])
--- a/testing/marionette/client/marionette/tests/unit/test_marionette.py
+++ b/testing/marionette/client/marionette/tests/unit/test_marionette.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from marionette_driver import errors
-import marionette_test
+from marionette import marionette_test
 
 
 class TestHandleError(marionette_test.MarionetteTestCase):
     def test_malformed_packet(self):
         for t in [{}, {"error": None}]:
             with self.assertRaisesRegexp(errors.MarionetteException, "Malformed packet"):
                 self.marionette._handle_error(t)
 
--- a/testing/marionette/client/marionette/tests/unit/test_modal_dialogs.py
+++ b/testing/marionette/client/marionette/tests/unit/test_modal_dialogs.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase, skip_if_e10s
+from marionette.marionette_test import MarionetteTestCase, skip_if_e10s
 from marionette_driver.errors import NoAlertPresentException, ElementNotVisibleException
 from marionette_driver.marionette import Alert
 from marionette_driver.wait import Wait
 
 class TestTabModals(MarionetteTestCase):
 
     def setUp(self):
         MarionetteTestCase.setUp(self)
--- a/testing/marionette/client/marionette/tests/unit/test_multi_finger.py
+++ b/testing/marionette/client/marionette/tests/unit/test_multi_finger.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase
-from marionette import MultiActions, Actions
+from marionette import MarionetteTestCase
+from marionette.marionette import MultiActions, Actions
 
 class testMultiFinger(MarionetteTestCase):
     def test_move_element(self):
       testAction = self.marionette.absolute_url("testAction.html")
       self.marionette.navigate(testAction)
       start = self.marionette.find_element("id", "button1")
       drop = self.marionette.find_element("id", "button2")
       ele = self.marionette.find_element("id", "button3")
--- a/testing/marionette/client/marionette/tests/unit/test_navigation.py
+++ b/testing/marionette/client/marionette/tests/unit/test_navigation.py
@@ -1,13 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase, skip_if_b2g, skip_if_e10s
+from marionette.marionette_test import (
+    MarionetteTestCase,
+    skip_if_b2g,
+    skip_if_e10s
+)
 from marionette_driver.errors import MarionetteException, TimeoutException
 
 class TestNavigate(MarionetteTestCase):
     def test_navigate(self):
         self.assertTrue(self.marionette.execute_script("window.location.href = 'about:blank'; return true;"))
         self.assertEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
--- a/testing/marionette/client/marionette/tests/unit/test_pagesource.py
+++ b/testing/marionette/client/marionette/tests/unit/test_pagesource.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase, skip_if_b2g
+from marionette.marionette_test import MarionetteTestCase, skip_if_b2g
 
 class TestPageSource(MarionetteTestCase):
     def testShouldReturnTheSourceOfAPage(self):
         test_html = self.marionette.absolute_url("testPageSource.html")
         self.marionette.navigate(test_html)
         source = self.marionette.page_source
         self.assertTrue("<html" in source)
         self.assertTrue("PageSource" in source)
--- a/testing/marionette/client/marionette/tests/unit/test_position.py
+++ b/testing/marionette/client/marionette/tests/unit/test_position.py
@@ -1,9 +1,9 @@
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestPosition(MarionetteTestCase):
 
     def test_should_get_element_position_back(self):
         test_url = self.marionette.absolute_url('rectangles.html')
         self.marionette.navigate(test_url)
 
--- a/testing/marionette/client/marionette/tests/unit/test_profile_management.py
+++ b/testing/marionette/client/marionette/tests/unit/test_profile_management.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
 from marionette_driver.errors import JavascriptException
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestLog(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.enforce_gecko_prefs({"marionette.test.bool": True, "marionette.test.string": "testing", "marionette.test.int": 3})
 
     def test_preferences_are_set(self):
         bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.bool');")
--- a/testing/marionette/client/marionette/tests/unit/test_rendered_element.py
+++ b/testing/marionette/client/marionette/tests/unit/test_rendered_element.py
@@ -8,17 +8,17 @@
 #     http://www.apache.org/licenses/LICENSE-2.0
 #
 #Unless required by applicable law or agreed to in writing, software
 #distributed under the License is distributed on an "AS IS" BASIS,
 #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #See the License for the specific language governing permissions and
 #limitations under the License.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class RenderedElementTests(MarionetteTestCase):
 
     def testWeCanGetComputedStyleValueOnElement(self):
         test_url = self.marionette.absolute_url('javascriptPage.html')
         self.marionette.navigate(test_url)
         element = self.marionette.find_element('id', "green-parent")
--- a/testing/marionette/client/marionette/tests/unit/test_report.py
+++ b/testing/marionette/client/marionette/tests/unit/test_report.py
@@ -1,14 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-import unittest
-from marionette_test import MarionetteTestCase, expectedFailure, skip
+from marionette import MarionetteTestCase, expectedFailure, skip
 
 
 class TestReport(MarionetteTestCase):
 
     def test_pass(self):
         assert True
 
     def test_fail(self):
--- a/testing/marionette/client/marionette/tests/unit/test_run_js_test.py
+++ b/testing/marionette/client/marionette/tests/unit/test_run_js_test.py
@@ -1,10 +1,9 @@
 # Any copyright is dedicated to the Public Domain.
 # http://creativecommons.org/publicdomain/zero/1.0/
 
-from marionette_test import MarionetteTestCase
-import unittest
+from marionette import MarionetteTestCase
 
 class TestRunJSTest(MarionetteTestCase):
     def test_basic(self):
         self.run_js_test('test_simpletest_pass.js')
         self.run_js_test('test_simpletest_fail.js')
--- a/testing/marionette/client/marionette/tests/unit/test_screen_orientation.py
+++ b/testing/marionette/client/marionette/tests/unit/test_screen_orientation.py
@@ -1,16 +1,16 @@
 # -*- fill-column: 100; comment-column: 100; -*-
 
 # 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/.
 
 from marionette_driver.errors import MarionetteException
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from mozrunner.devices.emulator_screen import EmulatorScreen
 
 default_orientation = "portrait-primary"
 unknown_orientation = "Unknown screen orientation: %s"
 
 class TestScreenOrientation(MarionetteTestCase):
     def tearDown(self):
         self.marionette.set_orientation(default_orientation)
--- a/testing/marionette/client/marionette/tests/unit/test_screenshot.py
+++ b/testing/marionette/client/marionette/tests/unit/test_screenshot.py
@@ -1,12 +1,12 @@
 import base64
 import imghdr
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 RED_ELEMENT_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAVUlEQVRoge3PsQ0AIAzAsI78fzBwBhHykD2ePev80LweAAGJB1ILpBZILZBaILVAaoHUAqkFUgukFkgtkFogtUBqgdQCqQVSC6QWSC2QWiC1QGp9A7ma+7nyXgOpzQAAAABJRU5ErkJggg=='
 GREEN_ELEMENT_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAV0lEQVRoge3PQRGAQAwAsWINvXgsNnI3+4iAzM7sDWZn9vneoxXRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNHcF7nBD/Ha5Ye4BbsYAAAAAElFTkSuQmCC'
 
 class ScreenshotTests(MarionetteTestCase):
 
     def testWeCanTakeAScreenShotOfEntireViewport(self):
--- a/testing/marionette/client/marionette/tests/unit/test_selected.py
+++ b/testing/marionette/client/marionette/tests/unit/test_selected.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestSelected(MarionetteTestCase):
     def test_selected(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         box = self.marionette.find_element("name", "myCheckBox")
         self.assertFalse(box.is_selected())
--- a/testing/marionette/client/marionette/tests/unit/test_selected_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_selected_chrome.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestSelectedChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', '_blank', 'chrome,centerscreen');")
--- a/testing/marionette/client/marionette/tests/unit/test_session.py
+++ b/testing/marionette/client/marionette/tests/unit/test_session.py
@@ -1,15 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-import marionette_test
+from marionette import MarionetteTestCase
 
-class TestSession(marionette_test.MarionetteTestCase):
+class TestSession(MarionetteTestCase):
     def setUp(self):
         super(TestSession, self).setUp()
         self.marionette.delete_session()
 
     def test_new_session_returns_capabilities(self):
         # Sends newSession
         caps = self.marionette.start_session()
 
@@ -36,9 +36,9 @@ class TestSession(marionette_test.Marion
         self.assertTrue(self.marionette.session_id is not None)
         self.assertTrue(isinstance(self.marionette.session_id, unicode))
 
     def test_we_can_set_the_session_id(self):
         # Sends newSession
         caps = self.marionette.start_session(session_id="ILoveCheese")
 
         self.assertEqual(self.marionette.session_id, "ILoveCheese")
-        self.assertTrue(isinstance(self.marionette.session_id, unicode))
\ No newline at end of file
+        self.assertTrue(isinstance(self.marionette.session_id, unicode))
--- a/testing/marionette/client/marionette/tests/unit/test_set_window_size.py
+++ b/testing/marionette/client/marionette/tests/unit/test_set_window_size.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from marionette_driver.errors import MarionetteException
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestSetWindowSize(MarionetteTestCase):
     def setUp(self):
         super(MarionetteTestCase, self).setUp()
         self.start_size = self.marionette.window_size
         self.max_width = self.marionette.execute_script("return window.screen.availWidth;")
         self.max_height = self.marionette.execute_script("return window.screen.availHeight;")
 
--- a/testing/marionette/client/marionette/tests/unit/test_simpletest_sanity.py
+++ b/testing/marionette/client/marionette/tests/unit/test_simpletest_sanity.py
@@ -1,16 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
-from marionette_driver.errors import (JavascriptException,
-                                      MarionetteException,
-                                      ScriptTimeoutException)
+from marionette import MarionetteTestCase
 
 class SimpletestSanityTest(MarionetteTestCase):
 
     callFinish = "return finish();"
 
     def test_is(self):
         def runtests():
             sentFail1 = "is(true, false, 'isTest1', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
--- a/testing/marionette/client/marionette/tests/unit/test_single_finger.py
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.marionette import Actions
 from marionette_driver.errors import MarionetteException
 #add this directory to the path
 import os
 import sys
 sys.path.append(os.path.dirname(__file__))
 from single_finger_functions import (
         chain, chain_flick, context_menu, double_tap,
--- a/testing/marionette/client/marionette/tests/unit/test_single_finger_desktop.py
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger_desktop.py
@@ -1,10 +1,9 @@
-from marionette_test import MarionetteTestCase
-from marionette_driver.marionette import Actions
+from marionette import MarionetteTestCase
 from marionette_driver.errors import MarionetteException
 #add this directory to the path
 import os
 import sys
 sys.path.append(os.path.dirname(__file__))
 from single_finger_functions import (
         chain, chain_flick, context_menu, double_tap,
         long_press_action, long_press_on_xy_action,
--- a/testing/marionette/client/marionette/tests/unit/test_skip_setup.py
+++ b/testing/marionette/client/marionette/tests/unit/test_skip_setup.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase, SkipTest
+from marionette import MarionetteTestCase, SkipTest
 
 class TestSetUpSkipped(MarionetteTestCase):
 
     testVar = {'test':'SkipTest'}
 
     def setUp(self):
         try:
             self.testVar['email']
--- a/testing/marionette/client/marionette/tests/unit/test_specialpowers.py
+++ b/testing/marionette/client/marionette/tests/unit/test_specialpowers.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from marionette_test import MarionetteTestCase
-from marionette_driver.errors import JavascriptException, MarionetteException
+from marionette import MarionetteTestCase
+
 
 class TestSpecialPowersContent(MarionetteTestCase):
 
     testpref = "testing.marionette.contentcharpref"
     testvalue = "blabla"
 
     def test_prefs(self):
         result = self.marionette.execute_script("""
--- a/testing/marionette/client/marionette/tests/unit/test_submit.py
+++ b/testing/marionette/client/marionette/tests/unit/test_submit.py
@@ -2,17 +2,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import time
 
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException
 from marionette_driver.wait import Wait
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestSubmit(MarionetteTestCase):
 
     def test_should_be_able_to_submit_forms(self):
         test_html = self.marionette.absolute_url("formPage.html")
         self.marionette.navigate(test_html)
         self.marionette.find_element(By.NAME, "login").submit()
         for x in xrange(1, 10):
--- a/testing/marionette/client/marionette/tests/unit/test_switch_frame.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import (JavascriptException,
                                       NoSuchFrameException)
 
 
 class TestSwitchFrame(MarionetteTestCase):
     def test_switch_simple(self):
         start_url = "test_iframe.html"
         verify_title = "Marionette IFrame Test"
--- a/testing/marionette/client/marionette/tests/unit/test_switch_frame_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_frame_chrome.py
@@ -1,15 +1,15 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import JavascriptException
-from marionette_driver.by import By
+
 
 class TestSwitchFrameChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
         self.marionette.switch_to_window('foo')
--- a/testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TestSwitchRemoteFrame(MarionetteTestCase):
     def setUp(self):
         super(TestSwitchRemoteFrame, self).setUp()
         self.oop_by_default = self.marionette.execute_script("""
             try {
               return SpecialPowers.getBoolPref('dom.ipc.browser_frames.oop_by_default');
--- a/testing/marionette/client/marionette/tests/unit/test_text.py
+++ b/testing/marionette/client/marionette/tests/unit/test_text.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.keys import Keys
 
 
 class TestText(MarionetteTestCase):
     def test_getText(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         l = self.marionette.find_element("id", "mozLink")
--- a/testing/marionette/client/marionette/tests/unit/test_text_chrome.py
+++ b/testing/marionette/client/marionette/tests/unit/test_text_chrome.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 ''' Disabled in bug 896043 and when working on Chrome code re-enable for bug 896046
 class TestTextChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
--- a/testing/marionette/client/marionette/tests/unit/test_timeouts.py
+++ b/testing/marionette/client/marionette/tests/unit/test_timeouts.py
@@ -1,17 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-import os
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.marionette import HTMLElement
 from marionette_driver.errors import (NoSuchElementException,
-                                      JavascriptException,
                                       MarionetteException,
                                       ScriptTimeoutException)
 
 class TestTimeouts(MarionetteTestCase):
     def test_pagetimeout_notdefinetimeout_pass(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
 
--- a/testing/marionette/client/marionette/tests/unit/test_typing.py
+++ b/testing/marionette/client/marionette/tests/unit/test_typing.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase, skip_if_b2g
+from marionette.marionette_test import MarionetteTestCase, skip_if_b2g
 from marionette_driver.keys import Keys
 from marionette_driver.errors import ElementNotVisibleException
 
 
 class TestTyping(MarionetteTestCase):
 
     def testShouldFireKeyPressEvents(self):
         test_html = self.marionette.absolute_url("javascriptPage.html")
--- a/testing/marionette/client/marionette/tests/unit/test_visibility.py
+++ b/testing/marionette/client/marionette/tests/unit/test_visibility.py
@@ -1,9 +1,9 @@
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestVisibility(MarionetteTestCase):
 
     def testShouldAllowTheUserToTellIfAnElementIsDisplayedOrNot(self):
         test_html = self.marionette.absolute_url("javascriptPage.html")
         self.marionette.navigate(test_html)
 
         self.assertTrue(self.marionette.find_element('id', "displayed").is_displayed())
--- a/testing/marionette/client/marionette/tests/unit/test_wait.py
+++ b/testing/marionette/client/marionette/tests/unit/test_wait.py
@@ -4,17 +4,17 @@
 
 import sys
 import time
 
 from marionette_driver import errors
 from marionette_driver import wait
 from marionette_driver.wait import Wait
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 
 class TickingClock(object):
     def __init__(self, incr=1):
         self.ticks = 0
         self.increment = incr
 
     def sleep(self, dur):
--- a/testing/marionette/client/marionette/tests/unit/test_window_handles.py
+++ b/testing/marionette/client/marionette/tests/unit/test_window_handles.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase, skip_if_e10s
+from marionette.marionette_test import MarionetteTestCase, skip_if_e10s
 from marionette_driver.keys import Keys
 
 
 class TestWindowHandles(MarionetteTestCase):
 
     @skip_if_e10s # Interactions with about: pages need e10s support (bug 1096488).
     def test_new_tab_window_handles(self):
         keys = [Keys.SHIFT]
--- a/testing/marionette/client/marionette/tests/unit/test_window_management.py
+++ b/testing/marionette/client/marionette/tests/unit/test_window_management.py
@@ -1,14 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import time
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestSwitchWindow(MarionetteTestCase):
     def open_new_window(self):
         self.marionette.set_context("chrome")
         self.marionette.set_script_timeout(5000)
         self.marionette.execute_async_script("""
 var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                    .getService(Components.interfaces.nsIWindowWatcher);
--- a/testing/marionette/client/marionette/tests/unit/test_window_position.py
+++ b/testing/marionette/client/marionette/tests/unit/test_window_position.py
@@ -7,17 +7,17 @@
 #     http://www.apache.org/licenses/LICENSE-2.0
 #
 #Unless required by applicable law or agreed to in writing, software
 #distributed under the License is distributed on an "AS IS" BASIS,
 #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #See the License for the specific language governing permissions and
 #limitations under the License.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import MarionetteException
 
 class TestWindowPosition(MarionetteTestCase):
 
     def test_that_we_return_the_window_position(self):
         position = self.marionette.get_window_position()
         self.assertTrue(isinstance(position['x'], int))
         self.assertTrue(isinstance(position['y'], int))
--- a/testing/marionette/client/marionette/tests/unit/test_window_switching.py
+++ b/testing/marionette/client/marionette/tests/unit/test_window_switching.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException
 from marionette_driver.wait import Wait
 
 
 class TestWindowSwitching(MarionetteTestCase):
     def testJSWindowCreationAndSwitching(self):
--- a/testing/marionette/client/marionette/tests/unit/test_window_title.py
+++ b/testing/marionette/client/marionette/tests/unit/test_window_title.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 #TODO use @skip_if_b2g when Bug 875921 is fixed
 class TestTitleChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
--- a/testing/marionette/client/marionette/tests/unit/test_window_type.py
+++ b/testing/marionette/client/marionette/tests/unit/test_window_type.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 
 class TestWindowTypeChrome(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.set_context("chrome")
         self.win = self.marionette.current_window_handle
         self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
         self.marionette.switch_to_window('foo')
--- a/testing/marionette/client/marionette/tests/unit/test_with_using_context.py
+++ b/testing/marionette/client/marionette/tests/unit/test_with_using_context.py
@@ -1,13 +1,13 @@
 # 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/.
 
-from marionette_test import MarionetteTestCase
+from marionette import MarionetteTestCase
 from marionette_driver.errors import MarionetteException
 
 
 class TestSetContext(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
 
         # shortcuts to improve readability of these tests
--- a/testing/marionette/mach_commands.py
+++ b/testing/marionette/mach_commands.py
@@ -33,30 +33,17 @@ VARIANT=eng ./build.sh
 
 # A parser that will accept structured logging commandline arguments.
 _parser = argparse.ArgumentParser()
 commandline.add_logging_group(_parser)
 
 def run_marionette(tests, b2g_path=None, emulator=None, testtype=None,
     address=None, binary=None, topsrcdir=None, **kwargs):
 
-    # Import the harness directly and under a different name here to avoid
-    # "marionette" being importable from two locations when "testing/marionette/client"
-    # is on sys.path.
-    # See bug 1050511 and bug 1114474. This needs to be removed with the
-    # resolution of bug 1109183.
-    clientdir = os.path.join(topsrcdir, 'testing/marionette/client')
-    if clientdir in sys.path:
-        sys.path.remove(clientdir)
-    path = os.path.join(topsrcdir, 'testing/marionette/client/marionette/runtests.py')
-    with open(path, 'r') as fh:
-        imp.load_module('marionetteharness', fh, path,
-                        ('.py', 'r', imp.PY_SOURCE))
-
-    from marionetteharness import (
+    from marionette.runtests import (
         MarionetteTestRunner,
         BaseMarionetteOptions,
         startTestRunner
     )
 
     parser = BaseMarionetteOptions()
     commandline.add_logging_group(parser)
     options, args = parser.parse_args()
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.behind.html.ini
@@ -11,10 +11,11 @@
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): PASS
       if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.6.8") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.9") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): PASS
+      if not debug and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): PASS
       FAIL
 
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.beside.html.ini
@@ -11,10 +11,11 @@
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): PASS
       if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.6.8") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.9") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): PASS
+      if not debug and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): PASS
       FAIL
 
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.cone.shape2.html.ini
@@ -11,10 +11,11 @@
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): PASS
       if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.6.8") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.9") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): PASS
+      if not debug and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): PASS
       FAIL
 
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.equal.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.equal.html.ini
@@ -11,10 +11,11 @@
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): PASS
       if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.6.8") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.9") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): PASS
+      if not debug and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): PASS
       FAIL
 
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.touch1.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.touch1.html.ini
@@ -11,10 +11,11 @@
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): PASS
       if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.6.8") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "mac") and (version == "OS X 10.9") and (processor == "x86_64") and (bits == 64): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): PASS
+      if not debug and (os == "mac") and (version == "OS X 10.10.2") and (processor == "x86") and (bits == 32): PASS
       FAIL
 
--- a/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.touch3.html.ini
+++ b/testing/web-platform/meta/2dcontext/fill-and-stroke-styles/2d.gradient.radial.touch3.html.ini
@@ -11,10 +11,11 @@
       if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): PASS
       if debug and (os == "win") and (version == "6.2.9200") and (processor == "x86") and (bits == 32): PA