merge mozilla-inbound to mozilla-central a=merge FIREFOX_AURORA_44_BASE
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 29 Oct 2015 11:49:27 +0100
changeset 304355 67a788db9f07822cfef52351bbbe3745dff8bd7f
parent 304338 f3754dcad86e2ddacb1ad2b003cafb91f299ecf8 (current diff)
parent 304354 e8610a7c6b3e9cdd7cbf30f36aed92e634abd4bc (diff)
child 304356 4fcce62133d48a3480cb9c4affa954a7d2bccc7d
child 304360 4c65cbdabc1feed64570f1fe0546a8e4b29fb828
child 304430 cbe032ac35c235cbd9dbf7c7b2f13f60ead60a30
child 304491 5d11875848ef67c7159cd1fcf0224e0a54ab92bc
child 304582 7756fb39915c856322cb594143cc270d77aacdac
child 305905 d93b73ab69aad662ccff2297aee9e650cbec99fc
child 353150 78a6460d1fe81307ca2ea15a3b550e328427941e
push id6917
push usergijskruitbosch@gmail.com
push dateThu, 29 Oct 2015 12:17:17 +0000
reviewersmerge
milestone44.0a1
merge mozilla-inbound to mozilla-central a=merge
netwerk/protocol/websocket/PWebSocketFrameListener.ipdl
netwerk/protocol/websocket/WebSocketFrameListenerChild.cpp
netwerk/protocol/websocket/WebSocketFrameListenerChild.h
netwerk/protocol/websocket/WebSocketFrameListenerParent.cpp
netwerk/protocol/websocket/WebSocketFrameListenerParent.h
netwerk/protocol/websocket/WebSocketFrameService.cpp
netwerk/protocol/websocket/WebSocketFrameService.h
netwerk/protocol/websocket/nsIWebSocketFrameService.idl
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -345,19 +345,16 @@ AccessibleWrap::get_accValue(
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   // TODO make this work with proxies.
   if (IsProxy())
     return E_NOTIMPL;
 
-  if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT)
-    return E_ACCESSDENIED;
-
   nsAutoString value;
   xpAccessible->Value(value);
 
   // See bug 438784: need to expose URL on doc's value attribute. For this,
   // reverting part of fix for bug 425693 to make this MSAA method behave
   // IAccessible2-style.
   if (value.IsEmpty())
     return S_FALSE;
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -2,16 +2,24 @@
 {
 "size": 80458572,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
+"size": 12057960,
+"digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"setup": "setup.sh",
+"unpack": true
+},
+{
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
 },
 {
 "size": 31078810,
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -729,19 +729,16 @@
 @BINPATH@/libEGL.dll
 @BINPATH@/libGLESv2.dll
 #endif
 #endif
 
 ; [Browser Chrome Files]
 @RESPATH@/chrome/browser@JAREXT@
 @RESPATH@/chrome/browser.manifest
-@RESPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
-@RESPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
-@RESPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/preview.png
 @RESPATH@/chrome/toolkit@JAREXT@
 @RESPATH@/chrome/toolkit.manifest
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 @RESPATH@/chrome/icons/default/default16.png
 @RESPATH@/chrome/icons/default/default32.png
 @RESPATH@/chrome/icons/default/default48.png
 #endif
--- a/browser/components/preferences/in-content/content.js
+++ b/browser/components/preferences/in-content/content.js
@@ -118,17 +118,17 @@ var gContentPane = {
    * Displays the notifications exceptions dialog where specific site notification
    * preferences can be set.
    */
   showNotificationExceptions()
   {
     let bundlePreferences = document.getElementById("bundlePreferences");
     let params = { permissionType: "desktop-notification" };
     params.windowTitle = bundlePreferences.getString("notificationspermissionstitle");
-    params.introText = bundlePreferences.getString("notificationspermissionstext2");
+    params.introText = bundlePreferences.getString("notificationspermissionstext3");
 
     gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
                     "resizable=yes", params);
   },
 
 
   // POP-UPS
 
--- a/browser/components/preferences/in-content/content.xul
+++ b/browser/components/preferences/in-content/content.xul
@@ -65,17 +65,17 @@
   <grid>
     <columns>
       <column flex="1"/>
       <column/>
     </columns>
     <rows>
       <row id="notificationsPolicyRow">
         <vbox align="start">
-          <label id="notificationsPolicy">&notificationsPolicyDesc.label;</label>
+          <label id="notificationsPolicy">&notificationsPolicyDesc2.label;</label>
         </vbox>
         <hbox pack="end">
           <button id="notificationsPolicyButton" label="&notificationsPolicyButton.label;"
                   accesskey="&notificationsPolicyButton.accesskey;"/>
         </hbox>
       </row>
       <row id="notificationsDoNotDisturbRow" hidden="true">
         <vbox align="start">
--- a/browser/locales/en-US/chrome/browser/preferences/content.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/content.dtd
@@ -3,17 +3,17 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!ENTITY  popups.label                "Pop-ups">
 
 <!ENTITY  blockPopups.label           "Block pop-up windows">
 <!ENTITY  blockPopups.accesskey       "B">
 
 <!ENTITY  notificationsPolicy.label            "Notifications">
-<!ENTITY  notificationsPolicyDesc.label        "Choose which sites are allowed to show notifications">
+<!ENTITY  notificationsPolicyDesc2.label        "Choose which sites are allowed to receive notifications">
 <!ENTITY  notificationsPolicyButton.accesskey  "h">
 <!ENTITY  notificationsPolicyButton.label      "Choose…">
 <!ENTITY  notificationsDoNotDisturb.label      "Do not disturb me">
 <!ENTITY  notificationsDoNotDisturb.accesskey  "n">
 <!ENTITY  notificationsDoNotDisturbDetails.value "No notification will be shown until you restart &brandShortName;">
 
 <!ENTITY  popupExceptions.label       "Exceptions…">
 <!ENTITY  popupExceptions.accesskey   "E">
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -20,17 +20,17 @@ acceptVeryLargeMinimumFont=Keep my chang
 #### Permissions Manager
 
 cookiepermissionstext=You can specify which websites are always or never allowed to use cookies.  Type the exact address of the site you want to manage and then click Block, Allow for Session, or Allow.
 cookiepermissionstitle=Exceptions - Cookies
 addonspermissionstext=You can specify which websites are allowed to install add-ons. Type the exact address of the site you want to allow and then click Allow.
 addons_permissions_title=Allowed Sites - Add-ons Installation
 popuppermissionstext=You can specify which websites are allowed to open pop-up windows. Type the exact address of the site you want to allow and then click Allow.
 popuppermissionstitle=Allowed Sites - Pop-ups
-notificationspermissionstext2=Control which websites are always or never allowed to show notifications. If you remove a site, it will need to request permission again.
+notificationspermissionstext3=Control which websites are always or never allowed to receive notifications. If you remove a site, it will need to request permission again.
 notificationspermissionstitle=Notification Permissions
 invalidURI=Please enter a valid hostname
 invalidURITitle=Invalid Hostname Entered
 
 #### Block List Manager
 
 blockliststext=You can choose which list Firefox will use to block Web elements that may track your browsing activity.
 blockliststitle=Block Lists
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -75,12 +75,12 @@ var ContentClick = {
     window.openLinkIn(json.href, where, params);
 
     // Mark the page as a user followed link.  This is done so that history can
     // distinguish automatic embed visits from user activated ones.  For example
     // pages loaded in frames are embed visits and lost with the session, while
     // visits across frames should be preserved.
     try {
       if (!PrivateBrowsingUtils.isWindowPrivate(window))
-        PlacesUIUtils.markPageAsFollowedLink(href);
+        PlacesUIUtils.markPageAsFollowedLink(json.href);
     } catch (ex) { /* Skip invalid URIs. */ }
   }
 };
--- a/configure.in
+++ b/configure.in
@@ -5159,17 +5159,17 @@ MOZ_ARG_DISABLE_BOOL(webm,
 if test -n "$MOZ_WEBM"; then
     AC_DEFINE(MOZ_WEBM)
     MOZ_VPX=1
 fi;
 
 dnl ========================================================
 dnl = Apple platform decoder support
 dnl ========================================================
-if test "$MOZ_WIDGET_TOOLKIT" = "cocoa"; then
+if test "$MOZ_WIDGET_TOOLKIT" = "cocoa" || test "$MOZ_WIDGET_TOOLKIT" = "uikit"; then
   MOZ_APPLEMEDIA=1
 fi
 
 if test "$COMPILE_ENVIRONMENT"; then
 if test -n "$MOZ_APPLEMEDIA"; then
   AC_DEFINE(MOZ_APPLEMEDIA)
   # hack in frameworks for fmp4 - see bug 1029974
   # We load VideoToolbox and CoreMedia dynamically, so they don't appear here.
--- a/devtools/server/tests/unit/test_get-executable-lines.js
+++ b/devtools/server/tests/unit/test_get-executable-lines.js
@@ -33,17 +33,17 @@ function run_test() {
 function test_executable_lines() {
   gThreadClient.addOneTimeListener("newSource", function _onNewSource(evt, packet) {
     do_check_eq(evt, "newSource");
 
     gThreadClient.getSources(function ({error, sources}) {
       do_check_true(!error);
       let source = gThreadClient.source(sources[0]);
       source.getExecutableLines(function(lines){
-        do_check_true(arrays_equal([2, 3, 5, 6, 7, 8, 12, 14, 16], lines));
+        do_check_true(arrays_equal([2, 5, 7, 8, 12, 14, 16], lines));
         finishClient(gClient);
       });
     });
   });
 
   let code = readFile("sourcemapped.js");
 
   Components.utils.evalInSandbox(code, gDebuggee, "1.8",
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -32,16 +32,17 @@
 #include "nsIUnicodeEncoder.h"
 #include "nsThreadUtils.h"
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsIStringBundle.h"
 #include "nsIConsoleService.h"
 #include "mozilla/dom/CloseEvent.h"
+#include "mozilla/net/WebSocketEventService.h"
 #include "nsICryptoHash.h"
 #include "nsJSUtils.h"
 #include "nsIScriptError.h"
 #include "nsNetUtil.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 #include "nsILoadGroup.h"
 #include "mozilla/Preferences.h"
@@ -235,16 +236,18 @@ public:
   nsWeakPtr mWeakLoadGroup;
 
   bool mIsMainThread;
 
   // This mutex protects mWorkerShuttingDown.
   mozilla::Mutex mMutex;
   bool mWorkerShuttingDown;
 
+  RefPtr<WebSocketEventService> mService;
+
 private:
   ~WebSocketImpl()
   {
     // If we threw during Init we never called disconnect
     if (!mDisconnectingOrDisconnected) {
       Disconnect();
     }
   }
@@ -625,21 +628,18 @@ WebSocketImpl::Disconnect()
       new DisconnectInternalRunnable(this);
     runnable->Dispatch(mWorkerPrivate->GetJSContext());
   }
 
   // DontKeepAliveAnyMore() can release the object. So hold a reference to this
   // until the end of the method.
   RefPtr<WebSocketImpl> kungfuDeathGrip = this;
 
-  nsCOMPtr<nsIThread> mainThread;
-  if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
-      NS_FAILED(NS_ProxyRelease(mainThread, mChannel))) {
-    NS_WARNING("Failed to proxy release of channel, leaking instead!");
-  }
+  NS_ReleaseOnMainThread(mChannel);
+  NS_ReleaseOnMainThread(static_cast<nsIWebSocketEventService*>(mService.forget().take()));
 
   mWebSocket->DontKeepAliveAnyMore();
   mWebSocket->mImpl = nullptr;
 
   if (mWorkerPrivate && mWorkerFeature) {
     UnregisterFeature();
   }
 
@@ -764,16 +764,21 @@ WebSocketImpl::OnStart(nsISupports* aCon
     mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
   }
 
   mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
   UpdateURI();
 
   mWebSocket->SetReadyState(WebSocket::OPEN);
 
+  mService->WebSocketOpened(mChannel->Serial(),mInnerWindowID,
+                            mWebSocket->mEffectiveURL,
+                            mWebSocket->mEstablishedProtocol,
+                            mWebSocket->mEstablishedExtensions);
+
   // Let's keep the object alive because the webSocket can be CCed in the
   // onopen callback.
   RefPtr<WebSocket> webSocket = mWebSocket;
 
   // Call 'onopen'
   rv = webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the open event");
@@ -1354,16 +1359,22 @@ WebSocket::Constructor(const GlobalObjec
 
   // It can be that we have been already disconnected because the WebSocket is
   // gone away while we where initializing the webSocket.
   if (!webSocket->mImpl) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
+  // Let's inform devtools about this new active WebSocket.
+  webSocket->mImpl->mService->WebSocketCreated(webSocket->mImpl->mChannel->Serial(),
+                                               webSocket->mImpl->mInnerWindowID,
+                                               webSocket->mURI,
+                                               webSocket->mImpl->mRequestedProtocolList);
+
   cws.Done();
   return webSocket.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket)
   bool isBlack = tmp->IsBlack();
@@ -1439,16 +1450,18 @@ WebSocketImpl::Init(JSContext* aCx,
                     uint32_t aScriptLine,
                     uint32_t aScriptColumn,
                     ErrorResult& aRv,
                     bool* aConnectionFailed)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
 
+  mService = WebSocketEventService::GetOrCreate();
+
   // We need to keep the implementation alive in case the init disconnects it
   // because of some error.
   RefPtr<WebSocketImpl> kungfuDeathGrip = this;
 
   // Attempt to kill "ghost" websocket: but usually too early for check to fail
   aRv = mWebSocket->CheckInnerWindowCorrectness();
   if (NS_WARN_IF(aRv.Failed())) {
     return;
@@ -1653,16 +1666,18 @@ WebSocketImpl::AsyncOpen(nsIPrincipal* a
   nsCOMPtr<nsIURI> uri;
   aRv = NS_NewURI(getter_AddRefs(uri), mURI);
   MOZ_ASSERT(!aRv.Failed());
 
   aRv = mChannel->AsyncOpen(uri, asciiOrigin, aInnerWindowID, this, nullptr);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
+
+  mInnerWindowID = aInnerWindowID;
 }
 
 //-----------------------------------------------------------------------------
 // WebSocketImpl methods:
 //-----------------------------------------------------------------------------
 
 class nsAutoCloseWS final
 {
@@ -1832,24 +1847,30 @@ WebSocket::CreateAndDispatchMessageEvent
   MOZ_ASSERT(mImpl);
   AssertIsOnTargetThread();
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
+  uint16_t messageType = nsIWebSocketEventListener::TYPE_STRING;
+
   // Create appropriate JS object for message
   JS::Rooted<JS::Value> jsData(aCx);
   if (aIsBinary) {
     if (mBinaryType == dom::BinaryType::Blob) {
+      messageType = nsIWebSocketEventListener::TYPE_BLOB;
+
       nsresult rv = nsContentUtils::CreateBlobBuffer(aCx, GetOwner(), aData,
                                                      &jsData);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
+      messageType = nsIWebSocketEventListener::TYPE_ARRAYBUFFER;
+
       JS::Rooted<JSObject*> arrayBuf(aCx);
       nsresult rv = nsContentUtils::CreateArrayBuffer(aCx, aData,
                                                       arrayBuf.address());
       NS_ENSURE_SUCCESS(rv, rv);
       jsData.setObject(*arrayBuf);
     } else {
       NS_RUNTIMEABORT("Unknown binary type!");
       return NS_ERROR_UNEXPECTED;
@@ -1859,16 +1880,20 @@ WebSocket::CreateAndDispatchMessageEvent
     NS_ConvertUTF8toUTF16 utf16Data(aData);
     JSString* jsString;
     jsString = JS_NewUCStringCopyN(aCx, utf16Data.get(), utf16Data.Length());
     NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
 
     jsData.setString(jsString);
   }
 
+  mImpl->mService->WebSocketMessageAvailable(mImpl->mChannel->Serial(),
+                                             mImpl->mInnerWindowID,
+                                             aData, messageType);
+
   // create an event that uses the MessageEvent interface,
   // which does not bubble, is not cancelable, and has no default action
 
   RefPtr<MessageEvent> event = NS_NewDOMMessageEvent(this, nullptr, nullptr);
 
   rv = event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false,
                                jsData, mImpl->mUTF16Origin, EmptyString(),
                                nullptr);
@@ -1878,21 +1903,25 @@ WebSocket::CreateAndDispatchMessageEvent
 
   return DispatchDOMEvent(nullptr, static_cast<Event*>(event), nullptr,
                           nullptr);
 }
 
 nsresult
 WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
                                        uint16_t aCode,
-                                       const nsAString &aReason)
+                                       const nsAString& aReason)
 {
   MOZ_ASSERT(mImpl);
   AssertIsOnTargetThread();
 
+  mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(),
+                                   mImpl->mInnerWindowID,
+                                   aWasClean, aCode, aReason);
+
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   CloseEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -552,23 +552,16 @@ nsDOMAttributeMap::RemoveNamedItemNS(con
 }
 
 uint32_t
 nsDOMAttributeMap::Count() const
 {
   return mAttributeCache.Count();
 }
 
-uint32_t
-nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
-                             void *aUserArg) const
-{
-  return mAttributeCache.EnumerateRead(aFunc, aUserArg);
-}
-
 size_t
 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   n += mAttributeCache.ShallowSizeOfExcludingThis(aMallocSizeOf);
   for (auto iter = mAttributeCache.ConstIter(); !iter.Done(); iter.Next()) {
     n += aMallocSizeOf(iter.Data().get());
--- a/dom/base/nsDOMAttributeMap.h
+++ b/dom/base/nsDOMAttributeMap.h
@@ -123,23 +123,17 @@ public:
    * attributes in mContent.
    *
    * @return The number of attribute nodes in the map.
    */
   uint32_t Count() const;
 
   typedef nsRefPtrHashtable<nsAttrHashKey, Attr> AttrCache;
 
-  /**
-   * Enumerates over the attribute nodess in the map and calls aFunc for each
-   * one. If aFunc returns PL_DHASH_STOP we'll stop enumerating at that point.
-   *
-   * @return The number of attribute nodes that aFunc was called for.
-   */
-  uint32_t Enumerate(AttrCache::EnumReadFunction aFunc, void *aUserArg) const;
+  static void BlastSubtreeToPieces(nsINode *aNode);
 
   Element* GetParentObject() const
   {
     return mContent;
   }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -7628,52 +7628,46 @@ nsIDocument::GetCompatMode(nsString& aCo
 
   if (mCompatMode == eCompatibility_NavQuirks) {
     aCompatMode.AssignLiteral("BackCompat");
   } else {
     aCompatMode.AssignLiteral("CSS1Compat");
   }
 }
 
-static void BlastSubtreeToPieces(nsINode *aNode);
-
-PLDHashOperator
-BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg)
-{
-  nsCOMPtr<nsIAttribute> *attr =
-    static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
-
-  *attr = aData;
-
-  NS_ASSERTION(attr->get(),
-               "non-nsIAttribute somehow made it into the hashmap?!");
-
-  return PL_DHASH_STOP;
-}
-
-static void
-BlastSubtreeToPieces(nsINode *aNode)
+void
+nsDOMAttributeMap::BlastSubtreeToPieces(nsINode *aNode)
 {
   if (aNode->IsElement()) {
     Element *element = aNode->AsElement();
     const nsDOMAttributeMap *map = element->GetAttributeMap();
     if (map) {
       nsCOMPtr<nsIAttribute> attr;
-      while (map->Enumerate(BlastFunc, &attr) > 0) {
+
+      // This non-standard style of iteration is presumably used because some
+      // of the code in the loop body can trigger element removal, which
+      // invalidates the iterator.
+      while (true) {
+        auto iter = map->mAttributeCache.ConstIter();
+        if (iter.Done()) {
+          break;
+        }
+        nsCOMPtr<nsIAttribute> attr = iter.UserData();
+        NS_ASSERTION(attr.get(),
+                     "non-nsIAttribute somehow made it into the hashmap?!");
+
         BlastSubtreeToPieces(attr);
 
-#ifdef DEBUG
-        nsresult rv =
-#endif
+        DebugOnly<nsresult> rv =
           element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
                              attr->NodeInfo()->NameAtom(),
                              false);
 
         // XXX Should we abort here?
-        NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!");
+        NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
       }
     }
   }
 
   uint32_t count = aNode->GetChildCount();
   for (uint32_t i = 0; i < count; ++i) {
     BlastSubtreeToPieces(aNode->GetFirstChild());
     aNode->RemoveChildAt(0, false);
@@ -7830,17 +7824,17 @@ nsIDocument::AdoptNode(nsINode& aAdopted
   }
 
   nsCOMArray<nsINode> nodesWithProperties;
   rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
                           newScope, nodesWithProperties);
   if (rv.Failed()) {
     // Disconnect all nodes from their parents, since some have the old document
     // as their ownerDocument and some have this as their ownerDocument.
-    BlastSubtreeToPieces(adoptedNode);
+    nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
 
     if (!sameDocument && oldDocument) {
       uint32_t count = nodesWithProperties.Count();
       for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
         for (uint32_t i = 0; i < count; ++i) {
           // Remove all properties.
           oldDocument->PropertyTable(j)->
             DeleteAllPropertiesFor(nodesWithProperties[i]);
@@ -7859,17 +7853,17 @@ nsIDocument::AdoptNode(nsINode& aAdopted
       for (uint32_t i = 0; i < count; ++i) {
         rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
                                                         newTable);
       }
     }
 
     if (rv.Failed()) {
       // Disconnect all nodes from their parents.
-      BlastSubtreeToPieces(adoptedNode);
+      nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
 
       return nullptr;
     }
   }
 
   NS_ASSERTION(adoptedNode->OwnerDoc() == this,
                "Should still be in the document we just got adopted into");
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5502,17 +5502,18 @@ nsGlobalWindow::CheckSecurityLeftAndTop(
 
 int32_t
 nsGlobalWindow::GetScrollBoundaryOuter(Side aSide)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   FlushPendingNotifications(Flush_Layout);
   if (nsIScrollableFrame *sf = GetScrollFrame()) {
-    return sf->GetScrollRange().Edge(aSide);
+    return nsPresContext::
+      AppUnitsToIntCSSPixels(sf->GetScrollRange().Edge(aSide));
   }
   return 0;
 }
 
 int32_t
 nsGlobalWindow::GetScrollMinX(ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
@@ -12864,36 +12865,28 @@ nsGlobalWindow::RemoveGamepad(uint32_t a
   if (!mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
     return;
   }
   // Free up the index we were using so it can be reused
   mGamepadIndexSet.Remove(gamepad->Index());
   mGamepads.Remove(aIndex);
 }
 
-// static
-PLDHashOperator
-nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData,
-                                   void* aUserArg)
-{
-  nsTArray<RefPtr<Gamepad> >* array =
-    static_cast<nsTArray<RefPtr<Gamepad> >*>(aUserArg);
-  array->EnsureLengthAtLeast(aData->Index() + 1);
-  (*array)[aData->Index()] = aData;
-  return PL_DHASH_NEXT;
-}
-
 void
 nsGlobalWindow::GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads)
 {
   MOZ_ASSERT(IsInnerWindow());
   aGamepads.Clear();
   // mGamepads.Count() may not be sufficient, but it's not harmful.
   aGamepads.SetCapacity(mGamepads.Count());
-  mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads);
+  for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
+    Gamepad* gamepad = iter.UserData();
+    aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1);
+    aGamepads[gamepad->Index()] = gamepad;
+  }
 }
 
 already_AddRefed<Gamepad>
 nsGlobalWindow::GetGamepad(uint32_t aIndex)
 {
   MOZ_ASSERT(IsInnerWindow());
   RefPtr<Gamepad> gamepad;
 
@@ -12913,32 +12906,25 @@ nsGlobalWindow::SetHasSeenGamepadInput(b
 
 bool
 nsGlobalWindow::HasSeenGamepadInput()
 {
   MOZ_ASSERT(IsInnerWindow());
   return mHasSeenGamepadInput;
 }
 
-// static
-PLDHashOperator
-nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, Gamepad* aData,
-                                    void* aUserArg)
-{
-  RefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
-  gamepadsvc->SyncGamepadState(aKey, aData);
-  return PL_DHASH_NEXT;
-}
-
 void
 nsGlobalWindow::SyncGamepadState()
 {
   MOZ_ASSERT(IsInnerWindow());
   if (mHasSeenGamepadInput) {
-    mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr);
+    RefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
+    for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
+      gamepadsvc->SyncGamepadState(iter.Key(), iter.UserData());
+    }
   }
 }
 #endif // MOZ_GAMEPAD
 
 bool
 nsGlobalWindow::GetVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices)
 {
   FORWARD_TO_INNER(GetVRDevices, (aDevices), false);
@@ -12971,19 +12957,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                 nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserDOMWindow)
   if (tmp->mMessageManager) {
     static_cast<nsFrameMessageManager*>(
       tmp->mMessageManager.get())->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   }
-
-  tmp->mGroupMessageManagers.EnumerateRead(DisconnectGroupMessageManager, nullptr);
-  tmp->mGroupMessageManagers.Clear();
+  tmp->DisconnectAndClearGroupMessageManagers();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroupMessageManagers)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // QueryInterface implementation for nsGlobalChromeWindow
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalChromeWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMChromeWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -758,22 +758,16 @@ public:
   // Inner windows only.
   void AddGamepad(uint32_t aIndex, mozilla::dom::Gamepad* aGamepad);
   void RemoveGamepad(uint32_t aIndex);
   void GetGamepads(nsTArray<RefPtr<mozilla::dom::Gamepad> >& aGamepads);
   already_AddRefed<mozilla::dom::Gamepad> GetGamepad(uint32_t aIndex);
   void SetHasSeenGamepadInput(bool aHasSeen);
   bool HasSeenGamepadInput();
   void SyncGamepadState();
-  static PLDHashOperator EnumGamepadsForSync(const uint32_t& aKey,
-                                             mozilla::dom::Gamepad* aData,
-                                             void* aUserArg);
-  static PLDHashOperator EnumGamepadsForGet(const uint32_t& aKey,
-                                            mozilla::dom::Gamepad* aData,
-                                            void* aUserArg);
 #endif
 
   // Inner windows only.
   // Enable/disable updates for gamepad input.
   void EnableGamepadUpdates();
   void DisableGamepadUpdates();
 
   // Get the VR devices for this window, initializing if necessary
@@ -1901,43 +1895,42 @@ public:
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMChromeWindow interface
   NS_DECL_NSIDOMCHROMEWINDOW
 
   static already_AddRefed<nsGlobalChromeWindow> Create(nsGlobalWindow *aOuterWindow);
 
-  static PLDHashOperator
-  DisconnectGroupMessageManager(const nsAString& aKey,
-                                nsIMessageBroadcaster* aMM,
-                                void* aUserArg)
+  void DisconnectAndClearGroupMessageManagers()
   {
-    if (aMM) {
-      static_cast<nsFrameMessageManager*>(aMM)->Disconnect();
+    for (auto iter = mGroupMessageManagers.Iter(); !iter.Done(); iter.Next()) {
+      nsIMessageBroadcaster* mm = iter.UserData();
+      if (mm) {
+        static_cast<nsFrameMessageManager*>(mm)->Disconnect();
+      }
     }
-    return PL_DHASH_NEXT;
+    mGroupMessageManagers.Clear();
   }
 
 protected:
   explicit nsGlobalChromeWindow(nsGlobalWindow *aOuterWindow)
     : nsGlobalWindow(aOuterWindow),
       mGroupMessageManagers(1)
   {
     mIsChrome = true;
     mCleanMessageManager = true;
   }
 
   ~nsGlobalChromeWindow()
   {
     MOZ_ASSERT(mCleanMessageManager,
                "chrome windows may always disconnect the msg manager");
 
-    mGroupMessageManagers.EnumerateRead(DisconnectGroupMessageManager, nullptr);
-    mGroupMessageManagers.Clear();
+    DisconnectAndClearGroupMessageManagers();
 
     if (mMessageManager) {
       static_cast<nsFrameMessageManager *>(
         mMessageManager.get())->Disconnect();
     }
 
     mCleanMessageManager = false;
   }
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -61,25 +61,135 @@ NS_IMPL_ISUPPORTS(HostObjectURLsReporter
 class BlobURLsReporter final : public nsIMemoryReporter
 {
  public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback,
                             nsISupports* aData, bool aAnonymize) override
   {
-    EnumArg env;
-    env.mCallback = aCallback;
-    env.mData = aData;
-    env.mAnonymize = aAnonymize;
+    if (!gDataTable) {
+      return NS_OK;
+    }
+
+    nsDataHashtable<nsPtrHashKey<nsIDOMBlob>, uint32_t> refCounts;
+
+    // Determine number of URLs per blob, to handle the case where it's > 1.
+    for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) {
+      nsCOMPtr<nsIDOMBlob> blob =
+        do_QueryInterface(iter.UserData()->mObject);
+      if (blob) {
+        refCounts.Put(blob, refCounts.Get(blob) + 1);
+      }
+    }
+
+    for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) {
+      nsCStringHashKey::KeyType key = iter.Key();
+      DataInfo* info = iter.UserData();
+
+      nsCOMPtr<nsIDOMBlob> tmp = do_QueryInterface(info->mObject);
+      RefPtr<mozilla::dom::Blob> blob =
+        static_cast<mozilla::dom::Blob*>(tmp.get());
+
+      if (blob) {
+        NS_NAMED_LITERAL_CSTRING(desc,
+          "A blob URL allocated with URL.createObjectURL; the referenced "
+          "blob cannot be freed until all URLs for it have been explicitly "
+          "invalidated with URL.revokeObjectURL.");
+        nsAutoCString path, url, owner, specialDesc;
+        nsCOMPtr<nsIURI> principalURI;
+        uint64_t size = 0;
+        uint32_t refCount = 1;
+        DebugOnly<bool> blobWasCounted;
+
+        blobWasCounted = refCounts.Get(blob, &refCount);
+        MOZ_ASSERT(blobWasCounted);
+        MOZ_ASSERT(refCount > 0);
+
+        bool isMemoryFile = blob->IsMemoryFile();
+
+        if (isMemoryFile) {
+          ErrorResult rv;
+          size = blob->GetSize(rv);
+          if (NS_WARN_IF(rv.Failed())) {
+            rv.SuppressException();
+            size = 0;
+          }
+        }
+
+        path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
+        BuildPath(path, key, info, aAnonymize);
+
+        if (refCount > 1) {
+          nsAutoCString addrStr;
+
+          addrStr = "0x";
+          addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16);
 
-    if (gDataTable) {
-      gDataTable->EnumerateRead(CountCallback, &env);
-      gDataTable->EnumerateRead(ReportCallback, &env);
+          path += " ";
+          path.AppendInt(refCount);
+          path += "@";
+          path += addrStr;
+
+          specialDesc = desc;
+          specialDesc += "\n\nNOTE: This blob (address ";
+          specialDesc += addrStr;
+          specialDesc += ") has ";
+          specialDesc.AppendInt(refCount);
+          specialDesc += " URLs.";
+          if (isMemoryFile) {
+            specialDesc += " Its size is divided ";
+            specialDesc += refCount > 2 ? "among" : "between";
+            specialDesc += " them in this report.";
+          }
+        }
+
+        const nsACString& descString = specialDesc.IsEmpty()
+            ? static_cast<const nsACString&>(desc)
+            : static_cast<const nsACString&>(specialDesc);
+        if (isMemoryFile) {
+          aCallback->Callback(EmptyCString(),
+              path,
+              KIND_OTHER,
+              UNITS_BYTES,
+              size / refCount,
+              descString,
+              aData);
+        } else {
+          aCallback->Callback(EmptyCString(),
+              path,
+              KIND_OTHER,
+              UNITS_COUNT,
+              1,
+              descString,
+              aData);
+        }
+      } else {
+        // Just report the path for the DOMMediaStream or MediaSource.
+        nsCOMPtr<mozilla::dom::MediaSource>
+          ms(do_QueryInterface(info->mObject));
+        nsAutoCString path;
+        path = ms ? "media-source-urls/" : "dom-media-stream-urls/";
+        BuildPath(path, key, info, aAnonymize);
+
+        NS_NAMED_LITERAL_CSTRING(desc,
+          "An object URL allocated with URL.createObjectURL; the referenced "
+          "data cannot be freed until all URLs for it have been explicitly "
+          "invalidated with URL.revokeObjectURL.");
+
+        aCallback->Callback(EmptyCString(),
+            path,
+            KIND_OTHER,
+            UNITS_COUNT,
+            1,
+            desc,
+            aData);
+      }
     }
+
     return NS_OK;
   }
 
   // Initialize info->mStack to record JS stack info, if enabled.
   // The string generated here is used in ReportCallback, below.
   static void GetJSStackForBlob(DataInfo* aInfo)
   {
     nsCString& stack = aInfo->mStack;
@@ -138,38 +248,16 @@ class BlobURLsReporter final : public ns
       rv = frame->GetCaller(getter_AddRefs(frame));
       NS_ENSURE_SUCCESS_VOID(rv);
     }
   }
 
  private:
   ~BlobURLsReporter() {}
 
-  struct EnumArg {
-    nsIHandleReportCallback* mCallback;
-    nsISupports* mData;
-    bool mAnonymize;
-    nsDataHashtable<nsPtrHashKey<nsIDOMBlob>, uint32_t> mRefCounts;
-  };
-
-  // Determine number of URLs per blob, to handle the case where it's > 1.
-  static PLDHashOperator CountCallback(nsCStringHashKey::KeyType aKey,
-                                       DataInfo* aInfo,
-                                       void* aUserArg)
-  {
-    EnumArg* envp = static_cast<EnumArg*>(aUserArg);
-    nsCOMPtr<nsIDOMBlob> blob;
-
-    blob = do_QueryInterface(aInfo->mObject);
-    if (blob) {
-      envp->mRefCounts.Put(blob, envp->mRefCounts.Get(blob) + 1);
-    }
-    return PL_DHASH_NEXT;
-  }
-
   static void BuildPath(nsAutoCString& path,
                         nsCStringHashKey::KeyType aKey,
                         DataInfo* aInfo,
                         bool anonymize)
   {
     nsCOMPtr<nsIURI> principalURI;
     nsAutoCString url, owner;
     if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) &&
@@ -196,122 +284,16 @@ class BlobURLsReporter final : public ns
     url = aKey;
     url.ReplaceChar('/', '\\');
     if (anonymize) {
       path += "<anonymized-url>";
     } else {
       path += url;
     }
   }
-
-  static PLDHashOperator ReportCallback(nsCStringHashKey::KeyType aKey,
-                                        DataInfo* aInfo,
-                                        void* aUserArg)
-  {
-    EnumArg* envp = static_cast<EnumArg*>(aUserArg);
-    nsCOMPtr<nsIDOMBlob> tmp = do_QueryInterface(aInfo->mObject);
-    RefPtr<mozilla::dom::Blob> blob = static_cast<mozilla::dom::Blob*>(tmp.get());
-
-    if (blob) {
-      NS_NAMED_LITERAL_CSTRING
-        (desc, "A blob URL allocated with URL.createObjectURL; the referenced "
-         "blob cannot be freed until all URLs for it have been explicitly "
-         "invalidated with URL.revokeObjectURL.");
-      nsAutoCString path, url, owner, specialDesc;
-      nsCOMPtr<nsIURI> principalURI;
-      uint64_t size = 0;
-      uint32_t refCount = 1;
-      DebugOnly<bool> blobWasCounted;
-
-      blobWasCounted = envp->mRefCounts.Get(blob, &refCount);
-      MOZ_ASSERT(blobWasCounted);
-      MOZ_ASSERT(refCount > 0);
-
-      bool isMemoryFile = blob->IsMemoryFile();
-
-      if (isMemoryFile) {
-        ErrorResult rv;
-        size = blob->GetSize(rv);
-        if (NS_WARN_IF(rv.Failed())) {
-          rv.SuppressException();
-          size = 0;
-        }
-      }
-
-      path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
-      BuildPath(path, aKey, aInfo, envp->mAnonymize);
-
-      if (refCount > 1) {
-        nsAutoCString addrStr;
-
-        addrStr = "0x";
-        addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16);
-
-        path += " ";
-        path.AppendInt(refCount);
-        path += "@";
-        path += addrStr;
-
-        specialDesc = desc;
-        specialDesc += "\n\nNOTE: This blob (address ";
-        specialDesc += addrStr;
-        specialDesc += ") has ";
-        specialDesc.AppendInt(refCount);
-        specialDesc += " URLs.";
-        if (isMemoryFile) {
-          specialDesc += " Its size is divided ";
-          specialDesc += refCount > 2 ? "among" : "between";
-          specialDesc += " them in this report.";
-        }
-      }
-
-      const nsACString& descString = specialDesc.IsEmpty()
-          ? static_cast<const nsACString&>(desc)
-          : static_cast<const nsACString&>(specialDesc);
-      if (isMemoryFile) {
-        envp->mCallback->Callback(EmptyCString(),
-            path,
-            KIND_OTHER,
-            UNITS_BYTES,
-            size / refCount,
-            descString,
-            envp->mData);
-      }
-      else {
-        envp->mCallback->Callback(EmptyCString(),
-            path,
-            KIND_OTHER,
-            UNITS_COUNT,
-            1,
-            descString,
-            envp->mData);
-      }
-    } else {
-      // Just report the path for the DOMMediaStream or MediaSource.
-      nsCOMPtr<mozilla::dom::MediaSource> ms(do_QueryInterface(aInfo->mObject));
-      nsAutoCString path;
-      path = ms ? "media-source-urls/" : "dom-media-stream-urls/";
-      BuildPath(path, aKey, aInfo, envp->mAnonymize);
-
-      NS_NAMED_LITERAL_CSTRING
-        (desc, "An object URL allocated with URL.createObjectURL; the referenced "
-               "data cannot be freed until all URLs for it have been explicitly "
-               "invalidated with URL.revokeObjectURL.");
-
-      envp->mCallback->Callback(EmptyCString(),
-          path,
-          KIND_OTHER,
-          UNITS_COUNT,
-          1,
-          desc,
-          envp->mData);
-    }
-
-    return PL_DHASH_NEXT;
-  }
 };
 
 NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
 
 } // namespace mozilla
 
 void
 nsHostObjectProtocolHandler::Init(void)
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -784,47 +784,16 @@ CheckForGhostWindowsEnumerator(nsISuppor
         }
       }
     }
   }
 
   return PL_DHASH_NEXT;
 }
 
-struct GetNonDetachedWindowDomainsEnumeratorData
-{
-  nsTHashtable<nsCStringHashKey> *nonDetachedDomains;
-  nsIEffectiveTLDService *tldService;
-};
-
-static PLDHashOperator
-GetNonDetachedWindowDomainsEnumerator(const uint64_t& aId, nsGlobalWindow* aWindow,
-                                      void* aClosure)
-{
-  GetNonDetachedWindowDomainsEnumeratorData *data =
-    static_cast<GetNonDetachedWindowDomainsEnumeratorData*>(aClosure);
-
-  // Null outer window implies null top, but calling GetTop() when there's no
-  // outer window causes us to spew debug warnings.
-  if (!aWindow->GetOuterWindow() || !aWindow->GetTopInternal()) {
-    // This window is detached, so we don't care about its domain.
-    return PL_DHASH_NEXT;
-  }
-
-  nsCOMPtr<nsIURI> uri = GetWindowURI(aWindow);
-
-  nsAutoCString domain;
-  if (uri) {
-    data->tldService->GetBaseDomain(uri, 0, domain);
-  }
-
-  data->nonDetachedDomains->PutEntry(domain);
-  return PL_DHASH_NEXT;
-}
-
 /**
  * Iterate over mDetachedWindows and update it to reflect the current state of
  * the world.  In particular:
  *
  *   - Remove weak refs to windows which no longer exist.
  *
  *   - Remove references to windows which are no longer detached.
  *
@@ -856,20 +825,32 @@ nsWindowMemoryReporter::CheckForGhostWin
   }
 
   mLastCheckForGhostWindows = TimeStamp::NowLoRes();
   KillCheckTimer();
 
   nsTHashtable<nsCStringHashKey> nonDetachedWindowDomains;
 
   // Populate nonDetachedWindowDomains.
-  GetNonDetachedWindowDomainsEnumeratorData nonDetachedEnumData =
-    { &nonDetachedWindowDomains, tldService };
-  windowsById->EnumerateRead(GetNonDetachedWindowDomainsEnumerator,
-                             &nonDetachedEnumData);
+  for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
+    // Null outer window implies null top, but calling GetTop() when there's no
+    // outer window causes us to spew debug warnings.
+    nsGlobalWindow* window = iter.UserData();
+    if (!window->GetOuterWindow() || !window->GetTopInternal()) {
+      // This window is detached, so we don't care about its domain.
+      continue;
+    }
+
+    nsCOMPtr<nsIURI> uri = GetWindowURI(window);
+    nsAutoCString domain;
+    if (uri) {
+      tldService->GetBaseDomain(uri, 0, domain);
+    }
+    nonDetachedWindowDomains.PutEntry(domain);
+  }
 
   // Update mDetachedWindows and write the ghost window IDs into aOutGhostIDs,
   // if it's not null.
   CheckForGhostWindowsEnumeratorData ghostEnumData =
     { &nonDetachedWindowDomains, aOutGhostIDs, tldService,
       GetGhostTimeout(), mLastCheckForGhostWindows };
   mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
                              &ghostEnumData);
--- a/dom/base/test/test_websocket_frame.html
+++ b/dom/base/test/test_websocket_frame.html
@@ -7,61 +7,106 @@
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
 </head>
 <body>
 <script class="testbody" type="text/javascript">
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
+const URI = "ws://mochi.test:8888/tests/dom/base/test/file_websocket_basic";
+
 var frameReceivedCounter = 0;
 var frameSentCounter = 0;
+var webSocketCreatedCounter = 0;
+var webSocketOpenedCounter = 0;
+var webSocketMessageAvailableCounter = 0;
+var webSocketClosedCounter = 0;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var tests = [
   { payload: "Hello world!" },
   { payload: (function() { var buffer = ""; for (var i = 0; i < 120; ++i) buffer += i; return buffer; }()) },
-  { payload: "end" },
 ]
 
 var innerId =
   window.top.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
 ok(innerId, "We have a valid innerWindowID: " + innerId);
 
-var service = Cc["@mozilla.org/websocketframe/service;1"]
-                .getService(Ci.nsIWebSocketFrameService);
-ok(!!service, "We have the nsIWebSocketFrameService");
+var service = Cc["@mozilla.org/websocketevent/service;1"]
+                .getService(Ci.nsIWebSocketEventService);
+ok(!!service, "We have the nsIWebSocketEventService");
 
 var listener = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebSocketFrameListener]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebSocketEventListener]),
+
+  webSocketCreated: function(aWebSocketSerialID, aURI, aProtocols) {
+    info("WebSocketCreated");
+
+    is(aURI, URI, "URI matches");
+    is(aProtocols, "frame", "Protocol matches");
+
+    webSocketCreatedCounter++;
+  },
+
+  webSocketOpened: function(aWebSocketSerialID, aEffectiveURI, aProtocols, aExtensions) {
+    info("WebSocketOpened");
+
+    is(aEffectiveURI, URI, "EffectiveURI matches");
+    is(aProtocols, "frame", "Protocol matches");
+    is(aExtensions, "permessage-deflate", "No extensions");
+
+    webSocketOpenedCounter++;
+  },
+
+  webSocketMessageAvailable: function(aWebSocketSerialID, aData, aMessageType) {
+    info("WebSocketMessageAvailable");
+
+    if (tests.length) {
+      is(aData, tests[0].payload, "Message matches!");
+      is(aMessageType, Ci.nsIWebSocketEventListener.TYPE_STRING, "The type is 'string'");
+
+      webSocketMessageAvailableCounter++;
+
+      tests.shift();
+      if (tests.length) {
+        ws.send(tests[0].payload);
+      } else {
+        ws.send("end");
+      }
+    }
+  },
+
+  webSocketClosed: function(aWebSocketSerialID, aWasClean,
+                            aCode, aReason) {
+    info("WebSocketClosed");
+
+    ok(aWasClean, "The socket is closed in a clean state");
+    is(aCode, 1000, "Exit code 1000");
+    ok(!aReason.length, "No reason");
+
+    webSocketClosedCounter++;
+    checkListener();
+  },
 
   frameReceived: function(aWebSocketSerialID, aFrame) {
     ok(!!aFrame, "We have received a frame");
 
     if (tests.length) {
       ok(aFrame.timeStamp, "Checking timeStamp: " + aFrame.timeStamp);
       is(aFrame.finBit, true, "Checking finBit");
       is(aFrame.rsvBit1, true, "Checking rsvBit1");
       is(aFrame.rsvBit2, false, "Checking rsvBit2");
       is(aFrame.rsvBit3, false, "Checking rsvBit3");
       is(aFrame.opCode, aFrame.OPCODE_TEXT, "Checking opCode");
       is(aFrame.maskBit, false, "Checking maskBit");
       is(aFrame.mask, 0, "Checking mask");
       is(aFrame.payload, tests[0].payload, "Checking payload: " + aFrame.payload);
-    } else {
-      ok(aFrame.timeStamp, "Checking timeStamp: " + aFrame.timeStamp);
-      is(aFrame.finBit, true, "Checking finBit");
-      is(aFrame.rsvBit1, false, "Checking rsvBit1");
-      is(aFrame.rsvBit2, false, "Checking rsvBit2");
-      is(aFrame.rsvBit3, false, "Checking rsvBit3");
-      is(aFrame.opCode, aFrame.OPCODE_CLOSE, "Checking opCode");
-      is(aFrame.maskBit, false, "Checking maskBit");
-      is(aFrame.mask, 0, "Checking mask");
     }
 
     frameReceivedCounter++;
   },
 
   frameSent: function(aWebSocketSerialID, aFrame) {
     ok(!!aFrame, "We have sent a frame");
 
@@ -70,63 +115,52 @@ var listener = {
       is(aFrame.finBit, true, "Checking finBit");
       is(aFrame.rsvBit1, true, "Checking rsvBit1");
       is(aFrame.rsvBit2, false, "Checking rsvBit2");
       is(aFrame.rsvBit3, false, "Checking rsvBit3");
       is(aFrame.opCode, aFrame.OPCODE_TEXT, "Checking opCode");
       is(aFrame.maskBit, true, "Checking maskBit");
       ok(!!aFrame.mask, "Checking mask: " + aFrame.mask);
       is(aFrame.payload, tests[0].payload, "Checking payload: " + aFrame.payload);
-    } else {
-      ok(aFrame.timeStamp, "Checking timeStamp: " + aFrame.timeStamp);
-      is(aFrame.finBit, true, "Checking finBit");
-      is(aFrame.rsvBit1, false, "Checking rsvBit1");
-      is(aFrame.rsvBit2, false, "Checking rsvBit2");
-      is(aFrame.rsvBit3, false, "Checking rsvBit3");
-      is(aFrame.opCode, aFrame.OPCODE_CLOSE, "Checking opCode");
-      is(aFrame.maskBit, true, "Checking maskBit");
-      ok(!!aFrame.mask, "Checking mask: " + aFrame.mask);
     }
 
     frameSentCounter++;
   }
 };
 
 service.addListener(innerId, listener);
 ok(true, "Listener added");
 
 function checkListener() {
   service.removeListener(innerId, listener);
 
   ok(frameReceivedCounter, "We received some frames!");
   ok(frameSentCounter, "We sent some frames!");
+  ok(webSocketCreatedCounter, "We have a create notification");
+  ok(webSocketOpenedCounter, "We have a open notification");
+  ok(webSocketMessageAvailableCounter, "We have a messageAvailable notification");
+  ok(webSocketClosedCounter, "We have a close notification");
   SimpleTest.finish();
 }
 
-var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_basic", "frame");
+var ws = new WebSocket(URI, "frame");
 ws.onopen = function(e) {
   info("onopen");
 
   ws.send(tests[0].payload);
 }
 
 ws.onclose = function(e) {
   info("onclose");
-
-  ws.close();
-  checkListener();
 }
 
 ws.onmessage = function(e) {
   info("onmessage");
-
-  is(e.data, tests[0].payload, "Wrong data");
-  tests.shift();
   if (tests.length) {
-    ws.send(tests[0].payload);
+    is(e.data, tests[0].payload, "Wrong data");
   }
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </body>
 </html>
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4492,17 +4492,17 @@ CanvasRenderingContext2D::DrawImage(cons
     }
     const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
     bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage, srcImage->GetSize(),
                                                    mVideoTexture, LOCAL_GL_TEXTURE_2D,
                                                    destOrigin);
     if (ok) {
       NativeSurface texSurf;
       texSurf.mType = NativeSurfaceType::OPENGL_TEXTURE;
-      texSurf.mFormat = SurfaceFormat::R5G6B5;
+      texSurf.mFormat = SurfaceFormat::R5G6B5_UINT16;
       texSurf.mSize.width = mCurrentVideoSize.width;
       texSurf.mSize.height = mCurrentVideoSize.height;
       texSurf.mSurface = (void*)((uintptr_t)mVideoTexture);
 
       srcSurf = mTarget->CreateSourceSurfaceFromNativeSurface(texSurf);
       imgSize.width = mCurrentVideoSize.width;
       imgSize.height = mCurrentVideoSize.height;
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1938,17 +1938,17 @@ WebGLContext::SurfaceFromElementResultTo
             *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA
             break;
         case SurfaceFormat::B8G8R8X8:
             *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
             break;
         case SurfaceFormat::A8:
             *format = WebGLTexelFormat::A8;
             break;
-        case SurfaceFormat::R5G6B5:
+        case SurfaceFormat::R5G6B5_UINT16:
             *format = WebGLTexelFormat::RGB565;
             break;
         default:
             NS_ASSERTION(false, "Unsupported image format. Unimplemented.");
             return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     imageOut = data;
--- a/dom/media/apple/AppleMP3Reader.cpp
+++ b/dom/media/apple/AppleMP3Reader.cpp
@@ -425,19 +425,20 @@ AppleMP3Reader::SetupDecoder()
   AudioStreamBasicDescription inputFormat, outputFormat;
   GetProperty(mAudioFileStream, kAudioFileStreamProperty_DataFormat, &inputFormat);
 
   memset(&outputFormat, 0, sizeof(outputFormat));
 
   // Set output format
 #if defined(MOZ_SAMPLE_TYPE_FLOAT32)
   outputFormat.mBitsPerChannel = 32;
-  outputFormat.mFormatFlags =
-    kLinearPCMFormatFlagIsFloat |
-    0;
+  outputFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat;
+#elif defined(MOZ_SAMPLE_TYPE_S16)
+  outputFormat.mBitsPerChannel = 32;
+  outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
 #else
 #error Unknown audio sample type
 #endif
 
   mAudioSampleRate = outputFormat.mSampleRate = inputFormat.mSampleRate;
   mAudioChannels
     = outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
   mAudioFramesPerCompressedPacket = inputFormat.mFramesPerPacket;
@@ -501,29 +502,40 @@ AppleMP3Reader::Seek(int64_t aTime, int6
 void
 AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
+  AutoPinned<MediaResource> resource(mResource.GetResource());
+  nsTArray<MediaByteRange> byteRanges;
+  nsresult rv = resource->GetCachedRanges(byteRanges);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  IntervalSet<int64_t> intervals;
+  for (auto& range : byteRanges) {
+    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  }
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
-      mResource.MediaReadAt(interval.mStart, interval.Length());
+      resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
+  }
 
-    uint64_t duration = mMP3FrameParser.GetDuration();
-    if (duration != mDuration) {
-      LOGD("Updating media duration to %lluus\n", duration);
-      MOZ_ASSERT(mDecoder);
-      mDuration = duration;
-      mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
-    }
+  uint64_t duration = mMP3FrameParser.GetDuration();
+  if (duration != mDuration) {
+    LOGD("Updating media duration to %lluus\n", duration);
+    MOZ_ASSERT(mDecoder);
+    mDuration = duration;
+    mDecoder->DispatchUpdateEstimatedMediaDuration(duration);
   }
 }
 
 } // namespace mozilla
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -382,28 +382,38 @@ DirectShowReader::SeekInternal(int64_t a
 void
 DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
+  AutoPinned<MediaResource> resource(mDecoder->GetResource());
+  nsTArray<MediaByteRange> byteRanges;
+  nsresult rv = resource->GetCachedRanges(byteRanges);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  IntervalSet<int64_t> intervals;
+  for (auto& range : byteRanges) {
+    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  }
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
-      mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length());
+      resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
-
-    int64_t duration = mMP3FrameParser.GetDuration();
-    if (duration != mDuration) {
-      MOZ_ASSERT(mDecoder);
-      mDuration = duration;
-      mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration);
-    }
+  }
+  int64_t duration = mMP3FrameParser.GetDuration();
+  if (duration != mDuration) {
+    MOZ_ASSERT(mDecoder);
+    mDuration = duration;
+    mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration);
   }
 }
 
 } // namespace mozilla
--- a/dom/media/encoder/VP8TrackEncoder.cpp
+++ b/dom/media/encoder/VP8TrackEncoder.cpp
@@ -398,17 +398,17 @@ nsresult VP8TrackEncoder::PrepareRawFram
       case SurfaceFormat::B8G8R8X8:
         rv = libyuv::ARGBToI420(static_cast<uint8*>(map.GetData()),
                                 map.GetStride(),
                                 y, mFrameWidth,
                                 cb, halfWidth,
                                 cr, halfWidth,
                                 mFrameWidth, mFrameHeight);
         break;
-      case SurfaceFormat::R5G6B5:
+      case SurfaceFormat::R5G6B5_UINT16:
         rv = libyuv::RGB565ToI420(static_cast<uint8*>(map.GetData()),
                                   map.GetStride(),
                                   y, mFrameWidth,
                                   cb, halfWidth,
                                   cr, halfWidth,
                                   mFrameWidth, mFrameHeight);
         break;
       default:
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -1281,32 +1281,42 @@ void GStreamerReader::NotifyDataArrivedI
   MOZ_ASSERT(OnTaskQueue());
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
+  AutoPinned<MediaResource> resource(mResource.GetResource());
+  nsTArray<MediaByteRange> byteRanges;
+  nsresult rv = resource->GetCachedRanges(byteRanges);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  IntervalSet<int64_t> intervals;
+  for (auto& range : byteRanges) {
+    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  }
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
-      mResource.MediaReadAt(interval.mStart, interval.Length());
+      resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
-
-    int64_t duration = mMP3FrameParser.GetDuration();
-    if (duration != mLastParserDuration && mUseParserDuration) {
-      MOZ_ASSERT(mDecoder);
-      mLastParserDuration = duration;
-      mDecoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
-    }
+  }
+  int64_t duration = mMP3FrameParser.GetDuration();
+  if (duration != mLastParserDuration && mUseParserDuration) {
+    MOZ_ASSERT(mDecoder);
+    mLastParserDuration = duration;
+    mDecoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
   }
 }
 
 #if GST_VERSION_MAJOR >= 1
 GstCaps* GStreamerReader::BuildAudioSinkCaps()
 {
   GstCaps* caps = gst_caps_from_string("audio/x-raw, channels={1,2}");
   const char* format;
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -22,16 +22,17 @@
 #include "nsError.h"
 #include "nsIRunnable.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 #include "nsServiceManagerUtils.h"
+#include "gfxPlatform.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 struct JSContext;
 class JSObject;
 
@@ -70,23 +71,24 @@ static const char* const gMediaSourceTyp
 };
 
 // Returns true if we should enable MSE webm regardless of preferences.
 // 1. If MP4/H264 isn't supported:
 //   * Windows XP
 //   * Windows Vista and Server 2008 without the optional "Platform Update Supplement"
 //   * N/KN editions (Europe and Korea) of Windows 7/8/8.1/10 without the
 //     optional "Windows Media Feature Pack"
-
+// 2. If H264 hardware acceleration is not available.
 static bool
 IsWebMForced()
 {
   bool mp4supported =
     DecoderTraits::IsMP4TypeAndEnabled(NS_LITERAL_CSTRING("video/mp4"));
-  return !mp4supported;
+  bool hwsupported = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
+  return !mp4supported || !hwsupported;
 }
 
 static nsresult
 IsTypeSupported(const nsAString& aType)
 {
   if (aType.IsEmpty()) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
--- a/dom/media/omx/MediaCodecReader.cpp
+++ b/dom/media/omx/MediaCodecReader.cpp
@@ -515,20 +515,31 @@ MediaCodecReader::HasVideo()
 {
   return mInfo.HasVideo();
 }
 
 void
 MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength,
                                             int64_t aOffset)
 {
-  IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
+  AutoPinned<MediaResource> resource(mDecoder->GetResource());
+  nsTArray<MediaByteRange> byteRanges;
+  nsresult rv = resource->GetCachedRanges(byteRanges);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  IntervalSet<int64_t> intervals;
+  for (auto& range : byteRanges) {
+    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  }
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
-      mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length());
+      resource->MediaReadAt(interval.mStart, interval.Length());
     MonitorAutoLock monLock(mParserMonitor);
     if (mNextParserPosition == mParsedDataLength &&
         mNextParserPosition >= interval.mStart &&
         mNextParserPosition <= interval.mEnd) {
       // No pending parsing runnable currently. And available data are adjacent to
       // parsed data.
       int64_t shift = mNextParserPosition - interval.mStart;
       const char* buffer = reinterpret_cast<const char*>(bytes->Elements()) + shift;
--- a/dom/media/omx/MediaOmxReader.cpp
+++ b/dom/media/omx/MediaOmxReader.cpp
@@ -458,31 +458,41 @@ void MediaOmxReader::NotifyDataArrivedIn
   }
   if (HasVideo()) {
     return;
   }
   if (!mMP3FrameParser.NeedsData()) {
     return;
   }
 
-  IntervalSet<int64_t> intervals = mFilter.NotifyDataArrived(aLength, aOffset);
+  AutoPinned<MediaResource> resource(mDecoder->GetResource());
+  nsTArray<MediaByteRange> byteRanges;
+  nsresult rv = resource->GetCachedRanges(byteRanges);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  IntervalSet<int64_t> intervals;
+  for (auto& range : byteRanges) {
+    intervals += mFilter.NotifyDataArrived(range.Length(), range.mStart);
+  }
   for (const auto& interval : intervals) {
     RefPtr<MediaByteBuffer> bytes =
-      mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length());
+      resource->MediaReadAt(interval.mStart, interval.Length());
     NS_ENSURE_TRUE_VOID(bytes);
     mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart);
     if (!mMP3FrameParser.IsMP3()) {
       return;
     }
-
-    int64_t duration = mMP3FrameParser.GetDuration();
-    if (duration != mLastParserDuration) {
-      mLastParserDuration = duration;
-      decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
-    }
+  }
+  int64_t duration = mMP3FrameParser.GetDuration();
+  if (duration != mLastParserDuration) {
+    mLastParserDuration = duration;
+    decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration);
   }
 }
 
 bool MediaOmxReader::DecodeAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
   EnsureActive();
 
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -236,16 +236,25 @@ PDMFactory::SupportsMimeType(const nsACS
   return !!current;
 }
 
 void
 PDMFactory::CreatePDMs()
 {
   RefPtr<PlatformDecoderModule> m;
 
+  if (sUseBlankDecoder) {
+    m = CreateBlankDecoderModule();
+    StartupPDM(m);
+    // The Blank PDM SupportsMimeType reports true for all codecs; the creation
+    // of its decoder is infallible. As such it will be used for all media, we
+    // can stop creating more PDM from this point.
+    return;
+  }
+
   if (sGMPDecoderEnabled) {
     m = new GMPDecoderModule();
     StartupPDM(m);
   }
 #ifdef MOZ_WIDGET_ANDROID
   if(sAndroidMCDecoderPreferred && sAndroidMCDecoderEnabled) {
     m = new AndroidDecoderModule();
     StartupPDM(m);
@@ -277,21 +286,16 @@ PDMFactory::CreatePDMs()
   if(sAndroidMCDecoderEnabled){
     m = new AndroidDecoderModule();
     StartupPDM(m);
   }
 #endif
 
   m = new AgnosticDecoderModule();
   StartupPDM(m);
-
-  if (sUseBlankDecoder) {
-    m = CreateBlankDecoderModule();
-    StartupPDM(m);
-  }
 }
 
 bool
 PDMFactory::StartupPDM(PlatformDecoderModule* aPDM)
 {
   if (aPDM && NS_SUCCEEDED(aPDM->Startup())) {
     mCurrentPDMs.AppendElement(aPDM);
     return true;
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -392,16 +392,19 @@ AppleATDecoder::SetupDecoder(MediaRawDat
   mOutputFormat.mFormatID = kAudioFormatLinearPCM;
   mOutputFormat.mSampleRate = inputFormat.mSampleRate;
   mOutputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
 #if defined(MOZ_SAMPLE_TYPE_FLOAT32)
   mOutputFormat.mBitsPerChannel = 32;
   mOutputFormat.mFormatFlags =
     kLinearPCMFormatFlagIsFloat |
     0;
+#elif defined(MOZ_SAMPLE_TYPE_S16)
+  mOutputFormat.mBitsPerChannel = 16;
+  mOutputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | 0;
 #else
 # error Unknown audio sample type
 #endif
   // Set up the decoder so it gives us one sample per frame
   mOutputFormat.mFramesPerPacket = 1;
   mOutputFormat.mBytesPerPacket = mOutputFormat.mBytesPerFrame
         = mOutputFormat.mChannelsPerFrame * mOutputFormat.mBitsPerChannel / 8;
 
--- a/dom/media/platforms/apple/AppleCMLinker.cpp
+++ b/dom/media/platforms/apple/AppleCMLinker.cpp
@@ -4,18 +4,21 @@
  * 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 <dlfcn.h>
 
 #include "AppleCMLinker.h"
 #include "MainThreadUtils.h"
 #include "mozilla/ArrayUtils.h"
+#include "nsDebug.h"
+
+#ifndef MOZ_WIDGET_UIKIT
 #include "nsCocoaFeatures.h"
-#include "nsDebug.h"
+#endif
 
 extern PRLogModuleInfo* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleCMLinker::LinkStatus
 AppleCMLinker::sLinkStatus = LinkStatus_INIT;
@@ -52,17 +55,21 @@ AppleCMLinker::Link()
       break;
     }
   }
   if (!dlfound) {
     NS_WARNING("Couldn't load CoreMedia framework");
     goto fail;
   }
 
+#ifdef MOZ_WIDGET_UIKIT
+  if (true) {
+#else
   if (nsCocoaFeatures::OnLionOrLater()) {
+#endif
 #define LINK_FUNC2(func)                                       \
   func = (typeof(func))dlsym(sLink, #func);                    \
   if (!func) {                                                 \
     NS_WARNING("Couldn't load CoreMedia function " #func );    \
     goto fail;                                                 \
   }
 #define LINK_FUNC(func) LINK_FUNC2(CM ## func)
 #include "AppleCMFunctions.h"
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -9,26 +9,29 @@
 #include "AppleDecoderModule.h"
 #include "AppleUtils.h"
 #include "AppleVDADecoder.h"
 #include "AppleVDALinker.h"
 #include "MediaInfo.h"
 #include "mp4_demuxer/H264.h"
 #include "MP4Decoder.h"
 #include "MediaData.h"
-#include "MacIOSurfaceImage.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsAutoPtr.h"
-#include "nsCocoaFeatures.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 #include "VideoUtils.h"
 #include <algorithm>
 #include "gfxPlatform.h"
 
+#ifndef MOZ_WIDGET_UIKIT
+#include "nsCocoaFeatures.h"
+#include "MacIOSurfaceImage.h"
+#endif
+
 extern PRLogModuleInfo* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 //#define LOG_MEDIA_SHA1
 
 namespace mozilla {
 
 AppleVDADecoder::AppleVDADecoder(const VideoInfo& aConfig,
                                FlushableTaskQueue* aVideoTaskQueue,
@@ -38,18 +41,23 @@ AppleVDADecoder::AppleVDADecoder(const V
   , mCallback(aCallback)
   , mImageContainer(aImageContainer)
   , mPictureWidth(aConfig.mImage.width)
   , mPictureHeight(aConfig.mImage.height)
   , mDisplayWidth(aConfig.mDisplay.width)
   , mDisplayHeight(aConfig.mDisplay.height)
   , mInputIncoming(0)
   , mIsShutDown(false)
+#ifdef MOZ_WIDGET_UIKIT
+  , mUseSoftwareImages(true)
+  , mIs106(false)
+#else
   , mUseSoftwareImages(false)
   , mIs106(!nsCocoaFeatures::OnLionOrLater())
+  #endif
   , mQueuedSamples(0)
   , mMonitor("AppleVideoDecoder")
   , mIsFlushing(false)
   , mDecoder(nullptr)
 {
   MOZ_COUNT_CTOR(AppleVDADecoder);
   // TODO: Verify aConfig.mime_type.
 
@@ -378,16 +386,17 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
                         aFrameRef.duration.ToMicroseconds(),
                         buffer,
                         aFrameRef.is_sync_point,
                         aFrameRef.decode_timestamp.ToMicroseconds(),
                         visible);
     // Unlock the returned image data.
     CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
   } else {
+#ifndef MOZ_WIDGET_UIKIT
     IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
     MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
 
     RefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
 
     RefPtr<layers::Image> image =
       mImageContainer->CreateImage(ImageFormat::MAC_IOSURFACE);
     layers::MacIOSurfaceImage* videoImage =
@@ -399,16 +408,19 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
                                  mImageContainer,
                                  aFrameRef.byte_offset,
                                  aFrameRef.composition_timestamp.ToMicroseconds(),
                                  aFrameRef.duration.ToMicroseconds(),
                                  image.forget(),
                                  aFrameRef.is_sync_point,
                                  aFrameRef.decode_timestamp.ToMicroseconds(),
                                  visible);
+#else
+    MOZ_ASSERT_UNREACHABLE("No MacIOSurface on iOS");
+#endif
   }
 
   if (!data) {
     NS_ERROR("Couldn't create VideoData for frame");
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
@@ -603,16 +615,17 @@ AppleVDADecoder::CreateOutputConfigurati
     return CFDictionaryCreate(kCFAllocatorDefault,
                               outputKeys,
                               outputValues,
                               ArrayLength(outputKeys),
                               &kCFTypeDictionaryKeyCallBacks,
                               &kCFTypeDictionaryValueCallBacks);
   }
 
+#ifndef MOZ_WIDGET_UIKIT
   // Construct IOSurface Properties
   const void* IOSurfaceKeys[] = { MacIOSurfaceLib::kPropIsGlobal };
   const void* IOSurfaceValues[] = { kCFBooleanTrue };
   static_assert(ArrayLength(IOSurfaceKeys) == ArrayLength(IOSurfaceValues),
                 "Non matching keys/values array size");
 
   // Contruct output configuration.
   AutoCFRelease<CFDictionaryRef> IOSurfaceProperties =
@@ -633,16 +646,19 @@ AppleVDADecoder::CreateOutputConfigurati
                 "Non matching keys/values array size");
 
   return CFDictionaryCreate(kCFAllocatorDefault,
                             outputKeys,
                             outputValues,
                             ArrayLength(outputKeys),
                             &kCFTypeDictionaryKeyCallBacks,
                             &kCFTypeDictionaryValueCallBacks);
+#else
+  MOZ_ASSERT_UNREACHABLE("No MacIOSurface on iOS");
+#endif
 }
 
 /* static */
 already_AddRefed<AppleVDADecoder>
 AppleVDADecoder::CreateVDADecoder(
   const VideoInfo& aConfig,
   FlushableTaskQueue* aVideoTaskQueue,
   MediaDataDecoderCallback* aCallback,
--- a/dom/media/webm/WebMReader.cpp
+++ b/dom/media/webm/WebMReader.cpp
@@ -785,20 +785,30 @@ media::TimeIntervals WebMReader::GetBuff
   }
 
   return buffered;
 }
 
 void WebMReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset)
 {
   MOZ_ASSERT(OnTaskQueue());
-  RefPtr<MediaByteBuffer> bytes =
-    mDecoder->GetResource()->MediaReadAt(aOffset, aLength);
-  NS_ENSURE_TRUE_VOID(bytes);
-  mBufferedState->NotifyDataArrived(bytes->Elements(), aLength, aOffset);
+  AutoPinned<MediaResource> resource(mDecoder->GetResource());
+  nsTArray<MediaByteRange> byteRanges;
+  nsresult rv = resource->GetCachedRanges(byteRanges);
+
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  for (auto& range : byteRanges) {
+    RefPtr<MediaByteBuffer> bytes =
+      resource->MediaReadAt(range.mStart, range.Length());
+    NS_ENSURE_TRUE_VOID(bytes);
+    mBufferedState->NotifyDataArrived(bytes->Elements(), aLength, aOffset);
+  }
 }
 
 int WebMReader::GetVideoCodec()
 {
   return mVideoCodec;
 }
 
 nsIntRect WebMReader::GetPicture()
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -336,23 +336,20 @@ nsPluginInstanceOwner::nsPluginInstanceO
   if (mPluginHost)
     mPluginHost->NewPluginNativeWindow(&mPluginWindow);
   else
     mPluginWindow = nullptr;
 
   mPluginFrame = nullptr;
   mWidgetCreationComplete = false;
 #ifdef XP_MACOSX
-  memset(&mCGPluginPortCopy, 0, sizeof(NP_CGContext));
-  mInCGPaintLevel = 0;
   mSentInitialTopLevelWindowEvent = false;
   mLastWindowIsActive = false;
   mLastContentFocused = false;
   mLastScaleFactor = 1.0;
-  mColorProfile = nullptr;
   mShouldBlurOnActivate = false;
 #endif
   mContentFocused = false;
   mWidgetVisible = true;
   mPluginWindowVisible = false;
   mPluginDocumentActiveState = true;
   mLastMouseDownButtonType = -1;
 
@@ -1203,120 +1200,23 @@ void nsPluginInstanceOwner::RemoveFromCA
       delete sCATimer;
       sCATimer = nullptr;
     }
     delete sCARefreshListeners;
     sCARefreshListeners = nullptr;
   }
 }
 
-void nsPluginInstanceOwner::RenderCoreAnimation(CGContextRef aCGContext,
-                                                int aWidth, int aHeight)
-{
-  if (aWidth == 0 || aHeight == 0)
-    return;
-
-  if (!mCARenderer) {
-    mCARenderer = new nsCARenderer();
-  }
-
-  // aWidth and aHeight are in "display pixels".  In non-HiDPI modes
-  // "display pixels" are device pixels.  But in HiDPI modes each
-  // display pixel corresponds to more than one device pixel.
-  double scaleFactor = 1.0;
-  GetContentsScaleFactor(&scaleFactor);
-
-  if (!mIOSurface ||
-      (mIOSurface->GetWidth() != (size_t)aWidth ||
-       mIOSurface->GetHeight() != (size_t)aHeight ||
-       mIOSurface->GetContentsScaleFactor() != scaleFactor)) {
-    mIOSurface = nullptr;
-
-    // If the renderer is backed by an IOSurface, resize it as required.
-    mIOSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, scaleFactor);
-    if (mIOSurface) {
-      RefPtr<MacIOSurface> attachSurface = MacIOSurface::LookupSurface(
-                                              mIOSurface->GetIOSurfaceID(),
-                                              scaleFactor);
-      if (attachSurface) {
-        mCARenderer->AttachIOSurface(attachSurface);
-      } else {
-        NS_ERROR("IOSurface attachment failed");
-        mIOSurface = nullptr;
-      }
-    }
-  }
-
-  if (!mColorProfile) {
-    mColorProfile = CreateSystemColorSpace();
-  }
-
-  if (mCARenderer->isInit() == false) {
-    void *caLayer = nullptr;
-    nsresult rv = mInstance->GetValueFromPlugin(NPPVpluginCoreAnimationLayer, &caLayer);
-    if (NS_FAILED(rv) || !caLayer) {
-      return;
-    }
-
-    // We don't run Flash in-process so we can unconditionally disallow
-    // the offliner renderer.
-    mCARenderer->SetupRenderer(caLayer, aWidth, aHeight, scaleFactor,
-                               DISALLOW_OFFLINE_RENDERER);
-
-    // Setting up the CALayer requires resetting the painting otherwise we
-    // get garbage for the first few frames.
-    FixUpPluginWindow(ePluginPaintDisable);
-    FixUpPluginWindow(ePluginPaintEnable);
-  }
-
-  CGImageRef caImage = nullptr;
-  nsresult rt = mCARenderer->Render(aWidth, aHeight, scaleFactor, &caImage);
-  if (rt == NS_OK && mIOSurface && mColorProfile) {
-    nsCARenderer::DrawSurfaceToCGContext(aCGContext, mIOSurface, mColorProfile,
-                                         0, 0, aWidth, aHeight);
-  } else if (rt == NS_OK && caImage != nullptr) {
-    // Significant speed up by resetting the scaling
-    ::CGContextSetInterpolationQuality(aCGContext, kCGInterpolationNone );
-    ::CGContextTranslateCTM(aCGContext, 0, (double) aHeight * scaleFactor);
-    ::CGContextScaleCTM(aCGContext, scaleFactor, -scaleFactor);
-
-    ::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage);
-  } else {
-    NS_NOTREACHED("nsCARenderer::Render failure");
-  }
-}
-
-void* nsPluginInstanceOwner::GetPluginPortCopy()
-{
-  if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
-      GetDrawingModel() == NPDrawingModelCoreAnimation ||
-      GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
-    return &mCGPluginPortCopy;
-  return nullptr;
-}
-
 void nsPluginInstanceOwner::SetPluginPort()
 {
   void* pluginPort = GetPluginPort();
   if (!pluginPort || !mPluginWindow)
     return;
   mPluginWindow->window = pluginPort;
 }
-
-void nsPluginInstanceOwner::BeginCGPaint()
-{
-  ++mInCGPaintLevel;
-}
-
-void nsPluginInstanceOwner::EndCGPaint()
-{
-  --mInCGPaintLevel;
-  NS_ASSERTION(mInCGPaintLevel >= 0, "Mismatched call to nsPluginInstanceOwner::EndCGPaint()!");
-}
-
 #endif
 
 // static
 uint32_t
 nsPluginInstanceOwner::GetEventloopNestingLevel()
 {
   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   uint32_t currentLevel = 0;
@@ -2539,18 +2439,16 @@ nsEventStatus nsPluginInstanceOwner::Pro
 
 nsresult
 nsPluginInstanceOwner::Destroy()
 {
   SetFrame(nullptr);
 
 #ifdef XP_MACOSX
   RemoveFromCARefreshTimer();
-  if (mColorProfile)
-    ::CGColorSpaceRelease(mColorProfile);
 #endif
 
   nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
 
   // unregister context menu listener
   if (mCXMenuListener) {
     mCXMenuListener->Destroy(content);
     mCXMenuListener = nullptr;
@@ -3131,21 +3029,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
 #ifdef XP_MACOSX
 
 void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState)
 {
   if (!mPluginWindow || !mInstance || !mPluginFrame) {
     return;
   }
 
-  // If we've already set up a CGContext in nsPluginFrame::PaintPlugin(), we
-  // don't want calls to SetPluginPort() to step on our work.
-  if (mInCGPaintLevel < 1) {
-    SetPluginPort();
-  }
+  SetPluginPort();
 
   nsIntSize widgetClip = mPluginFrame->GetWidgetlessClipRect().Size();
 
   mPluginWindow->x = 0;
   mPluginWindow->y = 0;
 
   NPRect oldClipRect = mPluginWindow->clipRect;
 
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -131,29 +131,19 @@ public:
   nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
   NPEventModel GetEventModel();
   static void CARefresh(nsITimer *aTimer, void *aClosure);
   void AddToCARefreshTimer();
   void RemoveFromCARefreshTimer();
   // This calls into the plugin (NPP_SetWindow) and can run script.
   void FixUpPluginWindow(int32_t inPaintState);
   void HidePluginWindow();
-  // Return a pointer to the internal nsPluginPort structure that's used to
-  // store a copy of plugin port info and to detect when it's been changed.
-  void* GetPluginPortCopy();
   // Set plugin port info in the plugin (in the 'window' member of the
   // NPWindow structure passed to the plugin by SetWindow()).
   void SetPluginPort();
-  // Flag when we've set up a Thebes (and CoreGraphics) context in
-  // nsPluginFrame::PaintPlugin().  We need to know this in
-  // FixUpPluginWindow() (i.e. we need to know when FixUpPluginWindow() has
-  // been called from nsPluginFrame::PaintPlugin() when we're using the
-  // CoreGraphics drawing model).
-  void BeginCGPaint();
-  void EndCGPaint();
 #else // XP_MACOSX
   void UpdateWindowPositionAndClipRect(bool aSetWindow);
   void UpdateWindowVisibility(bool aVisible);
 #endif // XP_MACOSX
 
   void UpdateDocumentActiveState(bool aIsActive);
 
   void SetFrame(nsPluginFrame *aFrame);
@@ -288,21 +278,16 @@ private:
   nsPluginFrame              *mPluginFrame;
   nsWeakPtr                   mContent; // WEAK, content owns us
   nsCString                   mDocumentBase;
   bool                        mWidgetCreationComplete;
   nsCOMPtr<nsIWidget>         mWidget;
   RefPtr<nsPluginHost>      mPluginHost;
   
 #ifdef XP_MACOSX
-  NP_CGContext                              mCGPluginPortCopy;
-  int32_t                                   mInCGPaintLevel;
-  RefPtr<MacIOSurface>             mIOSurface;
-  RefPtr<nsCARenderer>             mCARenderer;
-  CGColorSpaceRef                           mColorProfile;
   static nsCOMPtr<nsITimer>                *sCATimer;
   static nsTArray<nsPluginInstanceOwner*>  *sCARefreshListeners;
   bool                                      mSentInitialTopLevelWindowEvent;
   bool                                      mLastWindowIsActive;
   bool                                      mLastContentFocused;
   double                                    mLastScaleFactor;
   // True if, the next time the window is activated, we should blur ourselves.
   bool                                      mShouldBlurOnActivate;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -184,19 +184,16 @@ PluginInstanceChild::PluginInstanceChild
 #ifdef MOZ_WIDGET_GTK
     mWsInfo.display = nullptr;
     mXtClient.top_widget = nullptr;
 #else
     mWsInfo.display = DefaultXDisplay();
 #endif
 #endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
 #if defined(OS_WIN)
-    memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
-#endif // OS_WIN
-#if defined(OS_WIN)
     InitPopupMenuHook();
     if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
         SetUnityHooks();
     }
 #endif // OS_WIN
 }
 
 PluginInstanceChild::~PluginInstanceChild()
@@ -816,31 +813,16 @@ PluginInstanceChild::AnswerNPP_HandleEve
     NPEvent evcopy = event.event;
 #endif
 
 #ifdef OS_WIN
     // FIXME/bug 567645: temporarily drop the "dummy event" on the floor
     if (WM_NULL == evcopy.event)
         return true;
 
-    // Painting for win32. SharedSurfacePaint handles everything.
-    if (mWindow.type == NPWindowTypeDrawable) {
-       if (evcopy.event == WM_PAINT) {
-          *handled = SharedSurfacePaint(evcopy);
-          return true;
-       }
-       else if (DoublePassRenderingEvent() == evcopy.event) {
-            // We'll render to mSharedSurfaceDib first, then render to a cached bitmap
-            // we store locally. The two passes are for alpha extraction, so the second
-            // pass must be to a flat white surface in order for things to work.
-            mAlphaExtract.doublePass = RENDER_BACK_ONE;
-            *handled = true;
-            return true;
-       }
-    }
     *handled = WinlessHandleEvent(evcopy);
     return true;
 #endif
 
     // XXX A previous call to mPluginIface->event might block, e.g. right click
     // for context menu. Still, we might get here again, calling into the plugin
     // a second time while it's in the previous call.
     if (!mPluginIface->event)
@@ -1291,23 +1273,16 @@ PluginInstanceChild::AnswerNPP_SetWindow
                   NS_ASSERTION(mPluginWndProc != PluginWindowProc, "WTF?");
               }
               RemoveProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty);
               HookSetWindowLongPtr();
           }
       }
       break;
 
-      case NPWindowTypeDrawable:
-          mWindow.type = aWindow.type;
-          if (GetQuirks() & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)
-              SetupFlashMsgThrottle();
-          return SharedSurfaceSetWindow(aWindow);
-      break;
-
       default:
           NS_NOTREACHED("Bad plugin window type.");
           return false;
       break;
     }
 
 #elif defined(XP_MACOSX)
 
@@ -2019,197 +1994,16 @@ PluginInstanceChild::WinlessHandleEvent(
 
     if (IsWindow(focusHwnd)) {
       SetFocus(focusHwnd);
     }
 
     return handled;
 }
 
-/* windowless drawing helpers */
-
-bool
-PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow)
-{
-    // If the surfaceHandle is empty, parent is telling us we can reuse our cached
-    // memory surface and hdc. Otherwise, we need to reset, usually due to a
-    // expanding plugin port size.
-    if (!aWindow.surfaceHandle) {
-        if (!mSharedSurfaceDib.IsValid()) {
-            return false;
-        }
-    }
-    else {
-        // Attach to the new shared surface parent handed us.
-        if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle,
-                                               aWindow.width, aWindow.height, false)))
-          return false;
-        // Free any alpha extraction resources if needed. This will be reset
-        // the next time it's used.
-        AlphaExtractCacheRelease();
-    }
-      
-    // NPRemoteWindow's origin is the origin of our shared dib.
-    mWindow.x      = aWindow.x;
-    mWindow.y      = aWindow.y;
-    mWindow.width  = aWindow.width;
-    mWindow.height = aWindow.height;
-    mWindow.type   = aWindow.type;
-
-    mWindow.window = reinterpret_cast<void*>(mSharedSurfaceDib.GetHDC());
-    ::SetViewportOrgEx(mSharedSurfaceDib.GetHDC(),
-                       -aWindow.x, -aWindow.y, nullptr);
-
-    if (mPluginIface->setwindow)
-        mPluginIface->setwindow(&mData, &mWindow);
-
-    return true;
-}
-
-void
-PluginInstanceChild::SharedSurfaceRelease()
-{
-    mSharedSurfaceDib.Close();
-    AlphaExtractCacheRelease();
-}
-
-/* double pass cache buffer - (rarely) used in cases where alpha extraction
- * occurs for windowless plugins. */
- 
-bool
-PluginInstanceChild::AlphaExtractCacheSetup()
-{
-    AlphaExtractCacheRelease();
-
-    mAlphaExtract.hdc = ::CreateCompatibleDC(nullptr);
-
-    if (!mAlphaExtract.hdc)
-        return false;
-
-    BITMAPINFOHEADER bmih;
-    memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER));
-    bmih.biSize        = sizeof(BITMAPINFOHEADER);
-    bmih.biWidth       = mWindow.width;
-    bmih.biHeight      = mWindow.height;
-    bmih.biPlanes      = 1;
-    bmih.biBitCount    = 32;
-    bmih.biCompression = BI_RGB;
-
-    void* ppvBits = nullptr;
-    mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc,
-                                           (BITMAPINFO*)&bmih,
-                                           DIB_RGB_COLORS,
-                                           (void**)&ppvBits,
-                                           nullptr,
-                                           (unsigned long)sizeof(BITMAPINFOHEADER));
-    if (!mAlphaExtract.bmp)
-      return false;
-
-    DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp));
-    return true;
-}
-
-void
-PluginInstanceChild::AlphaExtractCacheRelease()
-{
-    if (mAlphaExtract.bmp)
-        ::DeleteObject(mAlphaExtract.bmp);
-
-    if (mAlphaExtract.hdc)
-        ::DeleteObject(mAlphaExtract.hdc);
-
-    mAlphaExtract.bmp = nullptr;
-    mAlphaExtract.hdc = nullptr;
-}
-
-void
-PluginInstanceChild::UpdatePaintClipRect(RECT* aRect)
-{
-    if (aRect) {
-        // Update the clip rect on our internal hdc
-        HRGN clip = ::CreateRectRgnIndirect(aRect);
-        ::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
-        ::DeleteObject(clip);
-    }
-}
-
-int16_t
-PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
-{
-    if (!mPluginIface->event)
-        return false;
-
-    RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
-
-    switch(mAlphaExtract.doublePass) {
-        case RENDER_NATIVE:
-            // pass the internal hdc to the plugin
-            UpdatePaintClipRect(pRect);
-            evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
-            return mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
-        break;
-        case RENDER_BACK_ONE:
-              // Handle a double pass render used in alpha extraction for transparent
-              // plugins. (See nsPluginFrame and gfxWindowsNativeDrawing for details.)
-              // We render twice, once to the shared dib, and once to a cache which
-              // we copy back on a second paint. These paints can't be spread across
-              // multiple rpc messages as delays cause animation frame changes.
-              if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) {
-                  mAlphaExtract.doublePass = RENDER_NATIVE;
-                  return false;
-              }
-
-              // See gfxWindowsNativeDrawing, color order doesn't have to match.
-              UpdatePaintClipRect(pRect);
-              ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
-              evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
-              if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
-                  mAlphaExtract.doublePass = RENDER_NATIVE;
-                  return false;
-              }
-
-              // Copy to cache. We render to shared dib so we don't have to call
-              // setwindow between calls (flash issue).  
-              ::BitBlt(mAlphaExtract.hdc,
-                       pRect->left,
-                       pRect->top,
-                       pRect->right - pRect->left,
-                       pRect->bottom - pRect->top,
-                       mSharedSurfaceDib.GetHDC(),
-                       pRect->left,
-                       pRect->top,
-                       SRCCOPY);
-
-              ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
-              if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
-                  mAlphaExtract.doublePass = RENDER_NATIVE;
-                  return false;
-              }
-              mAlphaExtract.doublePass = RENDER_BACK_TWO;
-              return true;
-        break;
-        case RENDER_BACK_TWO:
-              // copy our cached surface back
-              UpdatePaintClipRect(pRect);
-              ::BitBlt(mSharedSurfaceDib.GetHDC(),
-                       pRect->left,
-                       pRect->top,
-                       pRect->right - pRect->left,
-                       pRect->bottom - pRect->top,
-                       mAlphaExtract.hdc,
-                       pRect->left,
-                       pRect->top,
-                       SRCCOPY);
-              mAlphaExtract.doublePass = RENDER_NATIVE;
-              return true;
-        break;
-    }
-    return false;
-}
-
 /* flash msg throttling helpers */
 
 // Flash has the unfortunate habit of flooding dispatch loops with custom
 // windowing events they use for timing. We throttle these by dropping the
 // delivery priority below any other event, including pending ipc io
 // notifications. We do this for both windowed and windowless controls.
 // Note flash's windowless msg window can last longer than our instance,
 // so we try to unhook when the window is destroyed and in NPP_Destroy.
@@ -4044,17 +3838,16 @@ PluginInstanceChild::Destroy()
     DeleteObjects(*mDeletingHash);
 
     // Null out our cached actors as they should have been killed in the
     // PluginInstanceDestroyed call above.
     mCachedWindowActor = nullptr;
     mCachedElementActor = nullptr;
 
 #if defined(OS_WIN)
-    SharedSurfaceRelease();
     DestroyWinlessPopupSurrogate();
     UnhookWinlessFlashThrottle();
     DestroyPluginWindow();
 #endif
 
     // Pending async calls are discarded, not delivered. This matches the
     // in-process behavior.
     for (uint32_t i = 0; i < mPendingAsyncCalls.Length(); ++i)
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -422,39 +422,16 @@ private:
 
     /**
      * During destruction we enumerate all remaining scriptable objects and
      * invalidate/delete them. Enumeration can re-enter, so maintain a
      * hash separate from PluginModuleChild.mObjectMap.
      */
     nsAutoPtr< nsTHashtable<DeletingObjectEntry> > mDeletingHash;
 
-#if defined(OS_WIN)
-private:
-    // Shared dib rendering management for windowless plugins.
-    bool SharedSurfaceSetWindow(const NPRemoteWindow& aWindow);
-    int16_t SharedSurfacePaint(NPEvent& evcopy);
-    void SharedSurfaceRelease();
-    bool AlphaExtractCacheSetup();
-    void AlphaExtractCacheRelease();
-    void UpdatePaintClipRect(RECT* aRect);
-
-private:
-    enum {
-      RENDER_NATIVE,
-      RENDER_BACK_ONE,
-      RENDER_BACK_TWO 
-    };
-    gfx::SharedDIBWin mSharedSurfaceDib;
-    struct {
-      uint16_t        doublePass;
-      HDC             hdc;
-      HBITMAP         bmp;
-    } mAlphaExtract;
-#endif // defined(OS_WIN)
 #if defined(MOZ_WIDGET_COCOA)
 private:
 #if defined(__i386__)
     NPEventModel                  mEventModel;
 #endif
     CGColorSpaceRef               mShColorSpace;
     CGContextRef                  mShContext;
     RefPtr<nsCARenderer> mCARenderer;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -171,17 +171,16 @@ PluginInstanceParent::InitMetadata(const
 
 void
 PluginInstanceParent::ActorDestroy(ActorDestroyReason why)
 {
 #if defined(OS_WIN)
     if (why == AbnormalShutdown) {
         // If the plugin process crashes, this is the only
         // chance we get to destroy resources.
-        SharedSurfaceRelease();
         UnsubclassPluginWindow();
     }
 #endif
     if (mFrontSurface) {
         mFrontSurface = nullptr;
         if (mImageContainer) {
             mImageContainer->ClearAllImages();
         }
@@ -199,17 +198,16 @@ PluginInstanceParent::Destroy()
         Telemetry::AutoTimer<Telemetry::BLOCKED_ON_PLUGIN_INSTANCE_DESTROY_MS>
             timer(Module()->GetHistogramKey());
         if (!CallNPP_Destroy(&retval)) {
             retval = NPERR_GENERIC_ERROR;
         }
     }
 
 #if defined(OS_WIN)
-    SharedSurfaceRelease();
     UnsubclassPluginWindow();
 #endif
 
     return retval;
 }
 
 PBrowserStreamParent*
 PluginInstanceParent::AllocPBrowserStreamParent(const nsCString& url,
@@ -944,21 +942,16 @@ PluginInstanceParent::NPP_SetWindow(cons
     NS_ENSURE_TRUE(aWindow, NPERR_GENERIC_ERROR);
 
     NPRemoteWindow window;
     mWindowType = aWindow->type;
 
 #if defined(OS_WIN)
     // On windowless controls, reset the shared memory surface as needed.
     if (mWindowType == NPWindowTypeDrawable) {
-        // SharedSurfaceSetWindow will take care of NPRemoteWindow.
-        if (!SharedSurfaceSetWindow(aWindow, window)) {
-          return NPERR_OUT_OF_MEMORY_ERROR;
-        }
-
         MaybeCreateChildPopupSurrogate();
     } else {
         SubclassPluginWindow(reinterpret_cast<HWND>(aWindow->window));
 
         // Skip SetWindow call for hidden QuickTime plugins.
         if ((mParent->GetQuirks() & QUIRK_QUICKTIME_AVOID_SETWINDOW) &&
             aWindow->width == 0 && aWindow->height == 0) {
             return NPERR_NO_ERROR;
@@ -1183,33 +1176,17 @@ PluginInstanceParent::NPP_HandleEvent(vo
     double scaleFactor = 1.0;
     mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
     npremoteevent.contentsScaleFactor = scaleFactor;
 #endif
     int16_t handled = 0;
 
 #if defined(OS_WIN)
     if (mWindowType == NPWindowTypeDrawable) {
-        if (DoublePassRenderingEvent() == npevent->event) {
-            return CallPaint(npremoteevent, &handled) && handled;
-        }
-
         switch (npevent->event) {
-            case WM_PAINT:
-            {
-                RECT rect;
-                SharedSurfaceBeforePaint(rect, npremoteevent);
-                if (!CallPaint(npremoteevent, &handled)) {
-                    handled = false;
-                }
-                SharedSurfaceAfterPaint(npevent);
-                return handled;
-            }
-            break;
-
             case WM_KILLFOCUS:
             {
               // When the user selects fullscreen mode in Flash video players,
               // WM_KILLFOCUS will be delayed by deferred event processing:
               // WM_LBUTTONUP results in a call to CreateWindow within Flash,
               // which fires WM_KILLFOCUS. Delayed delivery causes Flash to
               // misinterpret the event, dropping back out of fullscreen. Trap
               // this event and drop it.
@@ -1931,118 +1908,16 @@ PluginInstanceParent::UnsubclassPluginWi
  * setwindow: origin is relative to container
  * WM_PAINT: origin is relative to container
  *
  * PluginInstanceParent:
  *
  * painting: mPluginPort (nsIntRect, saved in SetWindow)
  */
 
-void
-PluginInstanceParent::SharedSurfaceRelease()
-{
-    mSharedSurfaceDib.Close();
-}
-
-bool
-PluginInstanceParent::SharedSurfaceSetWindow(const NPWindow* aWindow,
-                                             NPRemoteWindow& aRemoteWindow)
-{
-    aRemoteWindow.window = 0;
-    aRemoteWindow.x      = aWindow->x;
-    aRemoteWindow.y      = aWindow->y;
-    aRemoteWindow.width  = aWindow->width;
-    aRemoteWindow.height = aWindow->height;
-    aRemoteWindow.type   = aWindow->type;
-
-    nsIntRect newPort(aWindow->x, aWindow->y, aWindow->width, aWindow->height);
-
-    // save the the rect location within the browser window.
-    mPluginPort = newPort;
-
-    // move the port to our shared surface origin
-    newPort.MoveTo(0,0);
-
-    // check to see if we have the room in shared surface
-    if (mSharedSurfaceDib.IsValid() && mSharedSize.Contains(newPort)) {
-      // ok to paint
-      aRemoteWindow.surfaceHandle = 0;
-      return true;
-    }
-
-    // allocate a new shared surface
-    SharedSurfaceRelease();
-    if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(aWindow->window),
-                                           newPort.width, newPort.height, false)))
-      return false;
-
-    // save the new shared surface size we just allocated
-    mSharedSize = newPort;
-
-    base::SharedMemoryHandle handle;
-    if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(OtherPid(), &handle))) {
-      return false;
-    }
-
-    aRemoteWindow.surfaceHandle = handle;
-
-    return true;
-}
-
-void
-PluginInstanceParent::SharedSurfaceBeforePaint(RECT& rect,
-                                               NPRemoteEvent& npremoteevent)
-{
-    RECT* dr = (RECT*)npremoteevent.event.lParam;
-    HDC parentHdc = (HDC)npremoteevent.event.wParam;
-
-    nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top);
-    dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); // should always be smaller than dirtyRect
-
-    ::BitBlt(mSharedSurfaceDib.GetHDC(),
-             dirtyRect.x,
-             dirtyRect.y,
-             dirtyRect.width,
-             dirtyRect.height,
-             parentHdc,
-             dr->left,
-             dr->top,
-             SRCCOPY);
-
-    // setup the translated dirty rect we'll send to the child
-    rect.left   = dirtyRect.x;
-    rect.top    = dirtyRect.y;
-    rect.right  = dirtyRect.x + dirtyRect.width;
-    rect.bottom = dirtyRect.y + dirtyRect.height;
-
-    npremoteevent.event.wParam = WPARAM(0);
-    npremoteevent.event.lParam = LPARAM(&rect);
-}
-
-void
-PluginInstanceParent::SharedSurfaceAfterPaint(NPEvent* npevent)
-{
-    RECT* dr = (RECT*)npevent->lParam;
-    HDC parentHdc = (HDC)npevent->wParam;
-
-    nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top);
-    dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y);
-
-    // src copy the shared dib into the parent surface we are handed.
-    ::BitBlt(parentHdc,
-             dr->left,
-             dr->top,
-             dirtyRect.width,
-             dirtyRect.height,
-             mSharedSurfaceDib.GetHDC(),
-             dirtyRect.x,
-             dirtyRect.y,
-             SRCCOPY);
-}
-
 bool
 PluginInstanceParent::MaybeCreateAndParentChildPluginWindow()
 {
     // On Windows we need to create and set the parent before we set the
     // window on the plugin, or keyboard interaction will not work.
     if (!mChildPluginHWND) {
         if (!CallCreateChildPluginWindow(&mChildPluginHWND) ||
             !mChildPluginHWND) {
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -348,32 +348,26 @@ private:
     bool mIsWhitelistedForShumway;
     NPWindowType mWindowType;
     int16_t            mDrawingModel;
 
     nsDataHashtable<nsPtrHashKey<NPObject>, PluginScriptableObjectParent*> mScriptableObjects;
 
 #if defined(OS_WIN)
 private:
-    // Used in rendering windowless plugins in other processes.
-    bool SharedSurfaceSetWindow(const NPWindow* aWindow, NPRemoteWindow& aRemoteWindow);
-    void SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent);
-    void SharedSurfaceAfterPaint(NPEvent* npevent);
-    void SharedSurfaceRelease();
     // Used in handling parent/child forwarding of events.
     static LRESULT CALLBACK PluginWindowHookProc(HWND hWnd, UINT message,
                                                  WPARAM wParam, LPARAM lParam);
     void SubclassPluginWindow(HWND aWnd);
     void UnsubclassPluginWindow();
 
     bool MaybeCreateAndParentChildPluginWindow();
     void MaybeCreateChildPopupSurrogate();
 
 private:
-    gfx::SharedDIBWin  mSharedSurfaceDib;
     nsIntRect          mPluginPort;
     nsIntRect          mSharedSize;
     HWND               mPluginHWND;
     // This is used for the normal child plugin HWND for windowed plugins and,
     // if needed, also the child popup surrogate HWND for windowless plugins.
     HWND               mChildPluginHWND;
     HWND               mChildPluginsParentHWND;
     WNDPROC            mPluginWndProc;
--- a/dom/plugins/ipc/PluginMessageUtils.cpp
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -49,19 +49,16 @@ namespace mozilla {
 namespace plugins {
 
 NPRemoteWindow::NPRemoteWindow() :
   window(0), x(0), y(0), width(0), height(0), type(NPWindowTypeDrawable)
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
   , visualID(0)
   , colormap(0)
 #endif /* XP_UNIX */
-#if defined(XP_WIN)
-  ,surfaceHandle(0)
-#endif
 #if defined(XP_MACOSX)
   ,contentsScaleFactor(1.0)
 #endif
 {
   clipRect.top = 0;
   clipRect.left = 0;
   clipRect.bottom = 0;
   clipRect.right = 0;
@@ -151,23 +148,10 @@ void DeferNPVariantLastRelease(const NPN
   if (!NPVARIANT_IS_OBJECT(*v)) {
     f->releasevariantvalue(v);
     return;
   }
   DeferNPObjectLastRelease(f, v->value.objectValue);
   VOID_TO_NPVARIANT(*v);
 }
 
-#ifdef XP_WIN
-
-// The private event used for double-pass widgetless plugin rendering.
-UINT DoublePassRenderingEvent()
-{
-  static UINT gEventID = 0;
-  if (!gEventID)
-    gEventID = ::RegisterWindowMessage(L"MozDoublePassMsg");
-  return gEventID;
-}
-
-#endif
-
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -88,19 +88,16 @@ struct NPRemoteWindow
   uint32_t width;
   uint32_t height;
   NPRect clipRect;
   NPWindowType type;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
   VisualID visualID;
   Colormap colormap;
 #endif /* XP_UNIX */
-#if defined(XP_WIN)
-  base::SharedMemoryHandle surfaceHandle;
-#endif
 #if defined(XP_MACOSX)
   double contentsScaleFactor;
 #endif
 };
 
 #ifdef XP_WIN
 typedef HWND NativeWindowHandle;
 #elif defined(MOZ_X11)
@@ -249,21 +246,16 @@ struct DeletingObjectEntry : public nsPt
   explicit DeletingObjectEntry(const NPObject* key)
     : nsPtrHashKey<NPObject>(key)
     , mDeleted(false)
   { }
 
   bool mDeleted;
 };
 
-#ifdef XP_WIN
-// The private event used for double-pass widgetless plugin rendering.
-UINT DoublePassRenderingEvent();
-#endif
-
 } /* namespace plugins */
 
 } /* namespace mozilla */
 
 namespace IPC {
 
 template <>
 struct ParamTraits<NPRect>
@@ -340,19 +332,16 @@ struct ParamTraits<mozilla::plugins::NPR
     WriteParam(aMsg, aParam.width);
     WriteParam(aMsg, aParam.height);
     WriteParam(aMsg, aParam.clipRect);
     WriteParam(aMsg, aParam.type);
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     aMsg->WriteULong(aParam.visualID);
     aMsg->WriteULong(aParam.colormap);
 #endif
-#if defined(XP_WIN)
-    WriteParam(aMsg, aParam.surfaceHandle);
-#endif
 #if defined(XP_MACOSX)
     aMsg->WriteDouble(aParam.contentsScaleFactor);
 #endif
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     uint64_t window;
@@ -372,22 +361,16 @@ struct ParamTraits<mozilla::plugins::NPR
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     unsigned long visualID;
     unsigned long colormap;
     if (!(aMsg->ReadULong(aIter, &visualID) &&
           aMsg->ReadULong(aIter, &colormap)))
       return false;
 #endif
 
-#if defined(XP_WIN)
-    base::SharedMemoryHandle surfaceHandle;
-    if (!ReadParam(aMsg, aIter, &surfaceHandle))
-      return false;
-#endif
-
 #if defined(XP_MACOSX)
     double contentsScaleFactor;
     if (!aMsg->ReadDouble(aIter, &contentsScaleFactor))
       return false;
 #endif
 
     aResult->window = window;
     aResult->x = x;
@@ -395,19 +378,16 @@ struct ParamTraits<mozilla::plugins::NPR
     aResult->width = width;
     aResult->height = height;
     aResult->clipRect = clipRect;
     aResult->type = type;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     aResult->visualID = visualID;
     aResult->colormap = colormap;
 #endif
-#if defined(XP_WIN)
-    aResult->surfaceHandle = surfaceHandle;
-#endif
 #if defined(XP_MACOSX)
     aResult->contentsScaleFactor = contentsScaleFactor;
 #endif
     return true;
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -38,20 +38,20 @@ support-files =
 [test_bug854082.html]
 [test_bug863792.html]
 [test_bug967694.html]
 [test_bug985859.html]
 [test_bug986930.html]
 [test_bug1092842.html]
 skip-if = e10s
 [test_cocoa_focus.html]
-skip-if = toolkit != "cocoa"
+skip-if = toolkit != "cocoa" || e10s # Bug 1194534
 support-files = cocoa_focus.html
 [test_cocoa_window_focus.html]
-skip-if = toolkit != "cocoa"
+skip-if = toolkit != "cocoa" || e10s # Bug 1194534
 support-files = cocoa_window_focus.html
 [test_cookies.html]
 [test_copyText.html]
 skip-if = (toolkit != "gtk2") && (toolkit != "gtk3")
 [test_crash_nested_loop.html]
 skip-if = (toolkit != "gtk2") && (toolkit != "gtk3")
 [test_crashing.html]
 skip-if = !crashreporter
--- a/dom/push/PushServiceWebSocket.jsm
+++ b/dom/push/PushServiceWebSocket.jsm
@@ -1122,19 +1122,16 @@ this.PushServiceWebSocket = {
     this._releaseWakeLock();
 
     if (this._currentState != STATE_WAITING_FOR_WS_START) {
       debug("NOT in STATE_WAITING_FOR_WS_START. Current state " +
             this._currentState + ". Skipping");
       return;
     }
 
-    // Since we've had a successful connection reset the retry fail count.
-    this._retryFailCount = 0;
-
     let data = {
       messageType: "hello",
       use_webpush: true,
     };
 
     if (this._UAID) {
       data.uaid = this._UAID;
     }
@@ -1191,22 +1188,20 @@ this.PushServiceWebSocket = {
     let reply;
     try {
       reply = JSON.parse(message);
     } catch(e) {
       debug("Parsing JSON failed. text : " + message);
       return;
     }
 
-    // If we are not waiting for a hello message, reset the retry fail count
-    if (this._currentState != STATE_WAITING_FOR_HELLO) {
-      debug('Reseting _retryFailCount and _pingIntervalRetryTimes');
-      this._retryFailCount = 0;
-      this._pingIntervalRetryTimes = {};
-    }
+    // If we receive a message, we know the connection succeeded. Reset the
+    // connection attempt and ping interval counters.
+    this._retryFailCount = 0;
+    this._pingIntervalRetryTimes = {};
 
     let doNotHandle = false;
     if ((message === '{}') ||
         (reply.messageType === undefined) ||
         (reply.messageType === "ping") ||
         (typeof reply.messageType != "string")) {
       debug('Pong received');
       this._calculateAdaptivePing(false);
--- a/dom/push/test/xpcshell/head.js
+++ b/dom/push/test/xpcshell/head.js
@@ -373,17 +373,21 @@ MockWebSocket.prototype = {
   serverClose(statusCode, reason = '') {
     if (!isFinite(statusCode)) {
       statusCode = WEBSOCKET_CLOSE_GOING_AWAY;
     }
     waterfall(
       () => this._listener.onServerClose(this._context, statusCode, reason),
       () => this._listener.onStop(this._context, Cr.NS_BASE_STREAM_CLOSED)
     );
-  }
+  },
+
+  serverInterrupt(result = Cr.NS_ERROR_NET_RESET) {
+    waterfall(() => this._listener.onStop(this._context, result));
+  },
 };
 
 /**
  * Creates an object that exposes the same interface as NetworkInfo, used
  * to simulate network status changes on Desktop. All methods returns empty
  * carrier data.
  */
 function MockDesktopNetworkInfo() {}
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_retry_ws.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
+
+const userAgentID = '05f7b940-51b6-4b6f-8032-b83ebb577ded';
+
+function run_test() {
+  do_get_profile();
+  setPrefs({
+    userAgentID: userAgentID,
+    pingInterval: 10000,
+    retryBaseInterval: 25,
+  });
+  run_next_test();
+}
+
+add_task(function* test_ws_retry() {
+  let db = PushServiceWebSocket.newPushDB();
+  do_register_cleanup(() => {return db.drop().then(_ => db.close());});
+
+  yield db.put({
+    channelID: '61770ba9-2d57-4134-b949-d40404630d5b',
+    pushEndpoint: 'https://example.org/push/1',
+    scope: 'https://example.net/push/1',
+    version: 1,
+    originAttributes: '',
+    quota: Infinity,
+  });
+
+  let alarmDelays = [];
+  let setAlarm = PushService.setAlarm;
+  PushService.setAlarm = function(delay) {
+    alarmDelays.push(delay);
+    setAlarm.apply(this, arguments);
+  };
+
+  let handshakeDone;
+  let handshakePromise = new Promise(resolve => handshakeDone = resolve);
+  PushService.init({
+    serverURI: "wss://push.example.org/",
+    networkInfo: new MockDesktopNetworkInfo(),
+    makeWebSocket(uri) {
+      return new MockWebSocket(uri, {
+        onHello(request) {
+          if (alarmDelays.length == 10) {
+            PushService.setAlarm = setAlarm;
+            this.serverSendMsg(JSON.stringify({
+              messageType: 'hello',
+              status: 200,
+              uaid: userAgentID,
+            }));
+            handshakeDone();
+            return;
+          }
+          this.serverInterrupt();
+        },
+      });
+    },
+  });
+
+  yield waitForPromise(
+    handshakePromise,
+    45000,
+    'Timed out waiting for successful handshake'
+  );
+  deepEqual(alarmDelays, [25, 50, 100, 200, 400, 800, 1600, 3200, 6400, 10000],
+    'Wrong reconnect alarm delays');
+});
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -35,16 +35,17 @@ run-sequentially = This will delete all 
 [test_unregister_empty_scope.js]
 [test_unregister_error.js]
 [test_unregister_invalid_json.js]
 [test_unregister_not_found.js]
 [test_unregister_success.js]
 [test_webapps_cleardata.js]
 [test_updateRecordNoEncryptionKeys_ws.js]
 [test_reconnect_retry.js]
+[test_retry_ws.js]
 #http2 test
 [test_resubscribe_4xxCode_http2.js]
 [test_resubscribe_5xxCode_http2.js]
 [test_resubscribe_listening_for_msg_error_http2.js]
 [test_register_5xxCode_http2.js]
 [test_updateRecordNoEncryptionKeys_http2.js]
 [test_register_success_http2.js]
 skip-if = !hasNode
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1244,17 +1244,17 @@ public:
    * drawing is distributed over number of tiles which may each hold an
    * individual offset. The tiles in the set must each have the same backend
    * and format.
    */
   static already_AddRefed<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
 
   static bool DoesBackendSupportDataDrawtarget(BackendType aType);
 
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
   static already_AddRefed<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize);
   static already_AddRefed<GlyphRenderingOptions>
     CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
 #endif
 
 #ifdef WIN32
   static already_AddRefed<DrawTarget> CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   static already_AddRefed<DrawTarget>
--- a/gfx/2d/BorrowedContext.h
+++ b/gfx/2d/BorrowedContext.h
@@ -132,17 +132,17 @@ private:
   Display *mDisplay;
   Drawable mDrawable;
   Screen *mScreen;
   Visual *mVisual;
   XRenderPictFormat *mXRenderFormat;
 };
 #endif
 
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
 /* This is a helper class that let's you borrow a CGContextRef from a
  * DrawTargetCG. This is used for drawing themed widgets.
  *
  * Callers should check the cg member after constructing the object
  * to see if it succeeded. The DrawTarget should not be used while
  * the context is borrowed. */
 class BorrowedCGContext
 {
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include <dlfcn.h>
 #include "BorrowedContext.h"
 #include "DataSurfaceHelpers.h"
 #include "DrawTargetCG.h"
 #include "Logging.h"
 #include "SourceSurfaceCG.h"
 #include "Rect.h"
 #include "ScaledFontMac.h"
 #include "Tools.h"
@@ -172,32 +173,38 @@ DrawTargetCG::GetType() const
 {
   return GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED ?
            DrawTargetType::HARDWARE_RASTER : DrawTargetType::SOFTWARE_RASTER;
 }
 
 BackendType
 DrawTargetCG::GetBackendType() const
 {
+#ifdef MOZ_WIDGET_COCOA
   // It may be worth spliting Bitmap and IOSurface DrawTarget
   // into seperate classes.
   if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
     return BackendType::COREGRAPHICS_ACCELERATED;
   } else {
     return BackendType::COREGRAPHICS;
   }
+#else
+  return BackendType::COREGRAPHICS;
+#endif
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetCG::Snapshot()
 {
   if (!mSnapshot) {
+#ifdef MOZ_WIDGET_COCOA
     if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
       return MakeAndAddRef<SourceSurfaceCGIOSurfaceContext>(this);
     }
+#endif
     Flush();
     mSnapshot = new SourceSurfaceCGBitmapContext(this);
   }
 
   RefPtr<SourceSurface> snapshot(mSnapshot);
   return snapshot.forget();
 }
 
@@ -1712,22 +1719,24 @@ DrawTargetCG::Init(BackendType aType,
     static_assert(sizeof(decltype(mData[0])) == 1,
                   "mData.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
     mData.Realloc(/* actually an object count */ bufLen, true);
     aData = static_cast<unsigned char*>(mData);
   }
 
   mSize = aSize;
 
+#ifdef MOZ_WIDGET_COCOA
   if (aType == BackendType::COREGRAPHICS_ACCELERATED) {
     RefPtr<MacIOSurface> ioSurface = MacIOSurface::CreateIOSurface(aSize.width, aSize.height);
     mCg = ioSurface->CreateIOSurfaceContext();
     // If we don't have the symbol for 'CreateIOSurfaceContext' mCg will be null
     // and we will fallback to software below
   }
+#endif
 
   mFormat = SurfaceFormat::B8G8R8A8;
 
   if (!mCg || aType == BackendType::COREGRAPHICS) {
     int bitsPerComponent = 8;
 
     CGBitmapInfo bitinfo;
     if (aFormat == SurfaceFormat::A8) {
@@ -1815,31 +1824,35 @@ EnsureValidPremultipliedData(CGContextRe
       }
     }
   }
 }
 
 void
 DrawTargetCG::Flush()
 {
+#ifdef MOZ_WIDGET_COCOA
   if (GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE) {
     CGContextFlush(mCg);
   } else if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP &&
              mMayContainInvalidPremultipliedData) {
     // We can't guarantee that all our users can handle pixel data where an RGB
     // component value exceeds the pixel's alpha value. In particular, the
     // color conversion that CG does when we draw a CGImage snapshot of this
     // context into a context that has a different color space throws up on
     // invalid premultiplied data and creates completely wrong colors.
     // Sanitizing the data means that we lose some of the fake component alpha
     // behavior that font rendering tries to give us, but the result still
     // looks good enough to prefer it over grayscale font anti-aliasing.
     EnsureValidPremultipliedData(mCg);
     mMayContainInvalidPremultipliedData = false;
   }
+#else
+  //TODO
+#endif
 }
 
 bool
 DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
 {
   // XXX: we should come up with some consistent semantics for dealing
   // with zero area drawtargets
   if (aSize.width == 0 || aSize.height == 0) {
@@ -1869,26 +1882,30 @@ DrawTargetCG::Init(CGContextRef cgContex
   // DrawTargetCG around a CGContextRef from a cairo quartz surface which
   // already has it's origin adjusted.
   //
   // CGContextTranslateCTM(mCg, 0, mSize.height);
   // CGContextScaleCTM(mCg, 1, -1);
   mOriginalTransform = CGContextGetCTM(mCg);
 
   mFormat = SurfaceFormat::B8G8R8A8;
+#ifdef MOZ_WIDGET_COCOA
   if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) {
+#endif
     CGColorSpaceRef colorspace;
     CGBitmapInfo bitinfo = CGBitmapContextGetBitmapInfo(mCg);
     colorspace = CGBitmapContextGetColorSpace (mCg);
     if (CGColorSpaceGetNumberOfComponents(colorspace) == 1) {
       mFormat = SurfaceFormat::A8;
     } else if ((bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst) {
       mFormat = SurfaceFormat::B8G8R8X8;
     }
+#ifdef MOZ_WIDGET_COCOA
   }
+#endif
 
   return true;
 }
 
 bool
 DrawTargetCG::Init(BackendType aType, const IntSize &aSize, SurfaceFormat &aFormat)
 {
   int32_t stride = GetAlignedStride<16>(aSize.width * BytesPerPixel(aFormat));
@@ -1901,22 +1918,26 @@ already_AddRefed<PathBuilder>
 DrawTargetCG::CreatePathBuilder(FillRule aFillRule) const
 {
   return MakeAndAddRef<PathBuilderCG>(aFillRule);
 }
 
 void*
 DrawTargetCG::GetNativeSurface(NativeSurfaceType aType)
 {
+#ifdef MOZ_WIDGET_COCOA
   if ((aType == NativeSurfaceType::CGCONTEXT && GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) ||
       (aType == NativeSurfaceType::CGCONTEXT_ACCELERATED && GetContextType(mCg) == CG_CONTEXT_TYPE_IOSURFACE)) {
     return mCg;
   } else {
     return nullptr;
   }
+#else
+  return mCg;
+#endif
 }
 
 void
 DrawTargetCG::Mask(const Pattern &aSource,
                    const Pattern &aMask,
                    const DrawOptions &aDrawOptions)
 {
   MarkChanged();
--- a/gfx/2d/DrawTargetCG.h
+++ b/gfx/2d/DrawTargetCG.h
@@ -1,17 +1,24 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_gfx_DrawTargetCG_h
 #define mozilla_gfx_DrawTargetCG_h
 
+#ifdef MOZ_WIDGET_COCOA
 #include <ApplicationServices/ApplicationServices.h>
+#import <OpenGL/OpenGL.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#include <OpenGLES/ES2/gl.h>
+#include <OpenGLES/ES2/glext.h>
+#endif
 
 #include "2D.h"
 #include "Rect.h"
 #include "PathCG.h"
 #include "SourceSurfaceCG.h"
 #include "GLDefs.h"
 #include "Tools.h"
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -19,18 +19,20 @@
 #include <string.h>
 
 #include "Blur.h"
 #include "Logging.h"
 #include "Tools.h"
 
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
 #include "cairo-quartz.h"
+#ifdef MOZ_WIDGET_COCOA
 #include <ApplicationServices/ApplicationServices.h>
 #endif
+#endif
 
 #ifdef CAIRO_HAS_XLIB_SURFACE
 #include "cairo-xlib.h"
 #include "cairo-xlib-xrender.h"
 #endif
 
 #ifdef CAIRO_HAS_WIN32_SURFACE
 #include "cairo-win32.h"
@@ -664,17 +666,17 @@ GfxFormatForCairoSurface(cairo_surface_t
   cairo_surface_type_t type = cairo_surface_get_type(surface);
   if (type == CAIRO_SURFACE_TYPE_IMAGE) {
     return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface));
   }
 #ifdef CAIRO_HAS_XLIB_SURFACE
   // xlib is currently the only Cairo backend that creates 16bpp surfaces
   if (type == CAIRO_SURFACE_TYPE_XLIB &&
       cairo_xlib_surface_get_depth(surface) == 16) {
-    return SurfaceFormat::R5G6B5;
+    return SurfaceFormat::R5G6B5_UINT16;
   }
 #endif
   return CairoContentToGfxFormat(cairo_surface_get_content(surface));
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetCairo::Snapshot()
 {
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -18,22 +18,22 @@
 #include "ScaledFontCairo.h"
 #endif
 #endif
 
 #if defined(WIN32)
 #include "ScaledFontWin.h"
 #endif
 
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
 #include "ScaledFontMac.h"
 #endif
 
 
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
 #include "DrawTargetCG.h"
 #endif
 
 #ifdef WIN32
 #include "DrawTargetD2D.h"
 #include "DrawTargetD2D1.h"
 #include "ScaledFontDWrite.h"
 #include <d3d10_1.h>
@@ -294,17 +294,17 @@ Factory::CreateDrawTarget(BackendType aB
     {
       RefPtr<DrawTargetD2D1> newTarget;
       newTarget = new DrawTargetD2D1();
       if (newTarget->Init(aSize, aFormat)) {
         retVal = newTarget;
       }
       break;
     }
-#elif defined XP_MACOSX
+#elif defined XP_DARWIN
   case BackendType::COREGRAPHICS:
   case BackendType::COREGRAPHICS_ACCELERATED:
     {
       RefPtr<DrawTargetCG> newTarget;
       newTarget = new DrawTargetCG();
       if (newTarget->Init(aBackend, aSize, aFormat)) {
         retVal = newTarget;
       }
@@ -377,17 +377,17 @@ Factory::CreateDrawTargetForData(Backend
     {
       RefPtr<DrawTargetSkia> newTarget;
       newTarget = new DrawTargetSkia();
       newTarget->Init(aData, aSize, aStride, aFormat);
       retVal = newTarget;
       break;
     }
 #endif
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
   case BackendType::COREGRAPHICS:
     {
       RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
       if (newTarget->Init(aBackend, aData, aSize, aStride, aFormat))
         return newTarget.forget();
       break;
     }
 #endif
@@ -484,17 +484,17 @@ Factory::CreateScaledFontForNativeFont(c
     }
 #if defined(USE_CAIRO) || defined(USE_SKIA)
   case NativeFontType::GDI_FONT_FACE:
     {
       return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
     }
 #endif
 #endif
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
   case NativeFontType::MAC_FONT_FACE:
     {
       return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
     }
 #endif
 #if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
   case NativeFontType::CAIRO_FONT_FACE:
     {
@@ -786,17 +786,17 @@ Factory::CreateDrawTargetForCairoSurface
 
   if (mRecorder && retVal) {
     return MakeAndAddRef<DrawTargetRecording>(mRecorder, retVal, true);
   }
 #endif
   return retVal.forget();
 }
 
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
 already_AddRefed<DrawTarget>
 Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize)
 {
   RefPtr<DrawTarget> retVal;
 
   RefPtr<DrawTargetCG> newTarget = new DrawTargetCG();
 
   if (newTarget->Init(cg, aSize)) {
--- a/gfx/2d/HelpersCairo.h
+++ b/gfx/2d/HelpersCairo.h
@@ -148,33 +148,33 @@ GfxFormatToCairoFormat(SurfaceFormat for
   switch (format)
   {
     case SurfaceFormat::B8G8R8A8:
       return CAIRO_FORMAT_ARGB32;
     case SurfaceFormat::B8G8R8X8:
       return CAIRO_FORMAT_RGB24;
     case SurfaceFormat::A8:
       return CAIRO_FORMAT_A8;
-    case SurfaceFormat::R5G6B5:
+    case SurfaceFormat::R5G6B5_UINT16:
       return CAIRO_FORMAT_RGB16_565;
     default:
       gfxCriticalError() << "Unknown image format " << (int)format;
       return CAIRO_FORMAT_ARGB32;
   }
 }
 
 static inline cairo_content_t
 GfxFormatToCairoContent(SurfaceFormat format)
 {
   switch (format)
   {
     case SurfaceFormat::B8G8R8A8:
       return CAIRO_CONTENT_COLOR_ALPHA;
     case SurfaceFormat::B8G8R8X8:
-    case SurfaceFormat::R5G6B5:  //fall through
+    case SurfaceFormat::R5G6B5_UINT16:  //fall through
       return CAIRO_CONTENT_COLOR;
     case SurfaceFormat::A8:
       return CAIRO_CONTENT_ALPHA;
     default:
       gfxCriticalError() << "Unknown image content format " << (int)format;
       return CAIRO_CONTENT_COLOR_ALPHA;
   }
 }
@@ -236,17 +236,17 @@ CairoFormatToGfxFormat(cairo_format_t fo
   switch (format) {
     case CAIRO_FORMAT_ARGB32:
       return SurfaceFormat::B8G8R8A8;
     case CAIRO_FORMAT_RGB24:
       return SurfaceFormat::B8G8R8X8;
     case CAIRO_FORMAT_A8:
       return SurfaceFormat::A8;
     case CAIRO_FORMAT_RGB16_565:
-      return SurfaceFormat::R5G6B5;
+      return SurfaceFormat::R5G6B5_UINT16;
     default:
       gfxCriticalError() << "Unknown cairo format " << format;
       return SurfaceFormat::UNKNOWN;
   }
 }
 
 static inline FontHinting
 CairoHintingToGfxHinting(cairo_hint_style_t aHintStyle)
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -25,34 +25,34 @@ GfxFormatToSkiaColorType(SurfaceFormat f
 {
   switch (format)
   {
     case SurfaceFormat::B8G8R8A8:
       return kBGRA_8888_SkColorType;
     case SurfaceFormat::B8G8R8X8:
       // We probably need to do something here.
       return kBGRA_8888_SkColorType;
-    case SurfaceFormat::R5G6B5:
+    case SurfaceFormat::R5G6B5_UINT16:
       return kRGB_565_SkColorType;
     case SurfaceFormat::A8:
       return kAlpha_8_SkColorType;
     default:
       return kRGBA_8888_SkColorType;
   }
 }
 
 static inline SurfaceFormat
 SkiaColorTypeToGfxFormat(SkColorType type)
 {
   switch (type)
   {
     case kBGRA_8888_SkColorType:
       return SurfaceFormat::B8G8R8A8;
     case kRGB_565_SkColorType:
-      return SurfaceFormat::R5G6B5;
+      return SurfaceFormat::R5G6B5_UINT16;
     case kAlpha_8_SkColorType:
       return SurfaceFormat::A8;
     default:
       return SurfaceFormat::B8G8R8A8;
   }
 }
 
 #ifdef USE_SKIA_GPU
@@ -61,17 +61,17 @@ GfxFormatToGrConfig(SurfaceFormat format
 {
   switch (format)
   {
     case SurfaceFormat::B8G8R8A8:
       return kBGRA_8888_GrPixelConfig;
     case SurfaceFormat::B8G8R8X8:
       // We probably need to do something here.
       return kBGRA_8888_GrPixelConfig;
-    case SurfaceFormat::R5G6B5:
+    case SurfaceFormat::R5G6B5_UINT16:
       return kRGB_565_GrPixelConfig;
     case SurfaceFormat::A8:
       return kAlpha_8_GrPixelConfig;
     default:
       return kRGBA_8888_GrPixelConfig;
   }
 
 }
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -394,18 +394,18 @@ public:
           mMessage << "SurfaceFormat::B8G8R8X8";
           break;
         case SurfaceFormat::R8G8B8A8:
           mMessage << "SurfaceFormat::R8G8B8A8";
           break;
         case SurfaceFormat::R8G8B8X8:
           mMessage << "SurfaceFormat::R8G8B8X8";
           break;
-        case SurfaceFormat::R5G6B5:
-          mMessage << "SurfaceFormat::R5G6B5";
+        case SurfaceFormat::R5G6B5_UINT16:
+          mMessage << "SurfaceFormat::R5G6B5_UINT16";
           break;
         case SurfaceFormat::A8:
           mMessage << "SurfaceFormat::A8";
           break;
         case SurfaceFormat::YUV:
           mMessage << "SurfaceFormat::YUV";
           break;
         case SurfaceFormat::UNKNOWN:
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -1,20 +1,33 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 // vim:set ts=2 sts=2 sw=2 et cin:
 /* 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 MacIOSurface_h__
 #define MacIOSurface_h__
-#ifdef XP_MACOSX
+#ifdef XP_DARWIN
 #include <QuartzCore/QuartzCore.h>
+#include <CoreVideo/CoreVideo.h>
 #include <dlfcn.h>
 
+struct _CGLContextObject;
+
+typedef _CGLContextObject* CGLContextObj;
+typedef struct CGContext* CGContextRef;
+typedef struct CGImage* CGImageRef;
+typedef uint32_t IOSurfaceID;
+
+#ifdef XP_IOS
+typedef kern_return_t IOReturn;
+typedef int CGLError;
+#endif
+
 typedef CFTypeRef IOSurfacePtr;
 typedef IOSurfacePtr (*IOSurfaceCreateFunc) (CFDictionaryRef properties);
 typedef IOSurfacePtr (*IOSurfaceLookupFunc) (uint32_t io_surface_id);
 typedef IOSurfaceID (*IOSurfaceGetIDFunc)(IOSurfacePtr io_surface);
 typedef void (*IOSurfaceVoidFunc)(IOSurfacePtr io_surface);
 typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options,
                                       uint32_t *seed);
 typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface,
@@ -37,28 +50,26 @@ typedef CGContextRef (*IOSurfaceContextC
 typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
 typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
 
 typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
   CVPixelBufferRef pixelBuffer);
 
 typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
 
+#ifdef XP_MACOSX
 #import <OpenGL/OpenGL.h>
+#else
+#import <OpenGLES/ES2/gl.h>
+#endif
+
 #include "2D.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/RefCounted.h"
 
-struct _CGLContextObject;
-
-typedef _CGLContextObject* CGLContextObj;
-typedef struct CGContext* CGContextRef;
-typedef struct CGImage* CGImageRef;
-typedef uint32_t IOSurfaceID;
-
 enum CGContextType {
   CG_CONTEXT_TYPE_UNKNOWN = 0,
   // These are found by inspection, it's possible they could be changed
   CG_CONTEXT_TYPE_BITMAP = 4,
   CG_CONTEXT_TYPE_IOSURFACE = 8
 };
 
 CGContextType GetContextType(CGContextRef ref);
--- a/gfx/2d/PathCG.h
+++ b/gfx/2d/PathCG.h
@@ -1,17 +1,22 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PATHCG_H_
 #define MOZILLA_GFX_PATHCG_H_
 
+#ifdef MOZ_WIDGET_COCOA
 #include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
 #include "2D.h"
 
 namespace mozilla {
 namespace gfx {
 
 class PathCG;
 
 class PathBuilderCG : public PathBuilder
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -8,21 +8,26 @@
 #include "PathSkia.h"
 #include "skia/include/core/SkPaint.h"
 #include "skia/include/core/SkPath.h"
 #include "skia/include/ports/SkTypeface_mac.h"
 #endif
 #include "DrawTargetCG.h"
 #include <vector>
 #include <dlfcn.h>
+#ifdef MOZ_WIDGET_UIKIT
+#include <CoreFoundation/CoreFoundation.h>
+#endif
 
+#ifdef MOZ_WIDGET_COCOA
 // prototype for private API
 extern "C" {
 CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph);
 };
+#endif
 
 
 namespace mozilla {
 namespace gfx {
 
 ScaledFontMac::CTFontDrawGlyphsFuncT* ScaledFontMac::CTFontDrawGlyphsPtr = nullptr;
 bool ScaledFontMac::sSymbolLookupDone = false;
 
@@ -76,58 +81,67 @@ SkTypeface* ScaledFontMac::GetSkTypeface
 // Note: cairo dlsyms it. We could do that but maybe it's
 // safe just to use?
 
 already_AddRefed<Path>
 ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
 {
   if (aTarget->GetBackendType() == BackendType::COREGRAPHICS ||
       aTarget->GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED) {
+#ifdef MOZ_WIDGET_COCOA
       CGMutablePathRef path = CGPathCreateMutable();
-
       for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
           // XXX: we could probably fold both of these transforms together to avoid extra work
           CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
+
           CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
 
           CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
                                                            aBuffer.mGlyphs[i].mPosition.x,
                                                            aBuffer.mGlyphs[i].mPosition.y);
           CGPathAddPath(path, &matrix, glyphPath);
           CGPathRelease(glyphPath);
       }
       RefPtr<Path> ret = new PathCG(path, FillRule::FILL_WINDING);
       CGPathRelease(path);
       return ret.forget();
+#else
+      //TODO: probably want CTFontCreatePathForGlyph
+      MOZ_CRASH("This needs implemented");
+#endif
   }
   return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
 }
 
 void
 ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
   if (!(aBackendType == BackendType::COREGRAPHICS || aBackendType == BackendType::COREGRAPHICS_ACCELERATED)) {
     ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
     return;
   }
-
+#ifdef MOZ_WIDGET_COCOA
   PathBuilderCG *pathBuilderCG =
     static_cast<PathBuilderCG*>(aBuilder);
   // XXX: check builder type
   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
     // XXX: we could probably fold both of these transforms together to avoid extra work
     CGAffineTransform flip = CGAffineTransformMakeScale(1, -1);
     CGPathRef glyphPath = ::CGFontGetGlyphPath(mFont, &flip, 0, aBuffer.mGlyphs[i].mIndex);
 
     CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
                                                      aBuffer.mGlyphs[i].mPosition.x,
                                                      aBuffer.mGlyphs[i].mPosition.y);
     CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath);
     CGPathRelease(glyphPath);
   }
+#else
+    //TODO: probably want CTFontCreatePathForGlyph
+    MOZ_CRASH("This needs implemented");
+#endif
 }
 
 uint32_t
 CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false)
 {
     uint32_t sum = 0L;
     const uint32_t *table = tableStart;
     const uint32_t *end = table+((length+3) & ~3) / sizeof(uint32_t);
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -1,17 +1,23 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_SCALEDFONTMAC_H_
 #define MOZILLA_GFX_SCALEDFONTMAC_H_
 
-#import <ApplicationServices/ApplicationServices.h>
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreText/CoreText.h>
+#endif
+
 #include "2D.h"
 
 #include "ScaledFontBase.h"
 
 namespace mozilla {
 namespace gfx {
 
 class ScaledFontMac : public ScaledFontBase
--- a/gfx/2d/SourceSurfaceCG.cpp
+++ b/gfx/2d/SourceSurfaceCG.cpp
@@ -4,17 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SourceSurfaceCG.h"
 #include "DrawTargetCG.h"
 #include "DataSourceSurfaceWrapper.h"
 #include "DataSurfaceHelpers.h"
 #include "mozilla/Types.h" // for decltype
 
+#ifdef MOZ_WIDGET_COCOA
 #include "MacIOSurface.h"
+#endif
 #include "Tools.h"
 
 namespace mozilla {
 namespace gfx {
 
 
 SourceSurfaceCG::~SourceSurfaceCG()
 {
@@ -379,16 +381,17 @@ SourceSurfaceCGBitmapContext::DrawTarget
 }
 
 SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
 {
   if (mImage)
     CGImageRelease(mImage);
 }
 
+#ifdef MOZ_WIDGET_COCOA
 SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget)
 {
   CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT_ACCELERATED);
 
   RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg);
 
   mFormat = aDrawTarget->GetFormat();
   mSize.width = surf->GetWidth();
@@ -447,11 +450,12 @@ SourceSurfaceCGIOSurfaceContext::~Source
     free(mData);
 }
 
 unsigned char*
 SourceSurfaceCGIOSurfaceContext::GetData()
 {
   return (unsigned char*)mData;
 }
+#endif
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/SourceSurfaceCG.h
+++ b/gfx/2d/SourceSurfaceCG.h
@@ -1,21 +1,27 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _MOZILLA_GFX_SOURCESURFACECG_H
 #define _MOZILLA_GFX_SOURCESURFACECG_H
 
+#ifdef MOZ_WIDGET_COCOA
 #include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#endif
 
 #include "2D.h"
 
+#ifdef MOZ_WIDGET_COCOA
 class MacIOSurface;
+#endif
 
 namespace mozilla {
 namespace gfx {
 
 CGImageRef
 CreateCGImage(CGDataProviderReleaseDataCallback aCallback,
               void *aInfo,
               const void *aData,
@@ -158,16 +164,17 @@ private:
 
   // The image buffer, if the buffer is owned by this class.
   AlignedArray<uint8_t> mDataHolder;
 
   int32_t mStride;
   IntSize mSize;
 };
 
+#ifdef MOZ_WIDGET_COCOA
 class SourceSurfaceCGIOSurfaceContext : public SourceSurfaceCGContext
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurfaceCGIOSurfaceContext)
   explicit SourceSurfaceCGIOSurfaceContext(DrawTargetCG *);
   ~SourceSurfaceCGIOSurfaceContext();
 
   virtual SurfaceType GetType() const { return SurfaceType::COREGRAPHICS_CGCONTEXT; }
@@ -191,14 +198,15 @@ private:
   mutable CGImageRef mImage;
   MacIOSurface* mIOSurface;
 
   void *mData;
   int32_t mStride;
 
   IntSize mSize;
 };
+#endif
 
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // _MOZILLA_GFX_SOURCESURFACECG_H
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -81,17 +81,17 @@ Distance(Point aA, Point aB)
 }
 
 static inline int
 BytesPerPixel(SurfaceFormat aFormat)
 {
   switch (aFormat) {
   case SurfaceFormat::A8:
     return 1;
-  case SurfaceFormat::R5G6B5:
+  case SurfaceFormat::R5G6B5_UINT16:
     return 2;
   default:
     return 4;
   }
 }
 
 template<typename T, int alignment = 16>
 struct AlignedArray
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -25,33 +25,47 @@ enum class SurfaceType : int8_t {
   SKIA, /* Surface wrapping a Skia bitmap */
   DUAL_DT, /* Snapshot of a dual drawtarget */
   D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
   RECORDING, /* Surface used for recording */
   TILED /* Surface from a tiled DrawTarget */
 };
 
 enum class SurfaceFormat : int8_t {
-  B8G8R8A8,
-  B8G8R8X8,
-  R8G8B8A8,
-  R8G8B8X8,
-  R5G6B5,
+  // The following values are named to reflect layout of colors in memory, from
+  // lowest byte to highest byte. The 32-bit value layout depends on machine
+  // endianness.
+  //               in-memory            32-bit LE value   32-bit BE value
+  B8G8R8A8,     // [BB, GG, RR, AA]     0xAARRGGBB        0xBBGGRRAA
+  B8G8R8X8,     // [BB, GG, RR, 00]     0x00RRGGBB        0xBBGGRR00
+  R8G8B8A8,     // [RR, GG, BB, AA]     0xAABBGGRR        0xRRGGBBAA
+  R8G8B8X8,     // [RR, GG, BB, 00]     0x00BBGGRR        0xRRGGBB00
+
+  // The _UINT16 suffix here indicates that the name reflects the layout when
+  // viewed as a uint16_t value. In memory these values are stored using native
+  // endianness.
+  R5G6B5_UINT16,                    // 0bRRRRRGGGGGGBBBBB
+
+  // This one is a single-byte, so endianness isn't an issue.
   A8,
+
+  // These ones are their own special cases.
   YUV,
   NV12,
+
+  // This represents the unknown format.
   UNKNOWN
 };
 
 inline bool IsOpaque(SurfaceFormat aFormat)
 {
   switch (aFormat) {
   case SurfaceFormat::B8G8R8X8:
   case SurfaceFormat::R8G8B8X8:
-  case SurfaceFormat::R5G6B5:
+  case SurfaceFormat::R5G6B5_UINT16:
   case SurfaceFormat::YUV:
   case SurfaceFormat::NV12:
     return true;
   default:
     return false;
   }
 }
 
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -43,20 +43,19 @@ EXPORTS.mozilla.gfx += [
     'ScaleFactors2D.h',
     'SourceSurfaceCairo.h',
     'StackArray.h',
     'Tools.h',
     'Types.h',
     'UserData.h',
 ]
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
     EXPORTS.mozilla.gfx += [
         'MacIOSurface.h',
-        'QuartzSupport.h',
     ]
     UNIFIED_SOURCES += [
         'DrawTargetCG.cpp',
         'PathCG.cpp',
         'ScaledFontMac.cpp',
         'SourceSurfaceCG.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
@@ -155,16 +154,19 @@ UNIFIED_SOURCES += [
     'SourceSurfaceRawData.cpp',
 ]
 
 SOURCES += [
     'PathHelpers.cpp', # Uses _USE_MATH_DEFINES
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    EXPORTS.mozilla.gfx += [
+        'QuartzSupport.h',
+    ]
     SOURCES += [
         'MacIOSurface.cpp',
         'QuartzSupport.mm',
     ]
 
 if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']:
     SOURCES += ['BlurNEON.cpp']
     SOURCES['BlurNEON.cpp'].flags += ['-mfpu=neon']
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -72,16 +72,17 @@ static const char *sExtensionNames[] = {
     "GL_ANGLE_framebuffer_blit",
     "GL_ANGLE_framebuffer_multisample",
     "GL_ANGLE_instanced_arrays",
     "GL_ANGLE_texture_compression_dxt3",
     "GL_ANGLE_texture_compression_dxt5",
     "GL_ANGLE_timer_query",
     "GL_APPLE_client_storage",
     "GL_APPLE_framebuffer_multisample",
+    "GL_APPLE_sync",
     "GL_APPLE_texture_range",
     "GL_APPLE_vertex_array_object",
     "GL_ARB_ES2_compatibility",
     "GL_ARB_ES3_compatibility",
     "GL_ARB_color_buffer_float",
     "GL_ARB_copy_buffer",
     "GL_ARB_depth_texture",
     "GL_ARB_draw_buffers",
@@ -778,17 +779,16 @@ GLContext::InitWithPrefix(const char *pr
 
             if (!LoadSymbols(coreSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports framebuffer_object without supplying its functions.");
                 MarkUnsupported(GLFeature::framebuffer_object);
             }
         }
 
         if (!IsSupported(GLFeature::framebuffer_object)) {
-
             // Check for aux symbols based on extensions
             if (IsSupported(GLFeature::framebuffer_object_EXT_OES))
             {
                 SymLoadStruct extSymbols[] = {
                     CORE_EXT_SYMBOL2(IsRenderbuffer, EXT, OES),
                     CORE_EXT_SYMBOL2(BindRenderbuffer, EXT, OES),
                     CORE_EXT_SYMBOL2(DeleteRenderbuffers, EXT, OES),
                     CORE_EXT_SYMBOL2(GenRenderbuffers, EXT, OES),
@@ -806,36 +806,28 @@ GLContext::InitWithPrefix(const char *pr
                     END_SYMBOLS
                 };
 
                 if (!LoadSymbols(extSymbols, trygl, prefix)) {
                     NS_ERROR("GL supports framebuffer_object without supplying its functions.");
                 }
             }
 
-            if (IsExtensionSupported(GLContext::ANGLE_framebuffer_blit) ||
-                IsExtensionSupported(GLContext::EXT_framebuffer_blit) ||
-                IsExtensionSupported(GLContext::NV_framebuffer_blit))
-
-            {
+            if (IsSupported(GLFeature::framebuffer_blit)) {
                 SymLoadStruct extSymbols[] = {
                     EXT_SYMBOL3(BlitFramebuffer, ANGLE, EXT, NV),
                     END_SYMBOLS
                 };
 
                 if (!LoadSymbols(extSymbols, trygl, prefix)) {
                     NS_ERROR("GL supports framebuffer_blit without supplying its functions.");
                 }
             }
 
-            if (IsExtensionSupported(GLContext::ANGLE_framebuffer_multisample) ||
-                IsExtensionSupported(GLContext::APPLE_framebuffer_multisample) ||
-                IsExtensionSupported(GLContext::EXT_framebuffer_multisample) ||
-                IsExtensionSupported(GLContext::EXT_multisampled_render_to_texture))
-            {
+            if (IsSupported(GLFeature::framebuffer_multisample)) {
                 SymLoadStruct extSymbols[] = {
                     EXT_SYMBOL3(RenderbufferStorageMultisample, ANGLE, APPLE, EXT),
                     END_SYMBOLS
                 };
 
                 if (!LoadSymbols(extSymbols, trygl, prefix)) {
                     NS_ERROR("GL supports framebuffer_multisample without supplying its functions.");
                 }
@@ -1541,28 +1533,42 @@ GLContext::InitWithPrefix(const char *pr
 
                 MarkExtensionUnsupported(NV_fence);
                 ClearSymbols(extSymbols);
             }
         }
 
         if (IsSupported(GLFeature::read_buffer)) {
             SymLoadStruct extSymbols[] = {
-                { (PRFuncPtr*) &mSymbols.fReadBuffer, { "ReadBuffer",    nullptr } },
+                { (PRFuncPtr*) &mSymbols.fReadBuffer, { "ReadBuffer", nullptr } },
                 END_SYMBOLS
             };
 
             if (!LoadSymbols(&extSymbols[0], trygl, prefix)) {
                 NS_ERROR("GL supports read_buffer without supplying its functions.");
 
                 MarkUnsupported(GLFeature::read_buffer);
                 ClearSymbols(extSymbols);
             }
         }
 
+        if (IsExtensionSupported(APPLE_framebuffer_multisample)) {
+            SymLoadStruct extSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fResolveMultisampleFramebufferAPPLE, { "ResolveMultisampleFramebufferAPPLE", nullptr } },
+                END_SYMBOLS
+            };
+
+            if (!LoadSymbols(&extSymbols[0], trygl, prefix)) {
+                NS_ERROR("GL supports APPLE_framebuffer_multisample without supplying its functions.");
+
+                MarkExtensionUnsupported(APPLE_framebuffer_multisample);
+                ClearSymbols(extSymbols);
+            }
+        }
+
         // Load developer symbols, don't fail if we can't find them.
         SymLoadStruct auxSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fGetTexImage, { "GetTexImage", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetTexLevelParameteriv, { "GetTexLevelParameteriv", nullptr } },
                 END_SYMBOLS
         };
         bool warnOnFailures = DebugMode();
         LoadSymbols(&auxSymbols[0], trygl, prefix, warnOnFailures);
@@ -2908,17 +2914,17 @@ GLContext::GetDrawFB()
 }
 
 GLuint
 GLContext::GetReadFB()
 {
     if (mScreen)
         return mScreen->GetReadFB();
 
-    GLenum bindEnum = IsSupported(GLFeature::framebuffer_blit)
+    GLenum bindEnum = IsSupported(GLFeature::split_framebuffer)
                         ? LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT
                         : LOCAL_GL_FRAMEBUFFER_BINDING;
 
     GLuint ret = 0;
     GetUIntegerv(bindEnum, &ret);
     return ret;
 }
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -119,16 +119,17 @@ enum class GLFeature {
     query_time_elapsed,
     read_buffer,
     renderbuffer_color_float,
     renderbuffer_color_half_float,
     robustness,
     sRGB_framebuffer,
     sRGB_texture,
     sampler_objects,
+    split_framebuffer,
     standard_derivatives,
     sync,
     texture_3D,
     texture_3D_compressed,
     texture_3D_copy,
     texture_float,
     texture_float_linear,
     texture_half_float,
@@ -385,16 +386,17 @@ public:
         ANGLE_framebuffer_blit,
         ANGLE_framebuffer_multisample,
         ANGLE_instanced_arrays,
         ANGLE_texture_compression_dxt3,
         ANGLE_texture_compression_dxt5,
         ANGLE_timer_query,
         APPLE_client_storage,
         APPLE_framebuffer_multisample,
+        APPLE_sync,
         APPLE_texture_range,
         APPLE_vertex_array_object,
         ARB_ES2_compatibility,
         ARB_ES3_compatibility,
         ARB_color_buffer_float,
         ARB_copy_buffer,
         ARB_depth_texture,
         ARB_draw_buffers,
@@ -3135,16 +3137,26 @@ public:
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fGetStringi);
         const GLubyte* ret = mSymbols.fGetStringi(name, index);
         AFTER_GL_CALL;
         return ret;
     }
 
 // -----------------------------------------------------------------------------
+// APPLE_framebuffer_multisample
+
+    void fResolveMultisampleFramebufferAPPLE() {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fResolveMultisampleFramebufferAPPLE);
+        mSymbols.fResolveMultisampleFramebufferAPPLE();
+        AFTER_GL_CALL;
+    }
+
+// -----------------------------------------------------------------------------
 // Constructor
 protected:
     explicit GLContext(const SurfaceCaps& caps,
                        GLContext* sharedContext = nullptr,
                        bool isOffscreen = false);
 
 
 // -----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/gfx/gl/GLContextEAGL.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GLCONTEXTEAGL_H_
+#define GLCONTEXTEAGL_H_
+
+#include "GLContext.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <OpenGLES/EAGL.h>
+
+namespace mozilla {
+namespace gl {
+
+class GLContextEAGL : public GLContext
+{
+    friend class GLContextProviderEAGL;
+
+    EAGLContext* const mContext;
+
+public:
+    MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GLContextEAGL, override)
+    GLContextEAGL(const SurfaceCaps& caps, EAGLContext* context,
+                  GLContext* sharedContext,
+                  bool isOffscreen, ContextProfile profile);
+
+    ~GLContextEAGL();
+
+    virtual GLContextType GetContextType() const override {
+        return GLContextType::EAGL;
+    }
+
+    static GLContextEAGL* Cast(GLContext* gl) {
+        MOZ_ASSERT(gl->GetContextType() == GLContextType::EAGL);
+        return static_cast<GLContextEAGL*>(gl);
+    }
+
+    bool Init() override;
+
+    bool AttachToWindow(nsIWidget* aWidget);
+
+    EAGLContext* GetEAGLContext() const { return mContext; }
+
+    virtual bool MakeCurrentImpl(bool aForce) override;
+
+    virtual bool IsCurrent() override;
+
+    virtual bool SetupLookupFunction() override;
+
+    virtual bool IsDoubleBuffered() const override;
+
+    virtual bool SupportsRobustness() const override;
+
+    virtual bool SwapBuffers() override;
+
+    virtual GLuint GetDefaultFramebuffer() override {
+        return mBackbufferFB;
+    }
+
+    virtual bool RenewSurface() override {
+        return RecreateRB();
+    }
+
+private:
+    GLuint mBackbufferRB;
+    GLuint mBackbufferFB;
+
+    void* mLayer;
+
+    bool RecreateRB();
+};
+
+} // namespace gl
+} // namespace mozilla
+
+#endif // GLCONTEXTEAGL_H_
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -543,31 +543,47 @@ static const FeatureInfo sFeatureInfoArr
         GLVersion::GL3_3,
         GLESVersion::ES3,
         GLContext::ARB_sampler_objects,
         {
             GLContext::Extensions_End
         }
     },
     {
+        // Do we have separate DRAW and READ framebuffer bind points?
+        "split_framebuffer",
+        GLVersion::GL3,
+        GLESVersion::ES3,
+        GLContext::ARB_framebuffer_object,
+        {
+            GLContext::ANGLE_framebuffer_blit,
+            GLContext::APPLE_framebuffer_multisample,
+            GLContext::EXT_framebuffer_blit,
+            GLContext::NV_framebuffer_blit,
+            GLContext::Extensions_End
+        }
+    },
+    {
         "standard_derivatives",
         GLVersion::GL2,
         GLESVersion::ES3,
         GLContext::Extension_None,
         {
             GLContext::OES_standard_derivatives,
             GLContext::Extensions_End
         }
     },
     {
         "sync",
         GLVersion::GL3_2,
         GLESVersion::ES3,
-        GLContext::ARB_sync,
+        GLContext::Extension_None,
         {
+            GLContext::ARB_sync,
+            GLContext::APPLE_sync,
             GLContext::Extensions_End
         }
     },
     {
         "texture_3D",
         GLVersion::GL1_2,
         GLESVersion::ES3,
         GLContext::Extension_None,
--- a/gfx/gl/GLContextProvider.h
+++ b/gfx/gl/GLContextProvider.h
@@ -48,16 +48,25 @@ namespace gl {
 
 #define GL_CONTEXT_PROVIDER_NAME GLContextProviderEGL
 #include "GLContextProviderImpl.h"
 #undef GL_CONTEXT_PROVIDER_NAME
 #ifndef GL_CONTEXT_PROVIDER_DEFAULT
   #define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEGL
 #endif
 
+#if defined(MOZ_WIDGET_UIKIT)
+#define GL_CONTEXT_PROVIDER_NAME GLContextProviderEAGL
+#include "GLContextProviderImpl.h"
+#undef GL_CONTEXT_PROVIDER_NAME
+#ifndef GL_CONTEXT_PROVIDER_DEFAULT
+#define GL_CONTEXT_PROVIDER_DEFAULT GLContextProviderEAGL
+#endif
+#endif
+
 #ifdef MOZ_GL_PROVIDER
   #define GL_CONTEXT_PROVIDER_NAME MOZ_GL_PROVIDER
   #include "GLContextProviderImpl.h"
   #undef GL_CONTEXT_PROVIDER_NAME
   #define GL_CONTEXT_PROVIDER_DEFAULT MOZ_GL_PROVIDER
 #endif
 
 #ifdef GL_CONTEXT_PROVIDER_DEFAULT
new file mode 100644
--- /dev/null
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 "GLContextProvider.h"
+#include "GLContextEAGL.h"
+#include "nsDebug.h"
+#include "nsIWidget.h"
+#include "gfxPrefs.h"
+#include "gfxFailure.h"
+#include "prenv.h"
+#include "mozilla/Preferences.h"
+#include "GeckoProfiler.h"
+
+#import <UIKit/UIKit.h>
+
+namespace mozilla {
+namespace gl {
+
+GLContextEAGL::GLContextEAGL(const SurfaceCaps& caps, EAGLContext* context,
+                             GLContext* sharedContext,
+                             bool isOffscreen, ContextProfile profile)
+    : GLContext(caps, sharedContext, isOffscreen)
+    , mContext(context)
+    , mBackbufferRB(0)
+    , mBackbufferFB(0)
+    , mLayer(nil)
+{
+    SetProfileVersion(ContextProfile::OpenGLES,
+                      [context API] == kEAGLRenderingAPIOpenGLES3 ? 300 : 200);
+}
+
+GLContextEAGL::~GLContextEAGL()
+{
+    MakeCurrent();
+
+    if (mBackbufferFB) {
+        fDeleteFramebuffers(1, &mBackbufferFB);
+    }
+
+    if (mBackbufferRB) {
+        fDeleteRenderbuffers(1, &mBackbufferRB);
+    }
+
+    MarkDestroyed();
+
+    if (mLayer) {
+      mLayer = nil;
+    }
+
+    if (mContext) {
+      [EAGLContext setCurrentContext:nil];
+      [mContext release];
+    }
+}
+
+bool
+GLContextEAGL::Init()
+{
+    if (!InitWithPrefix("gl", true))
+        return false;
+
+    return true;
+}
+
+bool
+GLContextEAGL::AttachToWindow(nsIWidget* aWidget)
+{
+    // This should only be called once
+    MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB);
+
+    UIView* view =
+        reinterpret_cast<UIView*>(aWidget->GetNativeData(NS_NATIVE_WIDGET));
+
+    if (!view) {
+      MOZ_CRASH("no view!");
+    }
+
+    mLayer = [view layer];
+
+    fGenFramebuffers(1, &mBackbufferFB);
+    return RecreateRB();
+}
+
+bool
+GLContextEAGL::RecreateRB()
+{
+    MakeCurrent();
+
+    CAEAGLLayer* layer = (CAEAGLLayer*)mLayer;
+
+    if (mBackbufferRB) {
+        // It doesn't seem to be enough to just call renderbufferStorage: below,
+        // we apparently have to recreate the RB.
+        fDeleteRenderbuffers(1, &mBackbufferRB);
+        mBackbufferRB = 0;
+    }
+
+    fGenRenderbuffers(1, &mBackbufferRB);
+    fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mBackbufferRB);
+
+    [mContext renderbufferStorage:LOCAL_GL_RENDERBUFFER
+              fromDrawable:layer];
+
+    fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBackbufferFB);
+    fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+                             LOCAL_GL_RENDERBUFFER, mBackbufferRB);
+
+    return LOCAL_GL_FRAMEBUFFER_COMPLETE == fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+}
+
+bool
+GLContextEAGL::MakeCurrentImpl(bool aForce)
+{
+    if (!aForce && [EAGLContext currentContext] == mContext) {
+        return true;
+    }
+
+    if (mContext) {
+        if(![EAGLContext setCurrentContext:mContext]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool
+GLContextEAGL::IsCurrent() {
+    return [EAGLContext currentContext] == mContext;
+}
+
+bool
+GLContextEAGL::SetupLookupFunction()
+{
+    return false;
+}
+
+bool
+GLContextEAGL::IsDoubleBuffered() const
+{
+    return true;
+}
+
+bool
+GLContextEAGL::SupportsRobustness() const
+{
+    return false;
+}
+
+bool
+GLContextEAGL::SwapBuffers()
+{
+  PROFILER_LABEL("GLContextEAGL", "SwapBuffers",
+    js::ProfileEntry::Category::GRAPHICS);
+
+  [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER];
+  return true;
+}
+
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateWrappingExisting(void*, void*)
+{
+    return nullptr;
+}
+
+static GLContextEAGL*
+GetGlobalContextEAGL()
+{
+    return static_cast<GLContextEAGL*>(GLContextProviderEAGL::GetGlobalContext());
+}
+
+static already_AddRefed<GLContext>
+CreateEAGLContext(bool aOffscreen, GLContextEAGL* sharedContext)
+{
+    EAGLRenderingAPI apis[] = { kEAGLRenderingAPIOpenGLES3, kEAGLRenderingAPIOpenGLES2 };
+
+    // Try to create a GLES3 context if we can, otherwise fall back to GLES2
+    EAGLContext* context = nullptr;
+    for (EAGLRenderingAPI api : apis) {
+        if (sharedContext) {
+            context = [[EAGLContext alloc] initWithAPI:api
+                       sharegroup:sharedContext->GetEAGLContext().sharegroup];
+        } else {
+            context = [[EAGLContext alloc] initWithAPI:api];
+        }
+
+        if (context) {
+            break;
+        }
+    }
+
+    if (!context) {
+        return nullptr;
+    }
+
+    SurfaceCaps caps = SurfaceCaps::ForRGBA();
+    ContextProfile profile = ContextProfile::OpenGLES;
+    RefPtr<GLContextEAGL> glContext = new GLContextEAGL(caps, context,
+                                                        sharedContext,
+                                                        aOffscreen,
+                                                        profile);
+
+    if (!glContext->Init()) {
+        glContext = nullptr;
+        return nullptr;
+    }
+
+    return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget)
+{
+    RefPtr<GLContext> glContext = CreateEAGLContext(false, GetGlobalContextEAGL());
+    if (!glContext) {
+        return nullptr;
+    }
+
+    if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) {
+        return nullptr;
+    }
+
+    return glContext.forget();
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateHeadless(CreateContextFlags flags)
+{
+    return CreateEAGLContext(true, GetGlobalContextEAGL());
+}
+
+already_AddRefed<GLContext>
+GLContextProviderEAGL::CreateOffscreen(const mozilla::gfx::IntSize& size,
+                                       const SurfaceCaps& caps,
+                                       CreateContextFlags flags)
+{
+    RefPtr<GLContext> glContext = CreateHeadless(flags);
+    if (!glContext->InitOffscreen(size, caps)) {
+        return nullptr;
+    }
+
+    return glContext.forget();
+}
+
+static RefPtr<GLContext> gGlobalContext;
+
+GLContext*
+GLContextProviderEAGL::GetGlobalContext()
+{
+    if (!gGlobalContext) {
+        gGlobalContext = CreateEAGLContext(true, nullptr);
+        if (!gGlobalContext ||
+            !static_cast<GLContextEAGL*>(gGlobalContext.get())->Init())
+        {
+            MOZ_CRASH("Failed to create global context");
+        }
+    }
+
+    return gGlobalContext;
+}
+
+void
+GLContextProviderEAGL::Shutdown()
+{
+    gGlobalContext = nullptr;
+}
+
+} /* namespace gl */
+} /* namespace mozilla */
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -681,14 +681,18 @@ struct GLContextSymbols
                                                                   GLint xoffset, GLint yoffset, GLint zoffset,
                                                                   GLsizei width, GLsizei height, GLsizei depth,
                                                                   GLenum format, GLsizei imageSize, const GLvoid* data);
     PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC fCompressedTexSubImage3D;
 
     // get_string_indexed
     typedef const GLubyte* (GLAPIENTRY * PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);
     PFNGLGETSTRINGIPROC fGetStringi;
+
+    // APPLE_framebuffer_multisample
+    typedef void (GLAPIENTRY * PFNRESOLVEMULTISAMPLEFRAMEBUFFERAPPLE) (void);
+    PFNRESOLVEMULTISAMPLEFRAMEBUFFERAPPLE fResolveMultisampleFramebufferAPPLE;
 };
 
 } // namespace gl
 } // namespace mozilla
 
 #endif /* GLCONTEXTSYMBOLS_H_ */
--- a/gfx/gl/GLContextTypes.h
+++ b/gfx/gl/GLContextTypes.h
@@ -14,17 +14,18 @@ namespace gl {
 
 class GLContext;
 
 enum class GLContextType {
     Unknown,
     WGL,
     CGL,
     GLX,
-    EGL
+    EGL,
+    EAGL
 };
 
 enum class OriginPos : uint8_t {
   TopLeft,
   BottomLeft
 };
 
 struct GLFormats
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -269,31 +269,31 @@ CopyDataSourceSurface(DataSourceSurface*
     MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
                aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
                aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
                aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
     MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
                aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
                aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
                aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
-               aDest->GetFormat() == SurfaceFormat::R5G6B5);
+               aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16);
 
     const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
                           aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
     const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
                            aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
     const bool needsSwap02 = isSrcBGR != isDestBGR;
 
     const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
                              aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
     const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
                               aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
     const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
 
-    const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5;
+    const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16;
 
     DataSourceSurface::MappedSurface srcMap;
     DataSourceSurface::MappedSurface destMap;
     if (!aSource->Map(DataSourceSurface::MapType::READ, &srcMap) ||
         !aDest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
         MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
         return;
     }
@@ -389,17 +389,17 @@ ReadPixelsIntoDataSurface(GLContext* gl,
         destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
         break;
     case SurfaceFormat::R8G8B8A8:
     case SurfaceFormat::R8G8B8X8:
         // Needs host (little) endian ABGR.
         destFormat = LOCAL_GL_RGBA;
         destType = LOCAL_GL_UNSIGNED_BYTE;
         break;
-    case SurfaceFormat::R5G6B5:
+    case SurfaceFormat::R5G6B5_UINT16:
         destFormat = LOCAL_GL_RGB;
         destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
         break;
     default:
         MOZ_CRASH("Bad format.");
     }
     destPixelSize = BytesPerPixel(dest->GetFormat());
     MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
@@ -433,17 +433,17 @@ ReadPixelsIntoDataSurface(GLContext* gl,
             case LOCAL_GL_BGRA: {
                 readFormatGFX = hasAlpha ? SurfaceFormat::B8G8R8A8
                                          : SurfaceFormat::B8G8R8X8;
                 break;
             }
             case LOCAL_GL_RGB: {
                 MOZ_ASSERT(destPixelSize == 2);
                 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
-                readFormatGFX = SurfaceFormat::R5G6B5;
+                readFormatGFX = SurfaceFormat::R5G6B5_UINT16;
                 break;
             }
             default: {
                 MOZ_CRASH("Bad read format.");
             }
         }
 
         switch (readType) {
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -77,16 +77,18 @@ GLScreenBuffer::CreateFactory(GLContext*
             case mozilla::layers::LayersBackend::LAYERS_OPENGL: {
 #if defined(XP_MACOSX)
                 factory = SurfaceFactory_IOSurface::Create(gl, caps, forwarder, flags);
 #elif defined(MOZ_WIDGET_GONK)
                 factory = MakeUnique<SurfaceFactory_Gralloc>(gl, caps, forwarder, flags);
 #elif defined(GL_PROVIDER_GLX)
                 if (sGLXLibrary.UseSurfaceSharing())
                   factory = SurfaceFactory_GLXDrawable::Create(gl, caps, forwarder, flags);
+#elif defined(MOZ_WIDGET_UIKIT)
+                factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, caps, forwarder, mFlags);
 #else
                 if (gl->GetContextType() == GLContextType::EGL) {
                     if (XRE_IsParentProcess()) {
                         factory = SurfaceFactory_EGLImage::Create(gl, caps, forwarder, flags);
                     }
                 }
 #endif
                 break;
@@ -139,39 +141,33 @@ GLScreenBuffer::~GLScreenBuffer()
 }
 
 void
 GLScreenBuffer::BindAsFramebuffer(GLContext* const gl, GLenum target) const
 {
     GLuint drawFB = DrawFB();
     GLuint readFB = ReadFB();
 
-    if (!gl->IsSupported(GLFeature::framebuffer_blit)) {
+    if (!gl->IsSupported(GLFeature::split_framebuffer)) {
         MOZ_ASSERT(drawFB == readFB);
         gl->raw_fBindFramebuffer(target, readFB);
         return;
     }
 
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
         gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
         gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
         break;
 
     case LOCAL_GL_DRAW_FRAMEBUFFER_EXT:
-        if (!gl->IsSupported(GLFeature::framebuffer_blit))
-            NS_WARNING("DRAW_FRAMEBUFFER requested but unavailable.");
-
         gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
         break;
 
     case LOCAL_GL_READ_FRAMEBUFFER_EXT:
-        if (!gl->IsSupported(GLFeature::framebuffer_blit))
-            NS_WARNING("READ_FRAMEBUFFER requested but unavailable.");
-
         gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
         break;
 
     default:
         MOZ_CRASH("Bad `target` for BindFramebuffer.");
     }
 }
 
@@ -184,47 +180,47 @@ GLScreenBuffer::BindFB(GLuint fb)
     mUserDrawFB = fb;
     mUserReadFB = fb;
     mInternalDrawFB = (fb == 0) ? drawFB : fb;
     mInternalReadFB = (fb == 0) ? readFB : fb;
 
     if (mInternalDrawFB == mInternalReadFB) {
         mGL->raw_fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mInternalDrawFB);
     } else {
-        MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+        MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
         mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
         mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
     }
 
 #ifdef DEBUG
     mInInternalMode_DrawFB = false;
     mInInternalMode_ReadFB = false;
 #endif
 }
 
 void
 GLScreenBuffer::BindDrawFB(GLuint fb)
 {
-    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
 
     GLuint drawFB = DrawFB();
     mUserDrawFB = fb;
     mInternalDrawFB = (fb == 0) ? drawFB : fb;
 
     mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
 
 #ifdef DEBUG
     mInInternalMode_DrawFB = false;
 #endif
 }
 
 void
 GLScreenBuffer::BindReadFB(GLuint fb)
 {
-    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
 
     GLuint readFB = ReadFB();
     mUserReadFB = fb;
     mInternalReadFB = (fb == 0) ? readFB : fb;
 
     mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
 
 #ifdef DEBUG
@@ -243,30 +239,30 @@ GLScreenBuffer::BindFB_Internal(GLuint f
     mInInternalMode_DrawFB = true;
     mInInternalMode_ReadFB = true;
 #endif
 }
 
 void
 GLScreenBuffer::BindDrawFB_Internal(GLuint fb)
 {
-    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
 
     mInternalDrawFB = mUserDrawFB = fb;
     mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
 
 #ifdef DEBUG
     mInInternalMode_DrawFB = true;
 #endif
 }
 
 void
 GLScreenBuffer::BindReadFB_Internal(GLuint fb)
 {
-    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
 
     mInternalReadFB = mUserReadFB = fb;
     mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
 
 #ifdef DEBUG
     mInInternalMode_ReadFB = true;
 #endif
 }
@@ -302,17 +298,17 @@ GLScreenBuffer::GetReadFB() const
 {
 #ifdef DEBUG
     MOZ_ASSERT(mGL->IsCurrent());
     MOZ_ASSERT(!mInInternalMode_ReadFB);
 
     // We use raw_ here because this is debug code and we need to see what
     // the driver thinks.
     GLuint actual = 0;
-    if (mGL->IsSupported(GLFeature::framebuffer_blit))
+    if (mGL->IsSupported(GLFeature::split_framebuffer))
         mGL->raw_fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
     else
         mGL->raw_fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&actual);
 
     GLuint predicted = mInternalReadFB;
     if (predicted != actual) {
         printf_stderr("Misprediction: Bound read FB predicted: %d. Was: %d.\n",
                       predicted, actual);
@@ -417,32 +413,38 @@ GLScreenBuffer::AssureBlitted()
         return;
 
     if (mDraw) {
         GLuint drawFB = DrawFB();
         GLuint readFB = ReadFB();
 
         MOZ_ASSERT(drawFB != 0);
         MOZ_ASSERT(drawFB != readFB);
-        MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+        MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
         MOZ_ASSERT(mDraw->mSize == mRead->Size());
 
         ScopedBindFramebuffer boundFB(mGL);
         ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
 
         BindReadFB_Internal(drawFB);
         BindDrawFB_Internal(readFB);
 
-        const gfx::IntSize&  srcSize = mDraw->mSize;
-        const gfx::IntSize& destSize = mRead->Size();
+        if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+            const gfx::IntSize&  srcSize = mDraw->mSize;
+            const gfx::IntSize& destSize = mRead->Size();
 
-        mGL->raw_fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
-                                  0, 0, destSize.width, destSize.height,
-                                  LOCAL_GL_COLOR_BUFFER_BIT,
-                                  LOCAL_GL_NEAREST);
+            mGL->raw_fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
+                                      0, 0, destSize.width, destSize.height,
+                                      LOCAL_GL_COLOR_BUFFER_BIT,
+                                      LOCAL_GL_NEAREST);
+        } else if (mGL->IsExtensionSupported(GLContext::APPLE_framebuffer_multisample)) {
+            mGL->fResolveMultisampleFramebufferAPPLE();
+        } else {
+            MOZ_CRASH("No available blit methods.");
+        }
         // Done!
     }
 
     mNeedsBlit = false;
 }
 
 void
 GLScreenBuffer::Morph(UniquePtr<SurfaceFactory> newFactory)
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -481,20 +481,20 @@ UploadImageDataToTexture(GLContext* gl,
               surfaceFormat = SurfaceFormat::B8G8R8X8;
             } else {
               format = LOCAL_GL_RGBA;
               type = LOCAL_GL_UNSIGNED_BYTE;
               surfaceFormat = SurfaceFormat::R8G8B8X8;
             }
             internalFormat = LOCAL_GL_RGBA;
             break;
-        case SurfaceFormat::R5G6B5:
+        case SurfaceFormat::R5G6B5_UINT16:
             internalFormat = format = LOCAL_GL_RGB;
             type = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
-            surfaceFormat = SurfaceFormat::R5G6B5;
+            surfaceFormat = SurfaceFormat::R5G6B5_UINT16;
             break;
         case SurfaceFormat::A8:
             internalFormat = format = LOCAL_GL_LUMINANCE;
             type = LOCAL_GL_UNSIGNED_BYTE;
             // We don't have a specific luminance shader
             surfaceFormat = SurfaceFormat::A8;
             break;
         default:
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -57,17 +57,17 @@ ScopedGLState::UnwrapImpl()
 }
 
 
 /* ScopedBindFramebuffer - Saves and restores with GetUserBoundFB and BindUserFB. */
 
 void
 ScopedBindFramebuffer::Init()
 {
-    if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+    if (mGL->IsSupported(GLFeature::split_framebuffer)) {
         mOldReadFB = mGL->GetReadFB();
         mOldDrawFB = mGL->GetDrawFB();
     } else {
         mOldReadFB = mOldDrawFB = mGL->GetFB();
     }
 }
 
 ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL)
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -90,18 +90,101 @@ SharedSurface_Basic::~SharedSurface_Basi
 
     if (mFB)
         mGL->fDeleteFramebuffers(1, &mFB);
 
     if (mOwnsTex)
         mGL->fDeleteTextures(1, &mTex);
 }
 
+
 ////////////////////////////////////////////////////////////////////////
 
 SurfaceFactory_Basic::SurfaceFactory_Basic(GLContext* gl, const SurfaceCaps& caps,
                                            const layers::TextureFlags& flags)
     : SurfaceFactory(SharedSurfaceType::Basic, gl, caps, nullptr, flags)
 { }
 
+
+////////////////////////////////////////////////////////////////////////
+// SharedSurface_GLTexture
+
+/*static*/ UniquePtr<SharedSurface_GLTexture>
+SharedSurface_GLTexture::Create(GLContext* prodGL,
+                                const GLFormats& formats,
+                                const IntSize& size,
+                                bool hasAlpha)
+{
+    MOZ_ASSERT(prodGL);
+
+    prodGL->MakeCurrent();
+
+    UniquePtr<SharedSurface_GLTexture> ret;
+    GLContext::LocalErrorScope localError(*prodGL);
+
+    GLuint tex = CreateTextureForOffscreen(prodGL, formats, size);
+
+    GLenum err = localError.GetError();
+    MOZ_ASSERT_IF(err, err == LOCAL_GL_OUT_OF_MEMORY);
+    if (err) {
+        prodGL->fDeleteTextures(1, &tex);
+        return Move(ret);
+    }
+
+    ret.reset(new SharedSurface_GLTexture(prodGL, size,
+                                          hasAlpha, tex));
+    return Move(ret);
+}
+
+SharedSurface_GLTexture::~SharedSurface_GLTexture()
+{
+    if (!mGL->MakeCurrent())
+        return;
+
+    if (mTex) {
+        mGL->fDeleteTextures(1, &mTex);
+    }
+
+    if (mSync) {
+        mGL->fDeleteSync(mSync);
+    }
+}
+
+void
+SharedSurface_GLTexture::ProducerReleaseImpl()
+{
+    mGL->MakeCurrent();
+
+    if (mGL->IsSupported(GLFeature::sync)) {
+        if (mSync) {
+            mGL->fDeleteSync(mSync);
+            mSync = 0;
+        }
+
+        mSync = mGL->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+        if (mSync) {
+            mGL->fFlush();
+            return;
+        }
+    }
+    MOZ_ASSERT(!mSync);
+
+    mGL->fFinish();
+}
+
+bool
+SharedSurface_GLTexture::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+    *out_descriptor = layers::SurfaceDescriptorSharedGLTexture(ProdTexture(),
+                                                               ProdTextureTarget(),
+                                                               (uintptr_t)mSync,
+                                                               mSize,
+                                                               mHasAlpha);
+
+    // Transfer ownership of the fence to the host
+    mSync = nullptr;
+    return true;
+}
+
+
 } // namespace gl
 
 } /* namespace mozilla */
--- a/gfx/gl/SharedSurfaceGL.h
+++ b/gfx/gl/SharedSurfaceGL.h
@@ -88,13 +88,85 @@ public:
                          const layers::TextureFlags& flags);
 
     virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
         bool hasAlpha = mReadCaps.alpha;
         return SharedSurface_Basic::Create(mGL, mFormats, size, hasAlpha);
     }
 };
 
+
+// Using shared GL textures:
+class SharedSurface_GLTexture
+    : public SharedSurface
+{
+public:
+    static UniquePtr<SharedSurface_GLTexture> Create(GLContext* prodGL,
+                                                     const GLFormats& formats,
+                                                     const gfx::IntSize& size,
+                                                     bool hasAlpha);
+
+    static SharedSurface_GLTexture* Cast(SharedSurface* surf) {
+        MOZ_ASSERT(surf->mType == SharedSurfaceType::SharedGLTexture);
+
+        return (SharedSurface_GLTexture*)surf;
+    }
+
+protected:
+    const GLuint mTex;
+    GLsync mSync;
+
+    SharedSurface_GLTexture(GLContext* prodGL,
+                            const gfx::IntSize& size,
+                            bool hasAlpha,
+                            GLuint tex)
+        : SharedSurface(SharedSurfaceType::SharedGLTexture,
+                        AttachmentType::GLTexture,
+                        prodGL,
+                        size,
+                        hasAlpha, true)
+        , mTex(tex)
+        , mSync(0)
+    {
+    }
+
+public:
+    virtual ~SharedSurface_GLTexture();
+
+    virtual void LockProdImpl() override {}
+    virtual void UnlockProdImpl() override {}
+
+    virtual void ProducerReleaseImpl() override;
+
+    virtual void Fence() override {}
+    virtual bool WaitSync() override { MOZ_CRASH("should not be called"); }
+    virtual bool PollSync() override { MOZ_CRASH("should not be called"); }
+
+    virtual GLuint ProdTexture() override {
+        return mTex;
+    }
+
+    virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+};
+
+class SurfaceFactory_GLTexture
+    : public SurfaceFactory
+{
+public:
+    SurfaceFactory_GLTexture(GLContext* prodGL,
+                             const SurfaceCaps& caps,
+                             const RefPtr<layers::ISurfaceAllocator>& allocator,
+                             const layers::TextureFlags& flags)
+        : SurfaceFactory(SharedSurfaceType::SharedGLTexture, prodGL, caps, allocator, flags)
+    {
+    }
+
+    virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+        bool hasAlpha = mReadCaps.alpha;
+        return SharedSurface_GLTexture::Create(mGL, mFormats, size, hasAlpha);
+    }
+};
+
 } // namespace gl
 
 } /* namespace mozilla */
 
 #endif /* SHARED_SURFACE_GL_H_ */
--- a/gfx/gl/SurfaceTypes.h
+++ b/gfx/gl/SurfaceTypes.h
@@ -72,16 +72,17 @@ enum class SharedSurfaceType : uint8_t {
     Basic,
     EGLImageShare,
     EGLSurfaceANGLE,
     DXGLInterop,
     DXGLInterop2,
     Gralloc,
     IOSurface,
     GLXDrawable,
+    SharedGLTexture,
 
     Max
 };
 
 enum class AttachmentType : uint8_t {
     Screen = 0,
 
     GLTexture,
--- a/gfx/gl/TextureImageEGL.cpp
+++ b/gfx/gl/TextureImageEGL.cpp
@@ -15,17 +15,17 @@ namespace gl {
 
 static GLenum
 GLFormatForImage(gfx::SurfaceFormat aFormat)
 {
     switch (aFormat) {
     case gfx::SurfaceFormat::B8G8R8A8:
     case gfx::SurfaceFormat::B8G8R8X8:
         return LOCAL_GL_RGBA;
-    case gfx::SurfaceFormat::R5G6B5:
+    case gfx::SurfaceFormat::R5G6B5_UINT16:
         return LOCAL_GL_RGB;
     case gfx::SurfaceFormat::A8:
         return LOCAL_GL_LUMINANCE;
     default:
         NS_WARNING("Unknown GL format for Surface format");
     }
     return 0;
 }
@@ -33,17 +33,17 @@ GLFormatForImage(gfx::SurfaceFormat aFor
 static GLenum
 GLTypeForImage(gfx::SurfaceFormat aFormat)
 {
     switch (aFormat) {
     case gfx::SurfaceFormat::B8G8R8A8:
     case gfx::SurfaceFormat::B8G8R8X8:
     case gfx::SurfaceFormat::A8:
         return LOCAL_GL_UNSIGNED_BYTE;
-    case gfx::SurfaceFormat::R5G6B5:
+    case gfx::SurfaceFormat::R5G6B5_UINT16:
         return LOCAL_GL_UNSIGNED_SHORT_5_6_5;
     default:
         NS_WARNING("Unknown GL format for Surface format");
     }
     return 0;
 }
 
 TextureImageEGL::TextureImageEGL(GLuint aTexture,
@@ -64,17 +64,17 @@ TextureImageEGL::TextureImageEGL(GLuint 
     , mTextureState(aTextureState)
     , mBound(false)
 {
     if (mUpdateFormat == gfx::SurfaceFormat::UNKNOWN) {
         mUpdateFormat =
                 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType());
     }
 
-    if (mUpdateFormat == gfx::SurfaceFormat::R5G6B5) {
+    if (mUpdateFormat == gfx::SurfaceFormat::R5G6B5_UINT16) {
         mTextureFormat = gfx::SurfaceFormat::R8G8B8X8;
     } else if (mUpdateFormat == gfx::SurfaceFormat::B8G8R8X8) {
         mTextureFormat = gfx::SurfaceFormat::B8G8R8X8;
     } else {
         mTextureFormat = gfx::SurfaceFormat::B8G8R8A8;
     }
 }
 
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -5,16 +5,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 gl_provider = 'Null'
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     gl_provider = 'WGL'
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     gl_provider = 'CGL'
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
+    gl_provider = 'EAGL'
 elif CONFIG['MOZ_WIDGET_GTK']:
     if CONFIG['MOZ_EGL_XRENDER_COMPOSITE']:
         gl_provider = 'EGL'
     else:
         gl_provider = 'GLX'
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt':
     gl_provider = 'GLX'
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
@@ -98,16 +100,25 @@ if gl_provider == 'CGL':
     ]
     # SharedSurfaceIO.cpp includes MacIOSurface.h which include Mac headers
     # which define Size and Point types in root namespace with often conflict with
     # our own types. While I haven't actually hit this issue in the present case,
     # it's been an issue in gfx/layers so let's not risk it.
     SOURCES += [
         'SharedSurfaceIO.cpp',
     ]
+elif gl_provider == 'EAGL':
+    # These files include ObjC headers that are unfriendly to unified builds
+    SOURCES += [
+        'GLContextProviderEAGL.mm',
+    ]
+    EXPORTS += [
+        'GLContextEAGL.h',
+    ]
+
 elif gl_provider == 'GLX':
     # GLContextProviderGLX.cpp needs to be kept out of UNIFIED_SOURCES
     # as it includes X11 headers which cause conflicts.
     SOURCES += [
         'GLContextProviderGLX.cpp',
         'SharedSurfaceGLX.cpp'
     ]
     EXPORTS += [
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -262,17 +262,17 @@ CreateTexturedEffect(gfx::SurfaceFormat 
                      const LayerRenderState &state = LayerRenderState())
 {
   MOZ_ASSERT(aSource);
   RefPtr<TexturedEffect> result;
   switch (aFormat) {
   case gfx::SurfaceFormat::B8G8R8A8:
   case gfx::SurfaceFormat::B8G8R8X8:
   case gfx::SurfaceFormat::R8G8B8X8:
-  case gfx::SurfaceFormat::R5G6B5:
+  case gfx::SurfaceFormat::R5G6B5_UINT16:
   case gfx::SurfaceFormat::R8G8B8A8:
     result = new EffectRGB(aSource, isAlphaPremultiplied, aFilter);
     break;
   case gfx::SurfaceFormat::YUV:
     result = new EffectYCbCr(aSource, aFilter);
     break;
   case gfx::SurfaceFormat::NV12:
     result = new EffectNV12(aSource, aFilter);
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -379,17 +379,17 @@ ConvertOmxYUVFormatToRGB565(android::sp<
 
   android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
                                          OMX_COLOR_Format16bitRGB565);
   if (!colorConverter.isValid()) {
     NS_WARNING("Invalid color conversion");
     return BAD_VALUE;
   }
 
-  uint32_t pixelStride = aMappedSurface->mStride/gfx::BytesPerPixel(gfx::SurfaceFormat::R5G6B5);
+  uint32_t pixelStride = aMappedSurface->mStride/gfx::BytesPerPixel(gfx::SurfaceFormat::R5G6B5_UINT16);
   rv = colorConverter.convert(buffer, width, height,
                               0, 0, width - 1, height - 1 /* source crop */,
                               aMappedSurface->mData, pixelStride, height,
                               0, 0, width - 1, height - 1 /* dest crop */);
   if (rv) {
     NS_WARNING("OMX color conversion failed");
     return BAD_VALUE;
   }
@@ -400,17 +400,17 @@ ConvertOmxYUVFormatToRGB565(android::sp<
 already_AddRefed<gfx::DataSourceSurface>
 GetDataSourceSurfaceFrom(android::sp<android::GraphicBuffer>& aGraphicBuffer,
                          gfx::IntSize aSize,
                          const layers::PlanarYCbCrData& aYcbcrData)
 {
   MOZ_ASSERT(aGraphicBuffer.get());
 
   RefPtr<gfx::DataSourceSurface> surface =
-    gfx::Factory::CreateDataSourceSurface(aSize, gfx::SurfaceFormat::R5G6B5);
+    gfx::Factory::CreateDataSourceSurface(aSize, gfx::SurfaceFormat::R5G6B5_UINT16);
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   gfx::DataSourceSurface::MappedSurface mappedSurface;
   if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
     NS_WARNING("Could not map DataSourceSurface");
     return nullptr;
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -311,17 +311,18 @@ AppendToString(std::stringstream& aStrea
                const char* pfx, const char* sfx)
 {
   aStream << pfx;
   switch (format) {
   case SurfaceFormat::B8G8R8A8:  aStream << "SurfaceFormat::B8G8R8A8"; break;
   case SurfaceFormat::B8G8R8X8:  aStream << "SurfaceFormat::B8G8R8X8"; break;
   case SurfaceFormat::R8G8B8A8:  aStream << "SurfaceFormat::R8G8B8A8"; break;
   case SurfaceFormat::R8G8B8X8:  aStream << "SurfaceFormat::R8G8B8X8"; break;
-  case SurfaceFormat::R5G6B5:    aStream << "SurfaceFormat::R5G6B5"; break;
+  case SurfaceFormat::R5G6B5_UINT16:
+                                 aStream << "SurfaceFormat::R5G6B5_UINT16"; break;
   case SurfaceFormat::A8:        aStream << "SurfaceFormat::A8"; break;
   case SurfaceFormat::YUV:       aStream << "SurfaceFormat::YUV"; break;
   case SurfaceFormat::NV12:      aStream << "SurfaceFormat::NV12"; break;
   case SurfaceFormat::UNKNOWN:   aStream << "SurfaceFormat::UNKNOWN"; break;
   default:
     NS_ERROR("unknown surface format");
     aStream << "???";
   }
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -185,16 +185,19 @@ APZCTreeManager::UpdateHitTestingTree(Co
     LayerMetricsWrapper root(aRoot);
     UpdateHitTestingTree(state, root,
                          // aCompositor is null in gtest scenarios
                          aCompositor ? aCompositor->RootLayerTreeId() : 0,
                          Matrix4x4(), nullptr, nullptr);
     mApzcTreeLog << "[end]\n";
   }
 
+  // We do not support tree structures where the root node has siblings.
+  MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
+
   for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
     APZCTM_LOG("Destroying node at %p with APZC %p\n",
         state.mNodesToDestroy[i].get(),
         state.mNodesToDestroy[i]->GetApzc());
     state.mNodesToDestroy[i]->Destroy();
   }
 
 #if ENABLE_APZCTM_LOGGING
--- a/gfx/layers/apz/src/APZUtils.h
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -16,20 +16,28 @@ namespace layers {
 
 enum HitTestResult {
   HitNothing,
   HitLayer,
   HitDispatchToContentRegion,
 };
 
 enum CancelAnimationFlags : uint32_t {
-  Default = 0,            /* Cancel all animations */
-  ExcludeOverscroll = 1   /* Don't clear overscroll */
+  Default = 0x0,            /* Cancel all animations */
+  ExcludeOverscroll = 0x1,  /* Don't clear overscroll */
+  RequestSnap = 0x2         /* Request snapping to snap points */
 };
 
+inline CancelAnimationFlags
+operator|(CancelAnimationFlags a, CancelAnimationFlags b)
+{
+  return static_cast<CancelAnimationFlags>(static_cast<int>(a)
+                                         | static_cast<int>(b));
+}
+
 enum class ScrollSource {
   // scrollTo() or something similar.
   DOM,
 
   // Touch-screen or trackpad with gesture support.
   Touch,
 
   // Mouse wheel.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -658,17 +658,24 @@ public:
 
   virtual bool DoSample(FrameMetrics& aFrameMetrics,
                         const TimeDuration& aDelta) override
   {
     // Can't inline these variables due to short-circuit evaluation.
     bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
     bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
     if (!continueX && !continueY) {
-      mApzc.OverscrollAnimationEnding();
+      // If we got into overscroll from a fling, that fling did not request a
+      // fling snap to avoid a resulting scrollTo from cancelling the overscroll
+      // animation too early. We do still want to request a fling snap, though,
+      // in case the end of the axis at which we're overscrolled is not a valid
+      // snap point, so we request one now. If there are no snap points, this will
+      // do nothing. If there are snap points, we'll get a scrollTo that snaps us
+      // back to the nearest valid snap point.
+      mApzc.RequestSnap();
       return false;
     }
     return true;
   }
 private:
   AsyncPanZoomController& mApzc;
 };
 
@@ -877,17 +884,17 @@ AsyncPanZoomController::GetInputQueue() 
   return mInputQueue;
 }
 
 void
 AsyncPanZoomController::Destroy()
 {
   APZThreadUtils::AssertOnCompositorThread();
 
-  CancelAnimation();
+  CancelAnimation(CancelAnimationFlags::RequestSnap);
 
   { // scope the lock
     MonitorAutoLock lock(mRefPtrMonitor);
     mGeckoContentController = nullptr;
     mGestureEventListener = nullptr;
   }
   mParent = nullptr;
   mTreeManager = nullptr;
@@ -1499,16 +1506,20 @@ nsEventStatus AsyncPanZoomController::On
     // Moreover, in cases of scroll handoff, the overscroll can be on an APZC
     // further up in the handoff chain rather than on the current APZC, so
     // we need to clear overscroll along the entire handoff chain.
     if (HasReadyTouchBlock()) {
       CurrentTouchBlock()->GetOverscrollHandoffChain()->ClearOverscroll();
     } else {
       ClearOverscroll();
     }
+    // Along with clearing the overscroll, we also want to snap to the nearest
+    // snap point as appropriate, so ask the main thread (which knows about such
+    // things) to handle it.
+    RequestSnap();
 
     ScheduleComposite();
     RequestContentRepaint();
     UpdateSharedCompositorFrameMetrics();
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
@@ -2485,16 +2496,21 @@ void AsyncPanZoomController::CancelAnima
   mY.SetVelocity(0);
   // Setting the state to nothing and cancelling the animation can
   // preempt normal mechanisms for relieving overscroll, so we need to clear
   // overscroll here.
   if (!(aFlags & ExcludeOverscroll) && IsOverscrolled()) {
     ClearOverscroll();
     repaint = true;
   }
+  // Similar to relieving overscroll, we also need to snap to any snap points
+  // if appropriate, so ask the main thread to do that.
+  if (aFlags & CancelAnimationFlags::RequestSnap) {
+    RequestSnap();
+  }
   if (repaint) {
     RequestContentRepaint();
     ScheduleComposite();
     UpdateSharedCompositorFrameMetrics();
   }
 }
 
 void AsyncPanZoomController::ClearOverscroll() {
@@ -2665,16 +2681,19 @@ bool AsyncPanZoomController::SnapBackIfO
   ReentrantMonitorAutoEnter lock(mMonitor);
   // It's possible that we're already in the middle of an overscroll
   // animation - if so, don't start a new one.
   if (IsOverscrolled() && mState != OVERSCROLL_ANIMATION) {
     APZC_LOG("%p is overscrolled, starting snap-back\n", this);
     StartOverscrollAnimation(ParentLayerPoint(0, 0));
     return true;
   }
+  // If we don't kick off an overscroll animation, we still need to ask the
+  // main thread to snap to any nearby snap points.
+  RequestSnap();
   return false;
 }
 
 bool AsyncPanZoomController::IsFlingingFast() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
   if (mState == FLING &&
       GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
     APZC_LOG("%p is moving fast\n", this);
@@ -3340,17 +3359,17 @@ AsyncPanZoomController::ResetInputState(
   CancelAnimationAndGestureState();
 }
 
 void
 AsyncPanZoomController::CancelAnimationAndGestureState()
 {
   mX.CancelGesture();
   mY.CancelGesture();
-  CancelAnimation();
+  CancelAnimation(CancelAnimationFlags::RequestSnap);
 }
 
 bool
 AsyncPanZoomController::HasReadyTouchBlock()
 {
   return GetInputQueue()->HasReadyTouchBlock();
 }
 
@@ -3526,24 +3545,17 @@ void AsyncPanZoomController::ShareCompos
       // so the content process know which APZC sent this shared FrameMetrics.
       if (!compositor->SendSharedCompositorFrameMetrics(mem, handle, mLayersId, mAPZCId)) {
         APZC_LOG("%p failed to share FrameMetrics with content process.", this);
       }
     }
   }
 }
 
-void AsyncPanZoomController::OverscrollAnimationEnding() {
-  // If we got into overscroll from a fling, that fling did not request a
-  // fling snap to avoid a resulting scrollTo from cancelling the overscroll
-  // animation too early. We do still want to request a fling snap, though,
-  // in case the end of the axis at which we're overscrolled is not a valid
-  // snap point, so we request one now. If there are no snap points, this will
-  // do nothing. If there are snap points, we'll get a scrollTo that snaps us
-  // back to the nearest valid snap point.
+void AsyncPanZoomController::RequestSnap() {
   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
     controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
                                  mFrameMetrics.GetScrollOffset());
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -642,19 +642,20 @@ protected:
   static AxisLockMode GetAxisLockMode();
 
   // Helper function for OnSingleTapUp() and OnSingleTapConfirmed().
   nsEventStatus GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers);
 
   // Common processing at the end of a touch block.
   void OnTouchEndOrCancel();
 
-  // This is called by OverscrollAnimation to notify us when the overscroll
-  // animation is ending.
-  void OverscrollAnimationEnding();
+  // This is called to request that the main thread snap the scroll position
+  // to a nearby snap position if appropriate. The current scroll position is
+  // used as the final destination.
+  void RequestSnap();
 
   uint64_t mLayersId;
   RefPtr<CompositorParent> mCompositorParent;
   RefPtr<TaskThrottler> mPaintThrottler;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
      compositor thread. */
--- a/gfx/layers/basic/GrallocTextureHostBasic.cpp
+++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp
@@ -21,33 +21,33 @@ HalFormatToSurfaceFormat(int aHalFormat,
   switch (aHalFormat) {
   case android::PIXEL_FORMAT_BGRA_8888:
     return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8;
   case android::PIXEL_FORMAT_RGBA_8888:
     return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8;
   case android::PIXEL_FORMAT_RGBX_8888:
     return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8;
   case android::PIXEL_FORMAT_RGB_565:
-    return gfx::SurfaceFormat::R5G6B5;
+    return gfx::SurfaceFormat::R5G6B5_UINT16;
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
 #endif
       // Needs convert to RGB565
-      return gfx::SurfaceFormat::R5G6B5;
+      return gfx::SurfaceFormat::R5G6B5_UINT16;
   default:
     if (aHalFormat >= 0x100 && aHalFormat <= 0x1FF) {
       // Reserved range for HAL specific formats.
       // Needs convert to RGB565
-      return gfx::SurfaceFormat::R5G6B5;
+      return gfx::SurfaceFormat::R5G6B5_UINT16;
     } else {
       MOZ_CRASH("Unhandled HAL pixel format");
       return SurfaceFormat::UNKNOWN; // not reached
     }
   }
 }
 
 static bool
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -210,17 +210,17 @@ ClientLayerManager::BeginTransactionWith
   // If we're drawing on behalf of a context with async pan/zoom
   // enabled, then the entire buffer of painted layers might be
   // composited (including resampling) asynchronously before we get
   // a chance to repaint, so we have to ensure that it's all valid
   // and not rotated.
   //
   // Desktop does not support async zoom yet, so we ignore this for those
   // platforms.
-#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_UIKIT)
   if (mWidget && mWidget->GetOwningTabChild()) {
     mCompositorMightResample = AsyncPanZoomEnabled();
   }
 #endif
 
   // If we have a non-default target, we need to let our shadow manager draw
   // to it. This will happen at the end of the transaction.
   if (aTarget && XRE_IsParentProcess()) {
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -210,16 +210,17 @@ TextureHost::Create(const SurfaceDescrip
     case SurfaceDescriptor::TSurfaceDescriptorShmem:
     case SurfaceDescriptor::TSurfaceDescriptorMemory:
     case SurfaceDescriptor::TSurfaceDescriptorDIB:
     case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
       return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
 
     case SurfaceDescriptor::TEGLImageDescriptor:
     case SurfaceDescriptor::TSurfaceTextureDescriptor:
+    case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
       return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
 
     case SurfaceDescriptor::TNewSurfaceDescriptorGralloc:
     case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
       if (aBackend == LayersBackend::LAYERS_OPENGL) {
         return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
       } else {
         return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -77,16 +77,24 @@ struct SurfaceTextureDescriptor {
 
 struct EGLImageDescriptor {
   uintptr_t image; // `EGLImage` is a `void*`.
   uintptr_t fence;
   IntSize size;
   bool hasAlpha;
 };
 
+struct SurfaceDescriptorSharedGLTexture {
+  uint32_t texture;
+  uint32_t target;
+  uintptr_t fence;
+  IntSize size;
+  bool hasAlpha;
+};
+
 struct NewSurfaceDescriptorGralloc {
   MaybeMagicGrallocBufferHandle buffer;
   bool isOpaque;
 };
 
 /**
  * Used for shmem-backed YCbCr and (flavors of) RGBA textures
  */
@@ -111,13 +119,14 @@ union SurfaceDescriptor {
   SurfaceDescriptorD3D10;
   SurfaceDescriptorFileMapping;
   SurfaceDescriptorDXGIYCbCr;
   SurfaceDescriptorX11;
   SurfaceTextureDescriptor;
   EGLImageDescriptor;
   SurfaceDescriptorMacIOSurface;
   NewSurfaceDescriptorGralloc;
+  SurfaceDescriptorSharedGLTexture;
   null_t;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -828,17 +828,17 @@ CompositorOGL::GetShaderConfigFor(Effect
         static_cast<TexturedEffect*>(aEffect);
     TextureSourceOGL* source = texturedEffect->mTexture->AsSourceOGL();
     MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL,
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8);
     MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_RECTANGLE_ARB,
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
-                  source->GetFormat() == gfx::SurfaceFormat::R5G6B5);
+                  source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16);
     config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
                                              source->GetFormat());
     break;
   }
   }
   config.SetColorMatrix(aColorMatrix);
   config.SetMask2D(aMask == MaskType::Mask2d);
   config.SetMask3D(aMask == MaskType::Mask3d);
@@ -1527,17 +1527,17 @@ CompositorOGL::Pause()
   // ReleaseSurface internally calls MakeCurrent.
   gl()->ReleaseSurface();
 #endif
 }
 
 bool
 CompositorOGL::Resume()
 {
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
   if (!gl() || gl()->IsDestroyed())
     return false;
 
   // RenewSurface internally calls MakeCurrent.
   return gl()->RenewSurface();
 #endif
   return true;
 }
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -176,17 +176,17 @@ SurfaceFormatForPixelFormat(android::Pix
   switch (aFormat) {
   case PIXEL_FORMAT_RGBA_8888:
     return gfx::SurfaceFormat::R8G8B8A8;
   case PIXEL_FORMAT_BGRA_8888:
     return gfx::SurfaceFormat::B8G8R8A8;
   case PIXEL_FORMAT_RGBX_8888:
     return gfx::SurfaceFormat::R8G8B8X8;
   case PIXEL_FORMAT_RGB_565:
-    return gfx::SurfaceFormat::R5G6B5;
+    return gfx::SurfaceFormat::R5G6B5_UINT16;
   case HAL_PIXEL_FORMAT_YV12:
     return gfx::SurfaceFormat::YUV;
   default:
     MOZ_CRASH("Unknown gralloc pixel format");
   }
   return gfx::SurfaceFormat::R8G8B8A8;
 }
 
@@ -279,17 +279,17 @@ GrallocTextureClientOGL::AllocateForSurf
     break;
   case gfx::SurfaceFormat::R8G8B8X8:
     format = android::PIXEL_FORMAT_RGBX_8888;
     break;
   case gfx::SurfaceFormat::B8G8R8X8:
      format = android::PIXEL_FORMAT_RGBX_8888;
      mFlags |= TextureFlags::RB_SWAPPED;
     break;
-  case gfx::SurfaceFormat::R5G6B5:
+  case gfx::SurfaceFormat::R5G6B5_UINT16:
     format = android::PIXEL_FORMAT_RGB_565;
     break;
   case gfx::SurfaceFormat::YUV:
     format = HAL_PIXEL_FORMAT_YV12;
     break;
   case gfx::SurfaceFormat::A8:
     NS_WARNING("gralloc does not support gfx::SurfaceFormat::A8");
     return false;
@@ -324,17 +324,17 @@ GrallocTextureClientOGL::AllocateForGLRe
   case gfx::SurfaceFormat::B8G8R8A8:
     format = android::PIXEL_FORMAT_RGBA_8888;
     break;
   case gfx::SurfaceFormat::R8G8B8X8:
   case gfx::SurfaceFormat::B8G8R8X8:
     // there is no android BGRX format?
     format = android::PIXEL_FORMAT_RGBX_8888;
     break;
-  case gfx::SurfaceFormat::R5G6B5:
+  case gfx::SurfaceFormat::R5G6B5_UINT16:
     format = android::PIXEL_FORMAT_RGB_565;
     break;
   default:
     NS_WARNING("Unsupported surface format");
     return false;
   }
 
   return AllocateGralloc(aSize, format, usage);
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -30,17 +30,17 @@ SurfaceFormatForAndroidPixelFormat(andro
   switch (aFormat) {
   case android::PIXEL_FORMAT_BGRA_8888:
     return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8;
   case android::PIXEL_FORMAT_RGBA_8888:
     return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8;
   case android::PIXEL_FORMAT_RGBX_8888:
     return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8;
   case android::PIXEL_FORMAT_RGB_565:
-    return gfx::SurfaceFormat::R5G6B5;
+    return gfx::SurfaceFormat::R5G6B5_UINT16;
   case HAL_PIXEL_FORMAT_YCbCr_422_SP:
   case HAL_PIXEL_FORMAT_YCrCb_420_SP:
   case HAL_PIXEL_FORMAT_YCbCr_422_I:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
   case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
   case HAL_PIXEL_FORMAT_YV12:
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
--- a/gfx/layers/opengl/OGLShaderProgram.h
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -244,17 +244,17 @@ ShaderConfigFromTargetAndFormat(GLenum a
                                 gfx::SurfaceFormat aFormat)
 {
   ShaderConfigOGL config;
   config.SetTextureTarget(aTarget);
   config.SetRBSwap(aFormat == gfx::SurfaceFormat::B8G8R8A8 ||
                    aFormat == gfx::SurfaceFormat::B8G8R8X8);
   config.SetNoAlpha(aFormat == gfx::SurfaceFormat::B8G8R8X8 ||
                     aFormat == gfx::SurfaceFormat::R8G8B8X8 ||
-                    aFormat == gfx::SurfaceFormat::R5G6B5);
+                    aFormat == gfx::SurfaceFormat::R5G6B5_UINT16);
   return config;
 }
 
 /**
  * This struct represents the shaders that make up a program and the uniform
  * and attribute parmeters that those shaders take.
  * It is used by ShaderProgramOGL.
  * Use the factory method GetProfileFor to create instances.
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -99,16 +99,26 @@ CreateTextureHostOGL(const SurfaceDescri
 
 #ifdef GL_PROVIDER_GLX
     case SurfaceDescriptor::TSurfaceDescriptorX11: {
       const auto& desc = aDesc.get_SurfaceDescriptorX11();
       result = new X11TextureHost(aFlags, desc);
       break;
     }
 #endif
+
+    case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: {
+      const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture();
+      result = new GLTextureHost(aFlags, desc.texture(),
+                                 desc.target(),
+                                 (GLsync)desc.fence(),
+                                 desc.size(),
+                                 desc.hasAlpha());
+      break;
+    }
     default: return nullptr;
   }
   return result.forget();
 }
 
 static gl::TextureImage::Flags
 FlagsToGLFlags(TextureFlags aFlags)
 {
@@ -640,10 +650,81 @@ EGLImageTextureHost::SetCompositor(Compo
 
 gfx::SurfaceFormat
 EGLImageTextureHost::GetFormat() const
 {
   MOZ_ASSERT(mTextureSource);
   return mTextureSource->GetFormat();
 }
 
+//
+
+GLTextureHost::GLTextureHost(TextureFlags aFlags,
+                             GLuint aTextureHandle,
+                             GLenum aTarget,
+                             GLsync aSync,
+                             gfx::IntSize aSize,
+                             bool aHasAlpha)
+  : TextureHost(aFlags)
+  , mTexture(aTextureHandle)
+  , mTarget(aTarget)
+  , mSync(aSync)
+  , mSize(aSize)
+  , mHasAlpha(aHasAlpha)
+  , mCompositor(nullptr)
+{}
+
+GLTextureHost::~GLTextureHost()
+{}
+
+gl::GLContext*
+GLTextureHost::gl() const
+{
+  return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+bool
+GLTextureHost::Lock()
+{
+  if (!mCompositor) {
+    return false;
+  }
+
+  if (mSync) {
+    gl()->MakeCurrent();
+    gl()->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED);
+    gl()->fDeleteSync(mSync);
+    mSync = 0;
+  }
+
+  if (!mTextureSource) {
+    gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+                                          : gfx::SurfaceFormat::R8G8B8X8;
+    mTextureSource = new GLTextureSource(mCompositor,
+                                         mTexture,
+                                         mTarget,
+                                         mSize,
+                                         format,
+                                         false /* owned by the client */);
+  }
+
+  return true;
+}
+void
+GLTextureHost::SetCompositor(Compositor* aCompositor)
+{
+  MOZ_ASSERT(aCompositor);
+  CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
+  mCompositor = glCompositor;
+  if (mTextureSource) {
+    mTextureSource->SetCompositor(glCompositor);
+  }
+}
+
+gfx::SurfaceFormat
+GLTextureHost::GetFormat() const
+{
+  MOZ_ASSERT(mTextureSource);
+  return mTextureSource->GetFormat();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -274,16 +274,66 @@ protected:
   GLenum mTextureTarget;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   // If the texture is externally owned, the gl handle will not be deleted
   // in the destructor.
   bool mExternallyOwned;
 };
 
+class GLTextureHost : public TextureHost
+{
+public:
+  GLTextureHost(TextureFlags aFlags,
+                GLuint aTextureHandle,
+                GLenum aTarget,
+                GLsync aSync,
+                gfx::IntSize aSize,
+                bool aHasAlpha);
+
+  virtual ~GLTextureHost();
+
+  // We don't own anything.
+  virtual void DeallocateDeviceData() override {}
+
+  virtual void SetCompositor(Compositor* aCompositor) override;
+
+  virtual bool Lock() override;
+
+  virtual void Unlock() override {}
+
+  virtual gfx::SurfaceFormat GetFormat() const override;
+
+  virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+  {
+    aTexture = mTextureSource;
+    return !!aTexture;
+  }
+
+  virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+  {
+    return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+  }
+
+  gl::GLContext* gl() const;
+
+  virtual gfx::IntSize GetSize() const override { return mSize; }
+
+  virtual const char* Name() override { return "GLTextureHost"; }
+
+protected:
+  const GLuint mTexture;
+  const GLenum mTarget;
+  GLsync mSync;
+  const gfx::IntSize mSize;
+  const bool mHasAlpha;
+  RefPtr<CompositorOGL> mCompositor;
+  RefPtr<GLTextureSource> mTextureSource;
+};
+
 ////////////////////////////////////////////////////////////////////////
 // SurfaceTexture
 
 #ifdef MOZ_WIDGET_ANDROID
 
 class SurfaceTextureSource : public TextureSource
                            , public TextureSourceOGL
 {
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -83,46 +83,46 @@ inline gfxRect ThebesRect(const RectDoub
 
 inline gfxImageFormat SurfaceFormatToImageFormat(SurfaceFormat aFormat)
 {
   switch (aFormat) {
   case SurfaceFormat::B8G8R8A8:
     return gfxImageFormat::ARGB32;
   case SurfaceFormat::B8G8R8X8:
     return gfxImageFormat::RGB24;
-  case SurfaceFormat::R5G6B5:
+  case SurfaceFormat::R5G6B5_UINT16:
     return gfxImageFormat::RGB16_565;
   case SurfaceFormat::A8:
     return gfxImageFormat::A8;
   default:
     return gfxImageFormat::Unknown;
   }
 }
 
 inline SurfaceFormat ImageFormatToSurfaceFormat(gfxImageFormat aFormat)
 {
   switch (aFormat) {
   case gfxImageFormat::ARGB32:
     return SurfaceFormat::B8G8R8A8;
   case gfxImageFormat::RGB24:
     return SurfaceFormat::B8G8R8X8;
   case gfxImageFormat::RGB16_565:
-    return SurfaceFormat::R5G6B5;
+    return SurfaceFormat::R5G6B5_UINT16;
   case gfxImageFormat::A8:
     return SurfaceFormat::A8;
   default:
   case gfxImageFormat::Unknown:
     return SurfaceFormat::B8G8R8A8;
   }
 }
 
 inline gfxContentType ContentForFormat(const SurfaceFormat &aFormat)
 {
   switch (aFormat) {
-  case SurfaceFormat::R5G6B5:
+  case SurfaceFormat::R5G6B5_UINT16:
   case SurfaceFormat::B8G8R8X8:
   case SurfaceFormat::R8G8B8X8:
     return gfxContentType::COLOR;
   case SurfaceFormat::A8:
     return gfxContentType::ALPHA;
   case SurfaceFormat::B8G8R8A8:
   case SurfaceFormat::R8G8B8A8:
   default:
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1847,17 +1847,17 @@ gfxPlatform::Optimal2DFormatForContent(g
   switch (aContent) {
   case gfxContentType::COLOR:
     switch (GetOffscreenFormat()) {
     case gfxImageFormat::ARGB32:
       return mozilla::gfx::SurfaceFormat::B8G8R8A8;
     case gfxImageFormat::RGB24:
       return mozilla::gfx::SurfaceFormat::B8G8R8X8;
     case gfxImageFormat::RGB16_565:
-      return mozilla::gfx::SurfaceFormat::R5G6B5;
+      return mozilla::gfx::SurfaceFormat::R5G6B5_UINT16;
     default:
       NS_NOTREACHED("unknown gfxImageFormat for gfxContentType::COLOR");
       return mozilla::gfx::SurfaceFormat::B8G8R8A8;
     }
   case gfxContentType::ALPHA:
     return mozilla::gfx::SurfaceFormat::A8;
   case gfxContentType::COLOR_ALPHA:
     return mozilla::gfx::SurfaceFormat::B8G8R8A8;
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -68,17 +68,17 @@ void mozilla_dump_image(void* bytes, int
 {
     if (0 == strideBytes) {
         strideBytes = width * bytepp;
     }
     SurfaceFormat format;
     // TODO more flexible; parse string?
     switch (bytepp) {
     case 2:
-        format = SurfaceFormat::R5G6B5;
+        format = SurfaceFormat::R5G6B5_UINT16;
         break;
     case 4:
     default:
         format = SurfaceFormat::R8G8B8A8;
         break;
     }
 
     RefPtr<DataSourceSurface> surf =
--- a/gfx/thebes/gfxWindowsNativeDrawing.cpp
+++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp
@@ -180,38 +180,16 @@ gfxWindowsNativeDrawing::BeginNativeDraw
         return mDC;
     } else {
         NS_ERROR("Bogus render state!");
         return nullptr;
     }
 }
 
 bool
-gfxWindowsNativeDrawing::IsDoublePass()
-{
-    if (mContext->GetDrawTarget()->GetBackendType() != mozilla::gfx::BackendType::CAIRO ||
-        mContext->GetDrawTarget()->IsDualDrawTarget()) {
-      return true;
-    }
-
-    RefPtr<gfxASurface> surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y);
-    if (!surf || surf->CairoStatus())
-        return false;
-    if (surf->GetType() != gfxSurfaceType::Win32 &&
-        surf->GetType() != gfxSurfaceType::Win32Printing) {
-        return true;
-    }
-    if ((surf->GetContentType() != gfxContentType::COLOR ||
-         (surf->GetContentType() == gfxContentType::COLOR_ALPHA &&
-          !(mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA))))
-        return true;
-    return false;
-}
-
-bool
 gfxWindowsNativeDrawing::ShouldRenderAgain()
 {
     switch (mRenderState) {
         case RENDER_STATE_NATIVE_DRAWING_DONE:
             return false;
 
         case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE:
             mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE;
--- a/gfx/thebes/gfxWindowsNativeDrawing.h
+++ b/gfx/thebes/gfxWindowsNativeDrawing.h
@@ -74,19 +74,16 @@ public:
     void TransformToNativeRect(const gfxRect& r, RECT& rout);
 
     /* Marks the end of native drawing */
     void EndNativeDrawing();
 
     /* Returns true if the native drawing should be executed again */
     bool ShouldRenderAgain();
 
-    /* Returns true if double pass alpha extraction is taking place. */
-    bool IsDoublePass();
-
     /* Places the result to the context, if necessary */
     void PaintToContext();
 
 private:
 
     RefPtr<gfxContext> mContext;
     gfxRect mNativeRect;
     uint32_t mNativeDrawFlags;
--- a/gfx/ycbcr/YCbCrUtils.cpp
+++ b/gfx/ycbcr/YCbCrUtils.cpp
@@ -23,17 +23,17 @@ GetYCbCrToRGBDestFormatAndSize(const lay
                  aData.mCbCrSize.width,
                  aData.mCbCrSize.height);
 
   // 'prescale' is true if the scaling is to be done as part of the
   // YCbCr to RGB conversion rather than on the RGB data when rendered.
   bool prescale = aSuggestedSize.width > 0 && aSuggestedSize.height > 0 &&
                   aSuggestedSize != aData.mPicSize;
 
-  if (aSuggestedFormat == SurfaceFormat::R5G6B5) {
+  if (aSuggestedFormat == SurfaceFormat::R5G6B5_UINT16) {
 #if defined(HAVE_YCBCR_TO_RGB565)
     if (prescale &&
         !IsScaleYCbCrToRGB565Fast(aData.mPicX,
                                   aData.mPicY,
                                   aData.mPicSize.width,
                                   aData.mPicSize.height,
                                   aSuggestedSize.width,
                                   aSuggestedSize.height,
@@ -83,17 +83,17 @@ ConvertYCbCrToRGB(const layers::PlanarYC
     TypeFromSize(aData.mYSize.width,
                  aData.mYSize.height,
                  aData.mCbCrSize.width,
                  aData.mCbCrSize.height);
 
   // Convert from YCbCr to RGB now, scaling the image if needed.
   if (aDestSize != aData.mPicSize) {
 #if defined(HAVE_YCBCR_TO_RGB565)
-    if (aDestFormat == SurfaceFormat::R5G6B5) {
+    if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
       ScaleYCbCrToRGB565(aData.mYChannel,
                          aData.mCbChannel,
                          aData.mCrChannel,
                          aDestBuffer,
                          aData.mPicX,
                          aData.mPicY,
                          aData.mPicSize.width,
                          aData.mPicSize.height,
@@ -117,17 +117,17 @@ ConvertYCbCrToRGB(const layers::PlanarYC
                         aData.mYStride,
                         aData.mCbCrStride,
                         aStride,
                         yuvtype,
                         ROTATE_0,
                         FILTER_BILINEAR);
   } else { // no prescale
 #if defined(HAVE_YCBCR_TO_RGB565)
-    if (aDestFormat == SurfaceFormat::R5G6B5) {
+    if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
       ConvertYCbCrToRGB565(aData.mYChannel,
                            aData.mCbChannel,
                            aData.mCrChannel,
                            aDestBuffer,
                            aData.mPicX,
                            aData.mPicY,
                            aData.mPicSize.width,
                            aData.mPicSize.height,
--- a/image/encoders/bmp/nsBMPEncoder.cpp
+++ b/image/encoders/bmp/nsBMPEncoder.cpp
@@ -610,38 +610,26 @@ nsBMPEncoder::EncodeInfoHeader()
   NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_red, 1);
   NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_green, 1);
   NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.gamma_blue, 1);
   NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.intent, 1);
   NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_offset, 1);
   NativeEndian::swapToLittleEndianInPlace(&littleEndianmBIH.profile_size, 1);
 
   ENCODE(&mImageBufferCurr, littleEndianmBIH.bihsize);
-
-  if (mBMPInfoHeader.bihsize == InfoHeaderLength::WIN_V2) {
-      uint16_t width = (uint16_t) littleEndianmBIH.width;
-      ENCODE(&mImageBufferCurr, width);
-      uint16_t height = (uint16_t) littleEndianmBIH.width;
-      ENCODE(&mImageBufferCurr, height);
-  } else {
-      ENCODE(&mImageBufferCurr, littleEndianmBIH.width);
-      ENCODE(&mImageBufferCurr, littleEndianmBIH.height);
-  }
-
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.width);
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.height);
   ENCODE(&mImageBufferCurr, littleEndianmBIH.planes);
   ENCODE(&mImageBufferCurr, littleEndianmBIH.bpp);
-
-  if (mBMPInfoHeader.bihsize > InfoHeaderLength::WIN_V2) {
-    ENCODE(&mImageBufferCurr, littleEndianmBIH.compression);
-    ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size);
-    ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm);
-    ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm);
-    ENCODE(&mImageBufferCurr, littleEndianmBIH.colors);
-    ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors);
-  }
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.compression);
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.image_size);
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.xppm);
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.yppm);
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.colors);
+  ENCODE(&mImageBufferCurr, littleEndianmBIH.important_colors);
 
   if (mBMPInfoHeader.bihsize > InfoHeaderLength::WIN_V3) {
     ENCODE(&mImageBufferCurr, littleEndianmBIH.red_mask);
     ENCODE(&mImageBufferCurr, littleEndianmBIH.green_mask);
     ENCODE(&mImageBufferCurr, littleEndianmBIH.blue_mask);
     ENCODE(&mImageBufferCurr, littleEndianmBIH.alpha_mask);
     ENCODE(&mImageBufferCurr, littleEndianmBIH.color_space);
     ENCODE(&mImageBufferCurr, littleEndianmBIH.white_point.r.x);
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -391,17 +391,17 @@ imgFrame::Optimize()
     // moment
   }
 
 #ifdef ANDROID
   SurfaceFormat optFormat = gfxPlatform::GetPlatform()
     ->Optimal2DFormatForContent(gfxContentType::COLOR);
 
   if (mFormat != SurfaceFormat::B8G8R8A8 &&
-      optFormat == SurfaceFormat::R5G6B5) {
+      optFormat == SurfaceFormat::R5G6B5_UINT16) {
     RefPtr<VolatileBuffer> buf =
       AllocateBufferForImage(mSize, optFormat);
     if (!buf) {
       return NS_OK;
     }
 
     RefPtr<DataSourceSurface> surf =
       CreateLockedSurface(buf, mSize, optFormat);
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -365,39 +365,36 @@ JS_TraceIncomingCCWs(JSTracer* trc, cons
 
 extern JS_PUBLIC_API(void)
 JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc,
                      void* thing, JS::TraceKind kind, bool includeDetails);
 
 namespace js {
 
 // Automates static dispatch for tracing for TraceableContainers.
-template <typename, typename=void> struct DefaultTracer;
-
-// The default for POD, non-pointer types is to do nothing.
-template <typename T>
-struct DefaultTracer<T, typename mozilla::EnableIf<!mozilla::IsPointer<T>::value &&
-                                                   mozilla::IsPod<T>::value>::Type> {
-    static void trace(JSTracer* trc, T* t, const char* name) {
-        MOZ_ASSERT(mozilla::IsPod<T>::value);
-        MOZ_ASSERT(!mozilla::IsPointer<T>::value);
-    }
-};
+template <typename> struct DefaultTracer;
 
 // The default for non-pod (e.g. struct) types is to call the trace method.
 template <typename T>
-struct DefaultTracer<T, typename mozilla::EnableIf<!mozilla::IsPod<T>::value>::Type> {
+struct DefaultTracer {
     static void trace(JSTracer* trc, T* t, const char* name) {
+        // If your build is failing here, it means you either need an
+        // implementation of DefaultTracer<T> for your type or, for container
+        // and structure types that contain GC pointers, a trace method.
         t->trace(trc);
     }
 };
 
 template <>
 struct DefaultTracer<jsid>
 {
     static void trace(JSTracer* trc, jsid* id, const char* name) {
         JS_CallUnbarrieredIdTracer(trc, id, name);
     }
 };
 
+template <> struct DefaultTracer<uint32_t> {
+    static void trace(JSTracer* trc, uint32_t* id, const char* name) {}
+};
+
 } // namespace js
 
 #endif /* js_TracingAPI_h */
--- a/js/src/ds/TraceableFifo.h
+++ b/js/src/ds/TraceableFifo.h
@@ -7,17 +7,17 @@
 #ifndef js_TraceableFifo_h
 #define js_TraceableFifo_h
 
 #include "ds/Fifo.h"
 #include "js/RootingAPI.h"
 
 namespace js {
 
-template <typename, typename> struct DefaultTracer;
+template <typename> struct DefaultTracer;
 
 // A TraceableFifo is a Fifo with an additional trace method that knows how to
 // visit all of the items stored in the Fifo. For Fifos that contain GC things,
 // this is usually more convenient than manually iterating and marking the
 // contents.
 //
 // Most types of GC pointers as keys and values can be traced with no extra
 // infrastructure. For structs and non-gc-pointer members, ensure that there is
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4211,183 +4211,168 @@ BytecodeEmitter::emitTemplateString(Pars
         if (!emitAtomOp(cx->names().empty, JSOP_STRING))
             return false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption, bool isLetExpr)
+BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
-    MOZ_ASSERT(isLetExpr == (emitOption == PushInitialValues));
 
     ParseNode* next;
-    for (ParseNode* pn2 = pn->pn_head; ; pn2 = next) {
-        if (!updateSourceCoordNotes(pn2->pn_pos.begin))
-            return false;
-        next = pn2->pn_next;
-
-        ParseNode* pn3;
-        if (!pn2->isKind(PNK_NAME)) {
-            if (pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT)) {
-                // If the emit option is DefineVars, emit variable binding
-                // ops, but not destructuring ops.  The parser (see
-                // Parser::variables) has ensured that our caller will be the
-                // PNK_FOR/PNK_FORIN/PNK_FOROF case in emitTree (we don't have
-                // to worry about this being a variable declaration, as
-                // destructuring declarations without initializers, e.g., |var
-                // [x]|, are not legal syntax), and that case will emit the
-                // destructuring code only after emitting an enumerating
-                // opcode and a branch that tests whether the enumeration
-                // ended. Thus, each iteration's assignment is responsible for
-                // initializing, and nothing needs to be done here.
-                //
-                // Otherwise this is emitting destructuring let binding
-                // initialization for a legacy comprehension expression. See
+    for (ParseNode* binding = pn->pn_head; binding; binding = next) {
+        if (!updateSourceCoordNotes(binding->pn_pos.begin))
+            return false;
+        next = binding->pn_next;
+
+        if (binding->isKind(PNK_ARRAY) || binding->isKind(PNK_OBJECT)) {
+            // Destructuring BindingPattern in a `for` loop head:
+            //     for (let [x, y] of pts) ...;
+            // or in a deprecated comprehension:
+            //     a = [x*y for (let [x, y] of pts)];
+            //
+            // (ES6 calls this a ForDeclaration. When emitting code for a plain
+            // LexicalDeclaration, like `let [x, y] = pt;`, binding will be a
+            // PNK_ASSIGN node, not a PNK_ARRAY node.  `let [x, y];` without an
+            // initializer is a SyntaxError.)
+
+            MOZ_ASSERT(pn->pn_count == 1);
+            if (emitOption == DefineVars) {
+                // Emit JSOP_DEFVAR instructions if needed, but not
+                // destructuring ops. Each iteration of the for-loop is
+                // responsible for initializing these variables, so it's
+                // the caller's responsibility.
+                if (!emitDestructuringDecls(pn->getOp(), binding))
+                    return false;
+            } else {
+                // We're emitting destructuring let binding initialization
+                // for a legacy comprehension expression. See
                 // emitForInOrOfVariables.
-                MOZ_ASSERT(pn->pn_count == 1);
-                if (emitOption == DefineVars) {
-                    if (!emitDestructuringDecls(pn->getOp(), pn2))
-                        return false;
-                } else {
-                    // Lexical bindings cannot be used before they are
-                    // initialized. Similar to the JSOP_INITLEXICAL case below.
-                    MOZ_ASSERT(emitOption != DefineVars);
-                    MOZ_ASSERT_IF(emitOption == InitializeVars, pn->pn_xflags & PNX_POPVAR);
-                    if (!emit1(JSOP_UNDEFINED))
-                        return false;
-                    if (!emitInitializeDestructuringDecls(pn->getOp(), pn2))
-                        return false;
-                }
-                break;
+
+                // Lexical bindings cannot be used before they are
+                // initialized. Similar to the JSOP_INITLEXICAL case below.
+                MOZ_ASSERT(emitOption == InitializeVars);
+                if (!emit1(JSOP_UNDEFINED))
+                    return false;
+                if (!emitInitializeDestructuringDecls(pn->getOp(), binding))
+                    return false;
+                if (!emit1(JSOP_POP))
+                    return false;
             }
-
+        } else if (binding->isKind(PNK_ASSIGN)) {
             /*
              * A destructuring initialiser assignment preceded by var will
              * never occur to the left of 'in' in a for-in loop.  As with 'for
              * (var x = i in o)...', this will cause the entire 'var [a, b] =
              * i' to be hoisted out of the loop.
              */
-            MOZ_ASSERT(pn2->isKind(PNK_ASSIGN));
-            MOZ_ASSERT(pn2->isOp(JSOP_NOP));
+            MOZ_ASSERT(binding->isOp(JSOP_NOP));
             MOZ_ASSERT(emitOption != DefineVars);
 
             /*
              * To allow the front end to rewrite var f = x; as f = x; when a
              * function f(){} precedes the var, detect simple name assignment
              * here and initialize the name.
              */
-            if (pn2->pn_left->isKind(PNK_NAME)) {
-                pn3 = pn2->pn_right;
-                pn2 = pn2->pn_left;
-                goto do_name;
-            }
-
-            pn3 = pn2->pn_left;
-            if (!emitDestructuringDecls(pn->getOp(), pn3))
-                return false;
-
-            if (!emitTree(pn2->pn_right))
-                return false;
-
-            if (!emitDestructuringOps(pn3, isLetExpr))
-                return false;
-
-            /* If we are not initializing, nothing to pop. */
-            if (emitOption != InitializeVars) {
-                if (next)
-                    continue;
-                break;
+            if (binding->pn_left->isKind(PNK_NAME)) {
+                if (!emitSingleVariable(pn, binding->pn_left, binding->pn_right, emitOption))
+                    return false;
+            } else {
+                ParseNode* initializer = binding->pn_left;
+                if (!emitDestructuringDecls(pn->getOp(), initializer))
+                    return false;
+
+                if (!emitTree(binding->pn_right))
+                    return false;
+
+                if (!emitDestructuringOpsHelper(initializer, emitOption))
+                    return false;
+
+                if (emitOption == InitializeVars) {
+                    if (!emit1(JSOP_POP))
+                        return false;
+                }
             }
-            goto emit_note_pop;
-        }
-
-        /*
-         * Load initializer early to share code above that jumps to do_name.
-         * NB: if this var redeclares an existing binding, then pn2 is linked
-         * on its definition's use-chain and pn_expr has been overlayed with
-         * pn_lexdef.
-         */
-        pn3 = pn2->maybeExpr();
-
-     do_name:
-        if (!bindNameToSlot(pn2))
-            return false;
-
-
-        JSOp op;
-        op = pn2->getOp();
-        MOZ_ASSERT(op != JSOP_CALLEE);
-        MOZ_ASSERT(!pn2->pn_scopecoord.isFree() || !pn->isOp(JSOP_NOP));
-
-        jsatomid atomIndex;
-        if (!maybeEmitVarDecl(pn->getOp(), pn2, &atomIndex))
-            return false;
-
-        if (pn3) {
-            MOZ_ASSERT(emitOption != DefineVars);
-            if (op == JSOP_SETNAME ||
-                op == JSOP_STRICTSETNAME ||
-                op == JSOP_SETGNAME ||
-                op == JSOP_STRICTSETGNAME ||
-                op == JSOP_SETINTRINSIC)
-            {
-                MOZ_ASSERT(emitOption != PushInitialValues);
-                JSOp bindOp;
-                if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME)
-                    bindOp = JSOP_BINDNAME;
-                else if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME)
-                    bindOp = JSOP_BINDGNAME;
-                else
-                    bindOp = JSOP_BINDINTRINSIC;
-                if (!emitIndex32(bindOp, atomIndex))
-                    return false;
-            }
-
-            bool oldEmittingForInit = emittingForInit;
-            emittingForInit = false;
-            if (!emitTree(pn3))
-                return false;
-            emittingForInit = oldEmittingForInit;
-        } else if (op == JSOP_INITLEXICAL || op == JSOP_INITGLEXICAL || isLetExpr) {
-            // 'let' bindings cannot be used before they are
-            // initialized. JSOP_INITLEXICAL distinguishes the binding site.
-            MOZ_ASSERT(emitOption != DefineVars);
-            MOZ_ASSERT_IF(emitOption == InitializeVars, pn->pn_xflags & PNX_POPVAR);
-            if (!emit1(JSOP_UNDEFINED))
-                return false;
-        }
-
-        // If we are not initializing, nothing to pop. If we are initializing
-        // lets, we must emit the pops.
-        if (emitOption != InitializeVars) {
-            if (next)
-                continue;
-            break;
-        }
-
-        MOZ_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr);
-        if (!pn2->pn_scopecoord.isFree()) {
-            if (!emitVarOp(pn2, op))
+        } else {
+            if (!emitSingleVariable(pn, binding, binding->maybeExpr(), emitOption))
+                return false;
+        }
+    }
+    return true;
+}
+
+bool
+BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode* initializer,
+                                    VarEmitOption emitOption)
+{
+    // NB: if this var redeclares an existing binding, then `binding` is linked
+    // on its definition's use-chain and pn_expr has been overlayed with
+    // pn_lexdef.
+    MOZ_ASSERT(binding->isKind(PNK_NAME));
+    if (!bindNameToSlot(binding))
+        return false;
+
+    JSOp op = binding->getOp();
+    MOZ_ASSERT(op != JSOP_CALLEE);
+    MOZ_ASSERT(!binding->pn_scopecoord.isFree() || !pn->isOp(JSOP_NOP));
+
+    jsatomid atomIndex;
+    if (!maybeEmitVarDecl(pn->getOp(), binding, &atomIndex))
+        return false;
+
+    if (initializer) {
+        MOZ_ASSERT(emitOption != DefineVars);
+        if (op == JSOP_SETNAME ||
+            op == JSOP_STRICTSETNAME ||
+            op == JSOP_SETGNAME ||
+            op == JSOP_STRICTSETGNAME)
+        {
+            MOZ_ASSERT(emitOption != PushInitialValues);
+            JSOp bindOp;
+            if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME)
+                bindOp = JSOP_BINDNAME;
+            else
+                bindOp = JSOP_BINDGNAME;
+            if (!emitIndex32(bindOp, atomIndex))
+                return false;
+        }
+
+        bool oldEmittingForInit = emittingForInit;
+        emittingForInit = false;
+        if (!emitTree(initializer))
+            return false;
+        emittingForInit = oldEmittingForInit;
+    } else if (op == JSOP_INITLEXICAL ||
+               op == JSOP_INITGLEXICAL ||
+               emitOption == PushInitialValues)
+    {
+        // 'let' bindings cannot be used before they are
+        // initialized. JSOP_INITLEXICAL distinguishes the binding site.
+        MOZ_ASSERT(emitOption != DefineVars);
+        if (!emit1(JSOP_UNDEFINED))
+            return false;
+    } else {
+        // The declaration is like `var x;`. Nothing to do.
+        return true;
+    }
+
+    // If we are not initializing, nothing to pop. If we are initializing
+    // lets, we must emit the pops.
+    if (emitOption == InitializeVars) {
+        MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr);
+        if (!binding->pn_scopecoord.isFree()) {
+            if (!emitVarOp(binding, op))
                 return false;
         } else {
             if (!emitIndexOp(op, atomIndex))
                 return false;
         }
-
-    emit_note_pop:
-        if (!next)
-            break;
-        if (!emit1(JSOP_POP))
-            return false;
-    }
-
-    if (pn->pn_xflags & PNX_POPVAR) {
         if (!emit1(JSOP_POP))
             return false;
     }
 
     return true;
 }
 
 bool
@@ -4403,23 +4388,26 @@ BytecodeEmitter::emitAssignment(ParseNod
 
     switch (lhs->getKind()) {
       case PNK_NAME:
         if (!bindNameToSlot(lhs))
             return false;
         if (lhs->pn_scopecoord.isFree()) {
             if (!makeAtomIndex(lhs->pn_atom, &atomIndex))
                 return false;
+
             JSOp bindOp;
-            if (lhs->isOp(JSOP_SETNAME) || lhs->isOp(JSOP_STRICTSETNAME))
+            if (lhs->isOp(JSOP_SETNAME) || lhs->isOp(JSOP_STRICTSETNAME)) {
                 bindOp = JSOP_BINDNAME;
-            else if (lhs->isOp(JSOP_SETGNAME) || lhs->isOp(JSOP_STRICTSETGNAME))
+            } else if (lhs->isOp(JSOP_SETGNAME) || lhs->isOp(JSOP_STRICTSETGNAME)) {
                 bindOp = JSOP_BINDGNAME;
-            else
-                bindOp = JSOP_BINDINTRINSIC;
+            } else {
+                MOZ_ASSERT(lhs->isOp(JSOP_SETINTRINSIC));
+                break;
+            }
             if (!emitIndex32(bindOp, atomIndex))
                 return false;
             offset++;
         }
         break;
       case PNK_DOT:
         if (lhs->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropLHS())
@@ -4979,16 +4967,25 @@ BytecodeEmitter::emitTry(ParseNode* pn)
         // If there's no catch block without a catchguard, the last <next catch
         // block> points to rethrow code.  This code will [gosub] to the finally
         // code if appropriate, and is also used for the catch-all trynote for
         // capturing exceptions thrown from catch{} blocks.
         //
         for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
             MOZ_ASSERT(this->stackDepth == depth);
 
+            // Clear the frame's return value that might have been set by the
+            // try block:
+            //
+            //   eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
+            if (!emit1(JSOP_UNDEFINED))
+                return false;
+            if (!emit1(JSOP_SETRVAL))
+                return false;
+
             // Emit the lexical scope and catch body.
             MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
             if (!emitTree(pn3))
                 return false;
 
             // gosub <finally>, if required.
             if (pn->pn_kid3) {
                 if (!emitBackPatchOp(&stmtInfo.gosubs()))
@@ -5029,24 +5026,36 @@ BytecodeEmitter::emitTry(ParseNode* pn)
         backPatch(stmtInfo.gosubs(), code().end(), JSOP_GOSUB);
 
         finallyStart = offset();
 
         // Indicate that we're emitting a subroutine body.
         stmtInfo.type = StmtType::SUBROUTINE;
         if (!updateSourceCoordNotes(pn->pn_kid3->pn_pos.begin))
             return false;
-        if (!emit1(JSOP_FINALLY) ||
-            !emit1(JSOP_GETRVAL) ||
-            !emitTree(pn->pn_kid3) ||
-            !emit1(JSOP_SETRVAL) ||
-            !emit1(JSOP_RETSUB))
-        {
-            return false;
-        }
+        if (!emit1(JSOP_FINALLY))
+            return false;
+        if (!emit1(JSOP_GETRVAL))
+            return false;
+
+        // Clear the frame's return value to make break/continue return
+        // correct value even if there's no other statement before them:
+        //
+        //   eval("x: try { 1 } finally { break x; }");  // undefined, not 1
+        if (!emit1(JSOP_UNDEFINED))
+            return false;
+        if (!emit1(JSOP_SETRVAL))
+            return false;
+
+        if (!emitTree(pn->pn_kid3))
+            return false;
+        if (!emit1(JSOP_SETRVAL))
+            return false;
+        if (!emit1(JSOP_RETSUB))
+            return false;
         hasTryFinally = true;
         MOZ_ASSERT(this->stackDepth == depth);
     }
     popStatement();
 
     // ReconstructPCStack needs a NOP here to mark the end of the last catch block.
     if (!emit1(JSOP_NOP))
         return false;
@@ -5165,17 +5174,17 @@ BytecodeEmitter::emitLetBlock(ParseNode*
     MOZ_ASSERT(pnLet->isArity(PN_BINARY));
     ParseNode* varList = pnLet->pn_left;
     MOZ_ASSERT(varList->isArity(PN_LIST));
     ParseNode* letBody = pnLet->pn_right;
     MOZ_ASSERT(letBody->isLexical() && letBody->isKind(PNK_LEXICALSCOPE));
 
     int letHeadDepth = this->stackDepth;
 
-    if (!emitVariables(varList, PushInitialValues, true))
+    if (!emitVariables(varList, PushInitialValues))
         return false;
 
     /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */
     uint32_t valuesPushed = this->stackDepth - letHeadDepth;
     StmtInfoBCE stmtInfo(cx);
     if (!enterBlockScope(&stmtInfo, letBody->pn_objbox, JSOP_UNINITIALIZED, valuesPushed))
         return false;
 
@@ -5588,62 +5597,57 @@ BytecodeEmitter::emitNormalFor(ParseNode
     LoopStmtInfo stmtInfo(cx);
     pushLoopStatement(&stmtInfo, StmtType::FOR_LOOP, top);
 
     ParseNode* forHead = pn->pn_left;
     ParseNode* forBody = pn->pn_right;
 
     /* C-style for (init; cond; update) ... loop. */
     bool forLoopRequiresFreshening = false;
-    JSOp op;
-    ParseNode* init = forHead->pn_kid1;
-    if (!init) {
-        // If there's no init, emit a nop so that there's somewhere to put the
-        // SRC_FOR annotation that IonBuilder will look for.
-        op = JSOP_NOP;
-    } else if (init->isKind(PNK_FRESHENBLOCK)) {
-        // Also emit a nop, as above.
-        op = JSOP_NOP;
-
-        // The loop's init declaration was hoisted into an enclosing lexical
-        // scope node.  Note that the block scope must be freshened each
-        // iteration.
-        forLoopRequiresFreshening = true;
-    } else {
-        emittingForInit = true;
-        if (!updateSourceCoordNotes(init->pn_pos.begin))
-            return false;
-        if (!emitTree(init))
-            return false;
-        emittingForInit = false;
-
-        op = JSOP_POP;
+    if (ParseNode* init = forHead->pn_kid1) {
+        if (init->isKind(PNK_FRESHENBLOCK)) {
+            // The loop's init declaration was hoisted into an enclosing lexical
+            // scope node.  Note that the block scope must be freshened each
+            // iteration.
+            forLoopRequiresFreshening = true;
+        } else {
+            emittingForInit = true;
+            if (!updateSourceCoordNotes(init->pn_pos.begin))
+                return false;
+            if (!emitTree(init))
+                return false;
+            emittingForInit = false;
+
+            if (!init->isKind(PNK_VAR) && !init->isKind(PNK_LET) && !init->isKind(PNK_CONST)) {
+                // 'init' is an expression, not a declaration. emitTree left
+                // its value on the stack.
+                if (!emit1(JSOP_POP))
+                    return false;
+            }
+        }
     }
 
     /*
-     * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
+     * NB: the SRC_FOR note has offsetBias 1 (JSOP_NOP_LENGTH).
      * Use tmp to hold the biased srcnote "top" offset, which differs
      * from the top local variable by the length of the JSOP_GOTO
      * emitted in between tmp and top if this loop has a condition.
      */
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR, &noteIndex))
         return false;
-    if (!emit1(op))
+    if (!emit1(JSOP_NOP))
         return false;
     ptrdiff_t tmp = offset();
 
     ptrdiff_t jmp = -1;
     if (forHead->pn_kid2) {
         /* Goto the loop condition, which branches back to iterate. */
         if (!emitJump(JSOP_GOTO, 0, &jmp))
             return false;
-    } else {
-        if (op != JSOP_NOP && !emit1(JSOP_NOP))
-            return false;
     }
 
     top = offset();
     stmtInfo.setTop(top);
 
     /* Emit code for the loop body. */
     if (!emitLoopHead(forBody))
         return false;
@@ -5680,22 +5684,19 @@ BytecodeEmitter::emitNormalFor(ParseNode
                 return false;
         }
     }
 
     /* Check for update code to do before the condition (if any). */
     if (ParseNode* update = forHead->pn_kid3) {
         if (!updateSourceCoordNotes(update->pn_pos.begin))
             return false;
-        op = JSOP_POP;
         if (!emitTree(update))
             return false;
-
-        /* Always emit the POP or NOP to help IonBuilder. */
-        if (!emit1(op))
+        if (!emit1(JSOP_POP))
             return false;
 
         /* Restore the absolute line number for source note readers. */
         uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.end);
         if (currentLine() != lineNum) {
             if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum)))
                 return false;
             current->currentLine = lineNum;
@@ -5716,23 +5717,23 @@ BytecodeEmitter::emitNormalFor(ParseNode
             return false;
     }
 
     /* Set the first note offset so we can find the loop condition. */
     if (!setSrcNoteOffset(noteIndex, 0, tmp3 - tmp))
         return false;
     if (!setSrcNoteOffset(noteIndex, 1, tmp2 - tmp))
         return false;
+
     /* The third note offset helps us find the loop-closing jump. */
     if (!setSrcNoteOffset(noteIndex, 2, offset() - tmp))
         return false;
 
     /* If no loop condition, just emit a loop-closing jump. */
-    op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
-    if (!emitJump(op, top - offset()))
+    if (!emitJump(forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO, top - offset()))
         return false;
 
     if (!tryNoteList.append(JSTRY_LOOP, stackDepth, top, offset()))
         return false;
 
     /* Now fixup all breaks and continues. */
     popStatement();
     return true;
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -99,26 +99,34 @@ struct CGYieldOffsetList {
 struct LoopStmtInfo;
 struct StmtInfoBCE;
 
 // Use zero inline elements because these go on the stack and affect how many
 // nested functions are possible.
 typedef Vector<jsbytecode, 0> BytecodeVector;
 typedef Vector<jssrcnote, 0> SrcNotesVector;
 
-// This enum tells EmitVariables and the destructuring functions how emit the
-// given Parser::variables parse tree. In the base case, DefineVars, the caller
-// only wants variables to be defined in the prologue (if necessary). For
-// PushInitialValues, variable initializer expressions are evaluated and left
-// on the stack. For InitializeVars, the initializer expressions values are
-// assigned (to local variables) and popped.
+// This enum tells BytecodeEmitter::emitVariables and the destructuring
+// methods how emit the given Parser::variables parse tree.
 enum VarEmitOption {
-    DefineVars        = 0,
-    PushInitialValues = 1,
-    InitializeVars    = 2
+    // The normal case. Emit code to evaluate initializer expressions and
+    // assign them to local variables. Also emit JSOP_DEF{VAR,LET,CONST}
+    // opcodes in the prologue if the declaration occurs at toplevel.
+    InitializeVars,
+
+    // Emit only JSOP_DEFVAR opcodes, in the prologue, if necessary. This is
+    // used in one case: `for (var $BindingPattern in/of obj)`. If we're at
+    // toplevel, the variable(s) must be defined with JSOP_DEFVAR, but they're
+    // populated inside the loop, via emitAssignment.
+    DefineVars,
+
+    // Emit code to evaluate initializer expressions and leave those values on
+    // the stack. This is used to implement `for (let/const ...;;)` and
+    // deprecated `let` blocks.
+    PushInitialValues
 };
 
 struct BytecodeEmitter
 {
     SharedContext* const sc;      /* context shared between parsing and bytecode generation */
 
     ExclusiveContext* const cx;
 
@@ -456,17 +464,19 @@ struct BytecodeEmitter
 
     bool emitVarOp(ParseNode* pn, JSOp op);
     bool emitVarIncDec(ParseNode* pn);
 
     bool emitNameOp(ParseNode* pn, bool callContext);
     bool emitNameIncDec(ParseNode* pn);
 
     bool maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* result);
-    bool emitVariables(ParseNode* pn, VarEmitOption emitOption, bool isLetExpr = false);
+    bool emitVariables(ParseNode* pn, VarEmitOption emitOption);
+    bool emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode* initializer,
+                            VarEmitOption emitOption);
 
     bool emitNewInit(JSProtoKey key);
     bool emitSingletonInitialiser(ParseNode* pn);
 
     bool emitPrepareIteratorResult();
     bool emitFinishIteratorResult(bool done);
     bool iteratorResultShape(unsigned* shape);
 
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -309,24 +309,22 @@ IsDeleteKind(ParseNodeKind kind)
  *                          pn_right: PNK_STATEMENTLIST node for this default's
  *                            statements
  *                          pn_val: constant value if lookup or table switch
  * PNK_WHILE    binary      pn_left: cond, pn_right: body
  * PNK_DOWHILE  binary      pn_left: body, pn_right: cond
  * PNK_FOR      binary      pn_left: either PNK_FORIN (for-in statement),
  *                            PNK_FOROF (for-of) or PNK_FORHEAD (for(;;))
  *                          pn_right: body
- * PNK_FORIN    ternary     pn_kid1:  PNK_VAR to left of 'in', or nullptr
- *                            its pn_xflags may have PNX_POPVAR bit set
+ * PNK_FORIN    ternary     pn_kid1: PNK_VAR to left of 'in', or nullptr
  *                          pn_kid2: PNK_NAME or destructuring expr
  *                            to left of 'in'; if pn_kid1, then this
  *                            is a clone of pn_kid1->pn_head
  *                          pn_kid3: object expr to right of 'in'
- * PNK_FOROF    ternary     pn_kid1:  PNK_VAR to left of 'of', or nullptr
- *                            its pn_xflags may have PNX_POPVAR bit set
+ * PNK_FOROF    ternary     pn_kid1: PNK_VAR to left of 'of', or nullptr
  *                          pn_kid2: PNK_NAME or destructuring expr
  *                            to left of 'of'; if pn_kid1, then this
  *                            is a clone of pn_kid1->pn_head
  *                          pn_kid3: expr to right of 'of'
  * PNK_FORHEAD  ternary     pn_kid1:  init expr before first ';' or nullptr
  *                          pn_kid2:  cond expr before second ';' or nullptr
  *                          pn_kid3:  update expr after second ';' or nullptr
  * PNK_THROW    unary       pn_op: JSOP_THROW, pn_kid: exception
@@ -752,25 +750,22 @@ class ParseNode
 #define PND_IMPORT             0x200    /* the definition is a module import. */
 
     static_assert(PND_IMPORT < (1 << NumDefinitionFlagBits), "Not enough bits");
 
 /* Flags to propagate from uses to definition. */
 #define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
 
 /* PN_LIST pn_xflags bits. */
-#define PNX_POPVAR      0x01            /* PNK_VAR or PNK_CONST last result
-                                           needs popping */
-#define PNX_FUNCDEFS    0x02            /* contains top-level function statements */
-#define PNX_SETCALL     0x04            /* call expression in lvalue context */
-/* 0x08 is available */
-#define PNX_ARRAYHOLESPREAD 0x10        /* one or more of
+#define PNX_FUNCDEFS    0x01            /* contains top-level function statements */
+#define PNX_SETCALL     0x02            /* call expression in lvalue context */
+#define PNX_ARRAYHOLESPREAD 0x04        /* one or more of
                                            1. array initialiser has holes
                                            2. array initializer has spread node */
-#define PNX_NONCONST    0x20            /* initialiser has non-constants */
+#define PNX_NONCONST    0x08            /* initialiser has non-constants */
 
     static_assert(PNX_NONCONST < (1 << NumListFlagBits), "Not enough bits");
 
     uint32_t frameSlot() const {
         MOZ_ASSERT(pn_arity == PN_CODE || pn_arity == PN_NAME);
         return pn_scopecoord.slot();
     }
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3241,17 +3241,17 @@ Parser<ParseHandler>::statements(YieldHa
     return pn;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandling)
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
-    Node pn = exprInParens(inHandling, yieldHandling);
+    Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
     if (!pn)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(pn)) {
         if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
             return null();
@@ -4463,17 +4463,16 @@ Parser<FullParseHandler>::lexicalDeclara
      *
      * See 8.1.1.1.6 and the note in 13.2.1.
      */
     ParseNodeKind kind = isConst ? PNK_CONST : PNK_LET;
     ParseNode* pn = variables(yieldHandling, kind, NotInForInit,
                               nullptr, CurrentLexicalStaticBlock(pc), HoistVars);
     if (!pn)
         return null();
-    pn->pn_xflags = PNX_POPVAR;
     return MatchOrInsertSemicolonAfterExpression(tokenStream) ? pn : nullptr;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::lexicalDeclaration(YieldHandling, bool)
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
@@ -4910,20 +4909,17 @@ Parser<FullParseHandler>::exportDeclarat
             return null();
         break;
       }
 
       case TOK_VAR:
         kid = variables(YieldIsName, PNK_VAR, NotInForInit);
         if (!kid)
             return null();
-        kid->pn_xflags = PNX_POPVAR;
-
-        kid = MatchOrInsertSemicolonAfterExpression(tokenStream) ? kid : nullptr;
-        if (!kid)
+        if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
             return null();
 
         MOZ_ASSERT(kid->isArity(PN_LIST));
         for (ParseNode* var = kid->pn_head; var; var = var->pn_next) {
             if (var->isKind(PNK_ASSIGN))
                 var = var->pn_left;
               MOZ_ASSERT(var->isKind(PNK_NAME));
               if (!addExportName(var->pn_atom))
@@ -5268,53 +5264,54 @@ Parser<FullParseHandler>::forStatement(Y
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MOZ_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
     MOZ_ASSERT(!!blockObj == (isForDecl && (pn1->isOp(JSOP_DEFLET) || pn1->isOp(JSOP_DEFCONST))));
 
-    // All forms of for-loop (for(;;), for-in, for-of) generate an implicit
-    // block to store any lexical variables declared by the loop-head.  We
-    // implement this by desugaring such loops.  These:
+    // If the head of a for-loop declares any lexical variables, we generate an
+    // implicit block to store them. We implement this by desugaring. These:
+    //
+    //     for (let/const <bindings>; <test>; <update>) <stmt>
+    //     for (let <pattern> in <expr>) <stmt>
+    //     for (let <pattern> of <expr>) <stmt>
     //
-    //   for (let/const <pattern-and-assigns>; <test>; <update>) <stmt>
-    //   for (let <pattern> in <expr>) <stmt>
-    //   for (let <pattern> of <expr>) <stmt>
+    // transform into roughly the same parse trees as these (using deprecated
+    // let-block syntax):
     //
-    // transform into almost these desugarings:
-    //
-    //   let (<pattern-and-assigns>) { for (; <test>; <update>) <stmt> }
-    //   let (<pattern>) { for (<pattern> in <expr>) <stmt> }
-    //   let (<pattern>) { for (<pattern> of <expr>) <stmt> }
+    //     let (<bindings>) { for (; <test>; <update>) <stmt> }
+    //     let (<pattern>) { for (<pattern> in <expr>) <stmt> }
+    //     let (<pattern>) { for (<pattern> of <expr>) <stmt> }
     //
-    // This desugaring is not *quite* correct.  Assignments in the head of a
-    // let-block are evaluated *outside* the scope of the variables declared by
-    // the let-block-head.  But ES6 mandates that they be evaluated in the same
-    // scope, triggering used-before-initialization temporal dead zone errors
-    // as necessary.  Bug 1069480 will fix this.
+    // This desugaring is not ES6 compliant. Initializers in the head of a
+    // let-block are evaluated *outside* the scope of the variables being
+    // initialized. ES6 mandates that they be evaluated in the same scope,
+    // triggering used-before-initialization temporal dead zone errors as
+    // necessary. See bug 1216623 on scoping and bug 1069480 on TDZ.
     //
-    // Additionally, ES6 mandates that *each iteration* of a for-loop create a
-    // fresh binding of loop variables.  For example:
+    // Additionally, in ES6, each iteration of a for-loop creates a fresh
+    // binding of the loop variables. For example:
     //
-    //   var funcs = [];
-    //   for (let i = 0; i < 2; i++)
-    //     funcs.push(function() { return i; });
-    //   assertEq(funcs[0](), 0);
-    //   assertEq(funcs[1](), 1);
+    //     var funcs = [];
+    //     for (let i = 0; i < 2; i++)
+    //         funcs.push(function() { return i; });
+    //     assertEq(funcs[0](), 0);  // the two closures capture...
+    //     assertEq(funcs[1](), 1);  // ...two different `i` bindings
     //
     // These semantics are implemented by "freshening" the implicit block --
     // changing the scope chain to a fresh clone of the instantaneous block
     // object -- each iteration, just before evaluating the "update" in
-    // for(;;) loops.  (We don't implement this freshening for for-in/of loops,
-    // but soon: bug 449811.)  No freshening occurs in for (const ...;;) as
-    // there's no point (you can't reassign consts), and moreover the spec
-    // requires it (which fact isn't exposed in-language but can be observed
-    // through the Debugger API).
+    // for(;;) loops. We don't implement this freshening for for-in/of loops
+    // yet: bug 449811.
+    //
+    // No freshening occurs in `for (const ...;;)` as there's no point: you
+    // can't reassign consts. This is observable through the Debugger API. (The
+    // ES6 spec also skips cloning the environment in this case.)
     //
     // If the for-loop head includes a lexical declaration, then we create an
     // implicit block scope, and:
     //
     //   * forLetImpliedBlock is the node for the implicit block scope.
     //   * forLetDecl is the node for the decl 'let/const <pattern>'.
     //
     // Otherwise both are null.
@@ -5684,17 +5681,17 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
-    Node discriminant = exprInParens(InAllowed, yieldHandling);
+    Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!discriminant)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
 
     AutoPushStmtInfoPC stmtInfo(*this, StmtType::SWITCH);
     if (!stmtInfo.generateBlockId())
@@ -5773,17 +5770,17 @@ Parser<ParseHandler>::switchStatement(Yi
                     }
                 } else if (handler.isReturnStatement(stmt)) {
                     afterReturn = true;
                 }
             }
             handler.addList(body, stmt);
         }
 
-        // In ES6, lexical bindings canot be accessed until initialized. If
+        // In ES6, lexical bindings cannot be accessed until initialized. If
         // there was a 'let' declaration in the case we just parsed, remember
         // the slot starting at which new lexical bindings will be
         // assigned. Since lexical bindings from previous cases will not
         // dominate uses in the current case, any such uses will require a
         // dead zone check.
         //
         // Currently this is overly conservative; we could do better, but
         // declaring lexical bindings within switch cases without introducing
@@ -6099,17 +6096,17 @@ Parser<FullParseHandler>::withStatement(
     // use reportStrictModeError.  However, 'with' is the sole instance of a
     // construct that is forbidden in strict mode code, but doesn't even merit a
     // warning under JSOPTION_EXTRA_WARNINGS.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
     if (pc->sc->strict() && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
         return null();
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
-    Node objectExpr = exprInParens(InAllowed, yieldHandling);
+    Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!objectExpr)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
 
     Rooted<StaticWithObject*> staticWith(context, StaticWithObject::create(context));
     if (!staticWith)
         return null();
 
@@ -6673,28 +6670,20 @@ Parser<ParseHandler>::shouldParseLetDecl
     switch (tt) {
       case TOK_NAME:
         // |let let| is disallowed per ES6 13.3.1.1.
         *parseDeclOut = tokenStream.nextName() != context->names().let;
         break;
 
       case TOK_LC:
       case TOK_LB:
-        // A following name is always a declaration.
-        //
         // |let {| and |let [| are destructuring declarations.
         *parseDeclOut = true;
         break;
 
-      case TOK_LP:
-        // Only parse let blocks for 1.7 and 1.8. Do not expose deprecated let
-        // blocks to content.
-        *parseDeclOut = versionNumber() == JSVERSION_1_7 || versionNumber() == JSVERSION_1_8;
-        break;
-
       default:
         break;
     }
 
     return true;
 }
 
 template <typename ParseHandler>
@@ -6739,20 +6728,16 @@ Parser<ParseHandler>::statement(YieldHan
       case TOK_LC:
         return blockStatement(yieldHandling);
 
       // VariableStatement[?Yield]
       case TOK_VAR: {
         Node pn = variables(yieldHandling, PNK_VAR, NotInForInit);
         if (!pn)
             return null();
-
-        // Tell js_EmitTree to generate a final POP.
-        handler.setListFlag(pn, PNX_POPVAR);
-
         if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
             return null();
         return pn;
       }
 
       // EmptyStatement
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
@@ -6930,17 +6915,17 @@ Parser<ParseHandler>::expr(InHandling in
     if (!matched)
         return pn;
 
     Node seq = handler.newCommaExpressionList(pn);
     if (!seq)
         return null();
     while (true) {
         if (handler.isUnparenthesizedYieldExpression(pn)) {
-            report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
+            report(ParseError, false, pn, JSMSG_BAD_YIELD_SYNTAX);
             return null();
         }
 
         pn = assignExpr(inHandling, yieldHandling, tripledotHandling);
         if (!pn)
             return null();
         handler.addList(seq, pn);
 
@@ -7552,16 +7537,35 @@ Parser<ParseHandler>::unaryExpr(YieldHan
                                     begin,
                                     pn);
         }
         return pn;
       }
     }
 }
 
+
+/*** Comprehensions *******************************************************************************
+ *
+ * We currently support four flavors of comprehensions, all deprecated:
+ *
+ *     [EXPR for (V in OBJ) if (COND)]  // legacy array comprehension
+ *     (EXPR for (V in OBJ) if (COND))  // legacy generator expression
+ *     [for (V of OBJ) if (COND) EXPR]  // ES6-era array comprehension
+ *     (for (V of OBJ) if (COND) EXPR)  // ES6-era generator expression
+ *
+ * (The last two flavors are called "ES6-era" because they were in ES6 draft
+ * specifications for a while. Shortly after this syntax was implemented in SM,
+ * TC39 decided to drop it.)
+ *
+ * Legacy generator expressions evaluate to legacy generators (using the
+ * StopIteration protocol); ES6-era generator expressions evaluate to ES6
+ * generators (using the `{done:, value:}` protocol).
+ */
+
 /*
  * A dedicated helper for transplanting the legacy comprehension expression E in
  *
  *   [E for (V in I)]   // legacy array comprehension
  *   (E for (V in I))   // legacy generator expression
  *
  * from its initial location in the AST, on the left of the 'for', to its final
  * position on the right. To avoid a separate pass we do this by adjusting the
@@ -7969,17 +7973,16 @@ Parser<FullParseHandler>::legacyComprehe
          * tree in order for ComprehensionTranslator to work.
          *
          * These are lets to tell the bytecode emitter to emit initialization
          * code for the temporal dead zone.
          */
         ParseNode* lets = handler.newDeclarationList(PNK_LET, pn3, JSOP_DEFLET);
         if (!lets)
             return null();
-        lets->pn_xflags |= PNX_POPVAR;
 
         /* Definitions can't be passed directly to EmitAssignment as lhs. */
         pn3 = cloneLeftHandSide(pn3);
         if (!pn3)
             return null();
 
         pn2->pn_left = handler.newTernary(headKind, lets, pn3, pn4);
         if (!pn2->pn_left)
@@ -8193,28 +8196,28 @@ Parser<ParseHandler>::generatorComprehen
 
     return genfn;
 }
 
 #if JS_HAS_GENERATOR_EXPRS
 
 /*
  * Starting from a |for| keyword after an expression, parse the comprehension
- * tail completing this generator expression. Wrap the expression at kid in a
+ * tail completing this generator expression. Wrap the expression |expr| in a
  * generator function that is immediately called to evaluate to the generator
  * iterator that is the value of this legacy generator expression.
  *
- * |kid| must be the expression before the |for| keyword; we return an
+ * |expr| must be the expression before the |for| keyword; we return an
  * application of a generator function that includes the |for| loops and
- * |if| guards, with |kid| as the operand of a |yield| expression as the
+ * |if| guards, with |expr| as the operand of a |yield| expression as the
  * innermost loop body.
  *
- * Note how unlike Python, we do not evaluate the expression to the right of
- * the first |in| in the chain of |for| heads. Instead, a generator expression
- * is merely sugar for a generator function expression and its application.
+ * Unlike Python, we do not evaluate the expression to the right of the first
+ * |in| in the chain of |for| heads. Instead, a generator expression is merely
+ * sugar for a generator function expression and its application.
  */
 template <>
 ParseNode*
 Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode* expr)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     // Make a new node for the desugared generator function.
@@ -8230,18 +8233,16 @@ Parser<FullParseHandler>::legacyGenerato
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::legacyGeneratorExpr(Node kid)
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
-static const char js_generator_str[] = "generator";
-
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
@@ -8373,18 +8374,18 @@ Parser<ParseHandler>::comprehensionTail(
     Node yieldExpr = newYieldExpression(begin, bodyExpr);
     if (!yieldExpr)
         return null();
     yieldExpr = handler.parenthesize(yieldExpr);
 
     return handler.newExprStatement(yieldExpr, pos().end);
 }
 
-// Parse an ES6 generator or array comprehension, starting at the first 'for'.
-// The caller is responsible for matching the ending TOK_RP or TOK_RB.
+// Parse an ES6-era generator or array comprehension, starting at the first
+// `for`. The caller is responsible for matching the ending TOK_RP or TOK_RB.
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
@@ -8442,16 +8443,19 @@ Parser<ParseHandler>::generatorComprehen
     if (!result)
         return null();
     handler.setBeginPosition(result, begin);
     handler.setEndPosition(result, pos().end);
 
     return result;
 }
 
+
+/* * */
+
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExprWithoutYield(YieldHandling yieldHandling, unsigned msg)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
     Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (res && pc->lastYieldOffset != startYieldOffset) {
         reportWithOffset(ParseError, false, pc->lastYieldOffset,
@@ -8496,17 +8500,17 @@ Parser<ParseHandler>::argumentList(Yield
                 return false;
         }
 
         if (handler.isUnparenthesizedYieldExpression(argNode)) {
             TokenKind tt;
             if (!tokenStream.peekToken(&tt))
                 return false;
             if (tt == TOK_COMMA) {
-                report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
+                report(ParseError, false, argNode, JSMSG_BAD_YIELD_SYNTAX);
                 return false;
             }
         }
 #if JS_HAS_GENERATOR_EXPRS
         if (!spread) {
             if (!tokenStream.matchToken(&matched, TOK_FOR))
                 return false;
             if (matched) {
@@ -8514,24 +8518,24 @@ Parser<ParseHandler>::argumentList(Yield
                     reportWithOffset(ParseError, false, pc->lastYieldOffset,
                                      JSMSG_BAD_GENEXP_BODY, js_yield_str);
                     return false;
                 }
                 argNode = legacyGeneratorExpr(argNode);
                 if (!argNode)
                     return false;
                 if (!arg0) {
-                    report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+                    report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX);
                     return false;
                 }
                 TokenKind tt;
                 if (!tokenStream.peekToken(&tt))
                     return false;
                 if (tt == TOK_COMMA) {
-                    report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+                    report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX);
                     return false;
                 }
             }
         }
 #endif
         arg0 = false;
 
         handler.addList(listNode, argNode);
@@ -8853,17 +8857,17 @@ Parser<ParseHandler>::arrayInitializer(Y
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
-    // Handle an ES7 array comprehension first.
+    // Handle an ES6-era array comprehension first.
     if (tt == TOK_FOR)
         return arrayComprehension(begin);
 
     if (tt == TOK_RB) {
         /*
          * Mark empty arrays as non-constant, since we cannot easily
          * determine their type.
          */
@@ -9366,35 +9370,48 @@ Parser<ParseHandler>::primaryExpr(YieldH
 
       case TOK_LC:
         return objectLiteral(yieldHandling);
 
       case TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
-        if (next != TOK_RP)
-            return parenExprOrGeneratorComprehension(yieldHandling);
-
-        // Not valid expression syntax, but this is valid in an arrow function
-        // with no params: `() => body`.
-        tokenStream.consumeKnownToken(next, TokenStream::Operand);
-
-        if (!tokenStream.peekToken(&next))
-            return null();
-        if (next != TOK_ARROW) {
-            report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
-                   "expression", TokenKindToDesc(TOK_RP));
-            return null();
-        }
-
-        // Now just return something that will allow parsing to continue.
-        // It doesn't matter what; when we reach the =>, we will rewind and
-        // reparse the whole arrow function. See Parser::assignExpr.
-        return handler.newNullLiteral(pos());
+
+        if (next == TOK_RP) {
+            // Not valid expression syntax, but this is valid in an arrow function
+            // with no params: `() => body`.
+            tokenStream.consumeKnownToken(next, TokenStream::Operand);
+
+            if (!tokenStream.peekToken(&next))
+                return null();
+            if (next != TOK_ARROW) {
+                report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+                       "expression", TokenKindToDesc(TOK_RP));
+                return null();
+            }
+
+            // Now just return something that will allow parsing to continue.
+            // It doesn't matter what; when we reach the =>, we will rewind and
+            // reparse the whole arrow function. See Parser::assignExpr.
+            return handler.newNullLiteral(pos());
+        }
+
+        if (next == TOK_FOR) {
+            uint32_t begin = pos().begin;
+            tokenStream.consumeKnownToken(next, TokenStream::Operand);
+            return generatorComprehension(begin);
+        }
+
+        Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed);
+        if (!expr)
+            return null();
+        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
+        handler.setEndPosition(expr, pos().end);
+        return handler.parenthesize(expr);
       }
 
       case TOK_TEMPLATE_HEAD:
         return templateLiteral(yieldHandling);
 
       case TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionTemplate();
 
@@ -9476,115 +9493,58 @@ Parser<ParseHandler>::primaryExpr(YieldH
 
       default:
         report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                "expression", TokenKindToDesc(tt));
         return null();
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::parenExprOrGeneratorComprehension(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
-    uint32_t begin = pos().begin;
-    uint32_t startYieldOffset = pc->lastYieldOffset;
-
-    bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
-        return null();
-    if (matched)
-        return generatorComprehension(begin);
-
-    Node pn = expr(InAllowed, yieldHandling, TripledotAllowed, PredictInvoked);
-    if (!pn)
-        return null();
-
-#if JS_HAS_GENERATOR_EXPRS
-    if (!tokenStream.matchToken(&matched, TOK_FOR))
-        return null();
-    if (matched) {
-        if (pc->lastYieldOffset != startYieldOffset) {
-            reportWithOffset(ParseError, false, pc->lastYieldOffset,
-                             JSMSG_BAD_GENEXP_BODY, js_yield_str);
-            return null();
-        }
-        if (handler.isUnparenthesizedCommaExpression(pn)) {
-            report(ParseError, false, null(),
-                   JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
-            return null();
-        }
-        pn = legacyGeneratorExpr(pn);
-        if (!pn)
-            return null();
-        handler.setBeginPosition(pn, begin);
-        TokenKind tt;
-        if (!tokenStream.getToken(&tt))
-            return null();
-        if (tt != TOK_RP) {
-            report(ParseError, false, null(),
-                   JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
-            return null();
-        }
-        handler.setEndPosition(pn, pos().end);
-        return handler.parenthesize(pn);
-    }
-#endif /* JS_HAS_GENERATOR_EXPRS */
-
-    pn = handler.parenthesize(pn);
-
-    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
-
-    return pn;
-}
-
-// Legacy generator comprehensions can sometimes appear without parentheses.
-// For example:
-//
-//   foo(x for (x in bar))
-//
-// In this case the parens are part of the call, and not part of the generator
-// comprehension.  This can happen in these contexts:
+// Legacy generator comprehensions can appear anywhere an expression is
+// enclosed in parentheses, even if those parentheses are part of statement
+// syntax or a function call:
 //
 //   if (_)
 //   while (_) {}
 //   do {} while (_)
 //   switch (_) {}
 //   with (_) {}
-//   foo(_) // must be first and only argument
+//   foo(_)  // must be first and only argument
 //
-// This is not the case for ES6 generator comprehensions; they must always be in
-// parentheses.
-
+// This is not the case for also-nonstandard ES6-era generator comprehensions.
+// Those must be enclosed in PrimaryExpression parentheses.
+//
+//     sum(x*x for (x in y)); // ok
+//     sum(for (x of y) x*x); // SyntaxError: needs more parens
+//
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling)
+Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+                                   TripledotHandling tripledotHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     uint32_t begin = pos().begin;
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
-    Node pn = expr(inHandling, yieldHandling, TripledotProhibited, PredictInvoked);
+    Node pn = expr(inHandling, yieldHandling, tripledotHandling, PredictInvoked);
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
         if (handler.isUnparenthesizedCommaExpression(pn)) {
-            report(ParseError, false, null(),
-                   JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+            report(ParseError, false, null(), JSMSG_BAD_GENERATOR_SYNTAX);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
     }
 #endif /* JS_HAS_GENERATOR_EXPRS */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -706,18 +706,18 @@ class Parser : private JS::AutoGCRooter,
                  TripledotHandling tripledotHandling,
                    InvokedPrediction invoked = PredictUninvoked);
     Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                    InvokedPrediction invoked = PredictUninvoked);
     Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
                     bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
     Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
                      InvokedPrediction invoked = PredictUninvoked);
-    Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling);
-    Node exprInParens(InHandling inHandling, YieldHandling yieldHandling);
+    Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+                      TripledotHandling tripledotHandling);
 
     bool tryNewTarget(Node& newTarget);
     bool checkAndMarkSuperScope();
 
     Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
                           HandlePropertyName funName);
 
     /*
@@ -732,28 +732,27 @@ class Parser : private JS::AutoGCRooter,
     bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
                              FunctionSyntaxKind kind, GeneratorKind generatorKind,
                              Directives inheritedDirectives, Directives* newDirectives);
 
     Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin);
 
     Node condition(InHandling inHandling, YieldHandling yieldHandling);
 
-    Node generatorComprehensionLambda(GeneratorKind comprehensionKind, unsigned begin,
-                                      Node innerStmt);
-
+    /* comprehensions */
     Node legacyComprehensionTail(Node kid, unsigned blockid, GeneratorKind comprehensionKind,
                                  ParseContext<ParseHandler>* outerpc,
                                  unsigned innerBlockScopeDepth);
     Node legacyArrayComprehension(Node array);
+    Node generatorComprehensionLambda(GeneratorKind comprehensionKind, unsigned begin,
+                                      Node innerStmt);
     Node legacyGeneratorExpr(Node kid);
-
+    Node comprehensionFor(GeneratorKind comprehensionKind);
+    Node comprehensionIf(GeneratorKind comprehensionKind);
     Node comprehensionTail(GeneratorKind comprehensionKind);
-    Node comprehensionIf(GeneratorKind comprehensionKind);
-    Node comprehensionFor(GeneratorKind comprehensionKind);
     Node comprehension(GeneratorKind comprehensionKind);
     Node arrayComprehension(uint32_t begin);
     Node generatorComprehension(uint32_t begin);
 
     bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread);
     Node destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
                            TokenKind tt);
     Node destructuringExprWithoutYield(YieldHandling yieldHandling, BindData<ParseHandler>* data,
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -23,16 +23,17 @@
 #include "vm/ArrayObject.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jscompartmentinlines.h"
+#include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/String-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
@@ -575,78 +576,80 @@ DispatchToTracer(JSTracer* trc, T* thing
     DoCallback(trc->asCallbackTracer(), thingp, name);
 }
 
 
 /*** GC Marking Interface *************************************************************************/
 
 namespace js {
 
-typedef bool DoNothingMarkingType;
+typedef bool HasNoImplicitEdgesType;
 
 template <typename T>
-struct LinearlyMarkedEphemeronKeyType {
-    typedef DoNothingMarkingType Type;
+struct ImplicitEdgeHolderType {
+    typedef HasNoImplicitEdgesType Type;
 };
 
-// For now, we only handle JSObject* keys, but the linear time algorithm can be
-// easily extended by adding in more types here, then making
+// For now, we only handle JSObject* and JSScript* keys, but the linear time
+// algorithm can be easily extended by adding in more types here, then making
 // GCMarker::traverse<T> call markPotentialEphemeronKey.
 template <>
-struct LinearlyMarkedEphemeronKeyType<JSObject*> {
+struct ImplicitEdgeHolderType<JSObject*> {
     typedef JSObject* Type;
 };
 
 template <>
-struct LinearlyMarkedEphemeronKeyType<JSScript*> {
+struct ImplicitEdgeHolderType<JSScript*> {
     typedef JSScript* Type;
 };
 
 void
 GCMarker::markEphemeronValues(gc::Cell* markedCell, WeakEntryVector& values)
 {
     size_t initialLen = values.length();
     for (size_t i = 0; i < initialLen; i++)
-        values[i].weakmap->maybeMarkEntry(this, markedCell, values[i].key);
+        values[i].weakmap->traceEntry(this, markedCell, values[i].key);
 
     // The vector should not be appended to during iteration because the key is
     // already marked, and even in cases where we have a multipart key, we
     // should only be inserting entries for the unmarked portions.
     MOZ_ASSERT(values.length() == initialLen);
 }
 
 template <typename T>
 void
-GCMarker::markPotentialEphemeronKeyHelper(T markedThing)
+GCMarker::markImplicitEdgesHelper(T markedThing)
 {
     if (!isWeakMarkingTracer())
         return;
 
-    MOZ_ASSERT(gc::TenuredCell::fromPointer(markedThing)->zone()->isGCMarking());
-    MOZ_ASSERT(!gc::TenuredCell::fromPointer(markedThing)->zone()->isGCSweeping());
+    Zone* zone = gc::TenuredCell::fromPointer(markedThing)->zone();
+    MOZ_ASSERT(zone->isGCMarking());
+    MOZ_ASSERT(!zone->isGCSweeping());
 
-    auto weakValues = weakKeys.get(JS::GCCellPtr(markedThing));
-    if (!weakValues)
+    auto p = zone->gcWeakKeys.get(JS::GCCellPtr(markedThing));
+    if (!p)
         return;
+    WeakEntryVector& markables = p->value;
 
-    markEphemeronValues(markedThing, weakValues->value);
-    weakValues->value.clear(); // If key address is reused, it should do nothing
+    markEphemeronValues(markedThing, markables);
+    markables.clear(); // If key address is reused, it should do nothing
 }
 
 template <>
 void
-GCMarker::markPotentialEphemeronKeyHelper(bool)
+GCMarker::markImplicitEdgesHelper(HasNoImplicitEdgesType)
 {
 }
 
 template <typename T>
 void
-GCMarker::markPotentialEphemeronKey(T* thing)
+GCMarker::markImplicitEdges(T* thing)
 {
-    markPotentialEphemeronKeyHelper<typename LinearlyMarkedEphemeronKeyType<T*>::Type>(thing);
+    markImplicitEdgesHelper<typename ImplicitEdgeHolderType<T*>::Type>(thing);
 }
 
 } // namespace js
 
 template <typename T>
 static inline bool
 MustSkipMarking(T thing)
 {
@@ -805,17 +808,17 @@ template <> void GCMarker::traverse(Shap
 // be used as a weakmap key and thereby recurse into weakmapped values.
 template <typename T>
 void
 js::GCMarker::markAndPush(StackTag tag, T* thing)
 {
     if (!mark(thing))
         return;
     pushTaggedPtr(tag, thing);
-    markPotentialEphemeronKey(thing);
+    markImplicitEdges(thing);
 }
 namespace js {
 template <> void GCMarker::traverse(JSObject* thing) { markAndPush(ObjectTag, thing); }
 template <> void GCMarker::traverse(ObjectGroup* thing) { markAndPush(GroupTag, thing); }
 template <> void GCMarker::traverse(jit::JitCode* thing) { markAndPush(JitCodeTag, thing); }
 template <> void GCMarker::traverse(JSScript* thing) { markAndPush(ScriptTag, thing); }
 } // namespace js
 
@@ -1420,17 +1423,17 @@ GCMarker::processMarkStackTop(SliceBudge
         AssertZoneIsMarking(obj);
 
         budget.step();
         if (budget.isOverBudget()) {
             repush(obj);
             return;
         }
 
-        markPotentialEphemeronKey(obj);
+        markImplicitEdges(obj);
         ObjectGroup* group = obj->groupFromGC();
         traverseEdge(obj, group);
 
         NativeObject *nobj = CallTraceHook(TraverseObjectFunctor(), this, obj,
                                            CheckGeneration::DoChecks, this, obj);
         if (!nobj)
             return;
 
@@ -1704,17 +1707,17 @@ GCMarker::GCMarker(JSRuntime* rt)
     started(false),
     strictCompartmentChecking(false)
 {
 }
 
 bool
 GCMarker::init(JSGCMode gcMode)
 {
-    return stack.init(gcMode) && weakKeys.init();
+    return stack.init(gcMode);
 }
 
 void
 GCMarker::start()
 {
     MOZ_ASSERT(!started);
     started = true;
     color = BLACK;
@@ -1732,17 +1735,18 @@ GCMarker::stop()
     MOZ_ASSERT(started);
     started = false;
 
     MOZ_ASSERT(!unmarkedArenaStackTop);
     MOZ_ASSERT(markLaterArenas == 0);
 
     /* Free non-ballast stack memory. */
     stack.reset();
-    weakKeys.clear();
+    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
+        zone->gcWeakKeys.clear();
 }
 
 void
 GCMarker::reset()
 {
     color = BLACK;
 
     stack.reset();
@@ -1772,23 +1776,36 @@ GCMarker::enterWeakMarkingMode()
     // During weak marking mode, we maintain a table mapping weak keys to
     // entries in known-live weakmaps.
     if (weakMapAction() == ExpandWeakMaps) {
         tag_ = TracerKindTag::WeakMarking;
 
         for (GCZoneGroupIter zone(runtime()); !zone.done(); zone.next()) {
             for (WeakMapBase* m : zone->gcWeakMapList) {
                 if (m->marked)
-                    m->markEphemeronEntries(this);
+                    (void) m->traceEntries(this);
             }
         }
     }
 }
 
 void
+GCMarker::leaveWeakMarkingMode()
+{
+    MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps && !linearWeakMarkingDisabled_,
+                  tag_ == TracerKindTag::WeakMarking);
+    tag_ = TracerKindTag::Marking;
+
+    // Table is expensive to maintain when not in weak marking mode, so we'll
+    // rebuild it upon entry rather than allow it to contain stale data.
+    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
+        zone->gcWeakKeys.clear();
+}
+
+void
 GCMarker::markDelayedChildren(ArenaHeader* aheader)
 {
     if (aheader->markOverflow) {
         bool always = aheader->allocatedDuringIncremental;
         aheader->markOverflow = 0;
 
         for (ArenaCellIterUnderGC i(aheader); !i.done(); i.next()) {
             TenuredCell* t = i.getCell();
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -25,16 +25,17 @@
 class JSLinearString;
 class JSRope;
 namespace js {
 class BaseShape;
 class GCMarker;
 class LazyScript;
 class NativeObject;
 class ObjectGroup;
+class WeakMapBase;
 namespace gc {
 struct ArenaHeader;
 } // namespace gc
 namespace jit {
 class JitCode;
 } // namespace jit
 
 static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
@@ -132,18 +133,16 @@ class MarkStack
     /* Grow the stack, ensuring there is space for at least count elements. */
     bool enlarge(unsigned count);
 
     void setGCMode(JSGCMode gcMode);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 };
 
-class WeakMapBase;
-
 namespace gc {
 
 struct WeakKeyTableHashPolicy {
     typedef JS::GCCellPtr Lookup;
     static HashNumber hash(const Lookup& v) { return mozilla::HashGeneric(v.asCell()); }
     static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; }
     static bool isEmpty(const JS::GCCellPtr& v) { return !v; }
     static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; }
@@ -152,22 +151,22 @@ struct WeakKeyTableHashPolicy {
 struct WeakMarkable {
     WeakMapBase* weakmap;
     JS::GCCellPtr key;
 
     WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg)
       : weakmap(weakmapArg), key(keyArg) {}
 };
 
-typedef Vector<WeakMarkable, 2, js::SystemAllocPolicy> WeakEntryVector;
+using WeakEntryVector = Vector<WeakMarkable, 2, js::SystemAllocPolicy>;
 
-typedef OrderedHashMap<JS::GCCellPtr,
-                       WeakEntryVector,
-                       WeakKeyTableHashPolicy,
-                       js::SystemAllocPolicy> WeakKeyTable;
+using WeakKeyTable = OrderedHashMap<JS::GCCellPtr,
+                                    WeakEntryVector,
+                                    WeakKeyTableHashPolicy,
+                                    js::SystemAllocPolicy>;
 
 } /* namespace gc */
 
 class GCMarker : public JSTracer
 {
   public:
     explicit GCMarker(JSRuntime* rt);
     bool init(JSGCMode gcMode);
@@ -204,27 +203,17 @@ class GCMarker : public JSTracer
     void setMarkColorBlack() {
         MOZ_ASSERT(isDrained());
         MOZ_ASSERT(color == gc::GRAY);
         color = gc::BLACK;
     }
     uint32_t markColor() const { return color; }
 
     void enterWeakMarkingMode();
-
-    void leaveWeakMarkingMode() {
-        MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps && !linearWeakMarkingDisabled_, tag_ == TracerKindTag::WeakMarking);
-        tag_ = TracerKindTag::Marking;
-
-        // Table is expensive to maintain when not in weak marking mode, so
-        // we'll rebuild it upon entry rather than allow it to contain stale
-        // data.
-        weakKeys.clear();
-    }
-
+    void leaveWeakMarkingMode();
     void abortLinearWeakMarking() {
         leaveWeakMarkingMode();
         linearWeakMarkingDisabled_ = true;
     }
 
     void delayMarkingArena(gc::ArenaHeader* aheader);
     void delayMarkingChildren(const void* thing);
     void markDelayedChildren(gc::ArenaHeader* aheader);
@@ -244,22 +233,16 @@ class GCMarker : public JSTracer
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 #ifdef DEBUG
     bool shouldCheckCompartments() { return strictCompartmentChecking; }
 #endif
 
     void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
 
-    /*
-     * Mapping from not yet marked keys to a vector of all values that the key
-     * maps to in any live weak map.
-     */
-    gc::WeakKeyTable weakKeys;
-
   private:
 #ifdef DEBUG
     void checkZone(void* p);
 #else
     void checkZone(void* p) {}
 #endif
 
     /*
@@ -286,18 +269,18 @@ class GCMarker : public JSTracer
     void repush(JSObject* obj) {
         MOZ_ASSERT(gc::TenuredCell::fromPointer(obj)->isMarked(markColor()));
         pushTaggedPtr(ObjectTag, obj);
     }
 
     template <typename T> void markAndTraceChildren(T* thing);
     template <typename T> void markAndPush(StackTag tag, T* thing);
     template <typename T> void markAndScan(T* thing);
-    template <typename T> void markPotentialEphemeronKeyHelper(T oldThing);
-    template <typename T> void markPotentialEphemeronKey(T* oldThing);
+    template <typename T> void markImplicitEdgesHelper(T oldThing);
+    template <typename T> void markImplicitEdges(T* oldThing);
     void eagerlyMarkChildren(JSLinearString* str);
     void eagerlyMarkChildren(JSRope* rope);
     void eagerlyMarkChildren(JSString* str);
     void eagerlyMarkChildren(LazyScript *thing);
     void eagerlyMarkChildren(Shape* shape);
     void lazilyMarkChildren(ObjectGroup* group);
 
     // We may not have concrete types yet, so this has to be out of the header.
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -60,17 +60,17 @@ Zone::~Zone()
 
     js_delete(debuggers);
     js_delete(jitZone_);
 }
 
 bool Zone::init(bool isSystemArg)
 {
     isSystem = isSystemArg;
-    return uniqueIds_.init() && gcZoneGroupEdges.init();
+    return uniqueIds_.init() && gcZoneGroupEdges.init() && gcWeakKeys.init();
 }
 
 void
 Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
 {
     if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
         jitUsingBarriers_ = needs;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -294,16 +294,22 @@ struct Zone : public JS::shadow::Zone,
     typedef js::Vector<js::gc::Cell*, 0, js::SystemAllocPolicy> GrayRootVector;
     GrayRootVector gcGrayRoots;
 
     // This zone's weak edges found via graph traversal during marking,
     // preserved for re-scanning during sweeping.
     using WeakEdges = js::Vector<js::gc::TenuredCell**, 0, js::SystemAllocPolicy>;
     WeakEdges gcWeakRefs;
 
+    /*
+     * Mapping from not yet marked keys to a vector of all values that the key
+     * maps to in any live weak map.
+     */
+    js::gc::WeakKeyTable gcWeakKeys;
+
     // A set of edges from this zone to other zones.
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     ZoneSet gcZoneGroupEdges;
 
     // Malloc counter to measure memory pressure for GC scheduling. It runs from
     // gcMaxMallocBytes down to zero. This counter should be used only when it's
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -405,17 +405,17 @@ NativeRegExpMacroAssembler::GenerateCode
         Label grow_failed;
 
         masm.movePtr(ImmPtr(runtime), temp1);
 
         // Save registers before calling C function
         LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile());
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
         volatileRegs.add(Register::FromCode(Registers::lr));
-#elif defined(JS_CODEGEN_MIPS32)
+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
         volatileRegs.add(Register::FromCode(Registers::ra));
 #endif
         volatileRegs.takeUnchecked(temp0);
         volatileRegs.takeUnchecked(temp1);
         masm.PushRegsInMask(volatileRegs);
 
         masm.setupUnalignedABICall(temp0);
         masm.passABIArg(temp1);
@@ -1338,17 +1338,17 @@ NativeRegExpMacroAssembler::CheckSpecial
     }
 }
 
 bool
 NativeRegExpMacroAssembler::CanReadUnaligned()
 {
 #if defined(JS_CODEGEN_ARM)
     return !jit::HasAlignmentFault();
-#elif defined(JS_CODEGEN_MIPS32)
+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     return false;
 #else
     return true;
 #endif
 }
 
 const uint8_t
 NativeRegExpMacroAssembler::word_character_map[] =
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -76,16 +76,18 @@ def main(argv):
                   metavar='FILE',
                   help='Write a list of failed tests to [FILE]')
     op.add_option('-r', '--read-tests', dest='read_tests', metavar='FILE',
                   help='Run test files listed in [FILE]')
     op.add_option('-R', '--retest', dest='retest', metavar='FILE',
                   help='Retest using test list file [FILE]')
     op.add_option('-g', '--debug', action='store_const', const='gdb', dest='debugger',
                   help='Run a single test under the gdb debugger')
+    op.add_option('-G', '--debug-rr', action='store_const', const='rr', dest='debugger',
+                  help='Run a single test under the rr debugger')
     op.add_option('--debugger', type='string',
                   help='Run a single test under the specified debugger')
     op.add_option('--valgrind', dest='valgrind', action='store_true',
                   help='Enable the |valgrind| flag, if valgrind is in $PATH.')
     op.add_option('--valgrind-all', dest='valgrind_all', action='store_true',
                   help='Run all tests with valgrind, if valgrind is in $PATH.')
     op.add_option('--jitflags', dest='jitflags', default='none', type='string',
                   help='IonMonkey option combinations. One of all, debug,'
@@ -261,21 +263,25 @@ def main(argv):
                 print('    {}'.format(tc.path))
             sys.exit(1)
 
         tc = job_list[0]
         if options.debugger == 'gdb':
             debug_cmd = ['gdb', '--args']
         elif options.debugger == 'lldb':
             debug_cmd = ['lldb', '--']
+        elif options.debugger == 'rr':
+            debug_cmd = ['rr', 'record']
         else:
             debug_cmd = options.debugger.split()
 
         with change_env(test_environment):
             subprocess.call(debug_cmd + tc.command(prefix, jittests.LIB_DIR))
+            if options.debugger == 'rr':
+                subprocess.call(['rr', 'replay'])
         sys.exit()
 
     try:
         ok = None
         if options.remote:
             ok = jittests.run_tests_remote(job_list, prefix, options)
         else:
             with change_env(test_environment):
--- a/js/src/jit-test/tests/asm.js/testBasic.js
+++ b/js/src/jit-test/tests/asm.js/testBasic.js
@@ -1,11 +1,12 @@
 // |jit-test| test-also-noasmjs
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
+load(libdir + "class.js");
 
 assertAsmTypeFail(USE_ASM);
 assertAsmTypeFail(USE_ASM + 'return');
 assertAsmTypeFail(USE_ASM + 'function f() 0');
 assertAsmTypeFail(USE_ASM + 'function f(){}');
 assertAsmTypeFail(USE_ASM + 'function f(){} return 0');
 assertAsmTypeFail(USE_ASM + 'function f() 0; return 0');
 assertAsmTypeFail(USE_ASM + 'function f(){} return g');
@@ -126,20 +127,22 @@ function assertTypeFailInEval(str)
     options("werror");
 }
 assertTypeFailInEval('function f({}) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function f({global}) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function f(global, {imports}) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function f(g = 2) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function *f() { "use asm"; function g() {} return g }');
 assertTypeFailInEval('f => { "use asm"; function g() {} return g }');
-assertTypeFailInEval('class f { constructor() {"use asm"; return {}} }');
 assertTypeFailInEval('var f = { method() {"use asm"; return {}} }');
-assertAsmTypeFail(USE_ASM + 'class c { constructor() {}}; return c;');
 assertAsmTypeFail(USE_ASM + 'return {m() {}};');
+if (classesEnabled()) {
+    assertTypeFailInEval('class f { constructor() {"use asm"; return {}} }');
+    assertAsmTypeFail(USE_ASM + 'class c { constructor() {}}; return c;');
+}
 
 assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0; (i for (x in [1,2,3])) } return f');
 assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0; [i for (x in [1,2,3])] } return f');
 assertAsmTypeFail(USE_ASM + 'function f() { (x for (x in [1,2,3])) } return f');
 assertAsmTypeFail(USE_ASM + 'function f() { [x for (x in [1,2,3])] } return f');
 assertTypeFailInEval('function f() { "use asm"; function g(i) {i=i|0; (i for (x in [1,2,3])) } return g }');
 assertTypeFailInEval('function f() { "use asm"; function g(i) {i=i|0; [i for (x in [1,2,3])] } return g }');
 assertTypeFailInEval('function f() { "use asm"; function g() { (x for (x in [1,2,3])) } return g }');
--- a/js/src/jit-test/tests/debug/Frame-onStep-11.js
+++ b/js/src/jit-test/tests/debug/Frame-onStep-11.js
@@ -28,9 +28,9 @@ dbg.onDebuggerStatement = function(frame
     if (foundLine != debugLine && this.script.getLineOffsets(foundLine).indexOf(this.offset) >= 0) {
       foundLines += "," + (foundLine - debugLine);
     }
   };
 };
 
 g.f();
 
-assertEq(foundLines, ",1,2,3,4,6,7,8,10");
+assertEq(foundLines, ",1,2,3,4,5,6,7,8,10");
--- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-02.js
+++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-02.js
@@ -13,9 +13,9 @@ Debugger(global).onDebuggerStatement = f
     });
 };
 
 global.log = '';
 global.eval("function f(n){var w0,x1=3,y2=4,z3=9} debugger;");
 global.f(3);
 
 // Should have hit each variable declared.
-assertEq(global.log, "18 21 26 31 35 ");
+assertEq(global.log, "21 26 31 35 ");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/bug1186973.js
@@ -0,0 +1,15 @@
+// |jit-test| error: cache does not have the same size
+load(libdir + 'bytecode-cache.js');
+
+var test = (function () {
+  function f(x) {
+    function ifTrue() {};
+    function ifFalse() {};
+
+    if (generation % 2 == 0)
+      return ifTrue();
+    return ifFalse();
+  }
+  return f.toSource() + "; f()";
+})();
+evalWithCache(test, { assertEqBytecode: true, assertEqResult : true });
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3236,18 +3236,18 @@ IonBuilder::whileOrForInLoop(jssrcnote* 
 
     pc = bodyEnd;
     return ControlStatus_Jumped;
 }
 
 IonBuilder::ControlStatus
 IonBuilder::forLoop(JSOp op, jssrcnote* sn)
 {
-    // Skip the NOP or POP.
-    MOZ_ASSERT(op == JSOP_POP || op == JSOP_NOP);
+    // Skip the NOP.
+    MOZ_ASSERT(op == JSOP_NOP);
     pc = GetNextPc(pc);
 
     jsbytecode* condpc = pc + GetSrcNoteOffset(sn, 0);
     jsbytecode* updatepc = pc + GetSrcNoteOffset(sn, 1);
     jsbytecode* ifne = pc + GetSrcNoteOffset(sn, 2);
     jsbytecode* exitpc = GetNextPc(ifne);
 
     // for loops have the following structures:
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -107,17 +107,17 @@ JitOptions::JitOptions()
     // Toggle whether eager scalar replacement is globally disabled.
     SET_DEFAULT(disableScalarReplacement, false);
 
     // Toggles whether shared stubs are used in Ionmonkey.
     SET_DEFAULT(disableSharedStubs, true);
 
     // Toggles whether sincos optimization is globally disabled.
     // See bug984018: The MacOS is the only one that has the sincos fast.
-    #if defined(XP_DARWIN)
+    #if defined(XP_MACOSX)
         SET_DEFAULT(disableSincos, false);
     #else
         SET_DEFAULT(disableSincos, true);
     #endif
 
     // Toggles whether sink code motion is globally disabled.
     SET_DEFAULT(disableSink, true);
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -196,17 +196,18 @@ MSG_DEF(JSMSG_BAD_CONTINUE,            0
 MSG_DEF(JSMSG_BAD_DESTRUCT_ASS,        0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
 MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET,     0, JSEXN_SYNTAXERR, "invalid destructuring target")
 MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS,     0, JSEXN_SYNTAXERR, "destructuring patterns in assignments can't be parenthesized")
 MSG_DEF(JSMSG_BAD_DESTRUCT_DECL,       0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
 MSG_DEF(JSMSG_BAD_DUP_ARGS,            0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,       0, JSEXN_SYNTAXERR, "invalid for each loop")
 MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,        0, JSEXN_SYNTAXERR, "invalid for/in left-hand side")
 MSG_DEF(JSMSG_BAD_GENERATOR_RETURN,    1, JSEXN_TYPEERR, "generator function {0} returns a value")
-MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX,    1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
+MSG_DEF(JSMSG_BAD_YIELD_SYNTAX,        0, JSEXN_SYNTAXERR, "yield expression must be parenthesized")
+MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX,    0, JSEXN_SYNTAXERR, "generator expression must be parenthesized")
 MSG_DEF(JSMSG_BAD_GENEXP_BODY,         1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,       0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_BAD_METHOD_DEF,          0, JSEXN_SYNTAXERR, "bad method definition")
 MSG_DEF(JSMSG_BAD_OCTAL,               1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
 MSG_DEF(JSMSG_BAD_OPERAND,             1, JSEXN_SYNTAXERR, "invalid {0} operand")
 MSG_DEF(JSMSG_BAD_PROP_ID,             0, JSEXN_SYNTAXERR, "invalid property id")
 MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD,     1, JSEXN_SYNTAXERR, "{0} not in function")
 MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,       1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4223,39 +4223,43 @@ js::gc::MarkingValidator::nonIncremental
      * Temporarily clear the weakmaps' mark flags for the compartments we are
      * collecting.
      */
 
     WeakMapSet markedWeakMaps;
     if (!markedWeakMaps.init())
         return;
 
+    /*
+     * For saving, smush all of the keys into one big table and split them back
+     * up into per-zone tables when restoring.
+     */
+    gc::WeakKeyTable savedWeakKeys;
+    if (!savedWeakKeys.init())
+        return;
+
     for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
         if (!WeakMapBase::saveZoneMarkedWeakMaps(zone, markedWeakMaps))
             return;
-    }
-
-    gc::WeakKeyTable savedWeakKeys;
-    if (!savedWeakKeys.init())
-        return;
-
-    for (gc::WeakKeyTable::Range r = gc->marker.weakKeys.all(); !r.empty(); r.popFront()) {
-        AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!savedWeakKeys.put(Move(r.front().key), Move(r.front().value)))
-            oomUnsafe.crash("saving weak keys table for validator");
+
+        for (gc::WeakKeyTable::Range r = zone->gcWeakKeys.all(); !r.empty(); r.popFront()) {
+            AutoEnterOOMUnsafeRegion oomUnsafe;
+            if (!savedWeakKeys.put(Move(r.front().key), Move(r.front().value)))
+                oomUnsafe.crash("saving weak keys table for validator");
+        }
+
+        zone->gcWeakKeys.clear();
     }
 
     /*
      * After this point, the function should run to completion, so we shouldn't
      * do anything fallible.
      */
     initialized = true;
 
-    gc->marker.weakKeys.clear();
-
     /* Re-do all the marking, but non-incrementally. */
     js::gc::State state = gc->incrementalState;
     gc->incrementalState = MARK_ROOTS;
 
     {
         gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_MARK);
 
         {
@@ -4306,24 +4310,27 @@ js::gc::MarkingValidator::nonIncremental
 
     /* Take a copy of the non-incremental mark state and restore the original. */
     for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
         ChunkBitmap* bitmap = &chunk->bitmap;
         ChunkBitmap* entry = map.lookup(chunk)->value();
         Swap(*entry, *bitmap);
     }
 
-    for (GCZonesIter zone(runtime); !zone.done(); zone.next())
+    for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
         WeakMapBase::unmarkZone(zone);
+        zone->gcWeakKeys.clear();
+    }
+
     WeakMapBase::restoreMarkedWeakMaps(markedWeakMaps);
 
-    gc->marker.weakKeys.clear();
     for (gc::WeakKeyTable::Range r = savedWeakKeys.all(); !r.empty(); r.popFront()) {
         AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!gc->marker.weakKeys.put(Move(r.front().key), Move(r.front().value)))
+        Zone* zone = gc::TenuredCell::fromPointer(r.front().key.asCell())->zone();
+        if (!zone->gcWeakKeys.put(Move(r.front().key), Move(r.front().value)))
             oomUnsafe.crash("restoring weak keys table for validator");
     }
 
     gc->incrementalState = state;
 }
 
 void
 js::gc::MarkingValidator::validate()
@@ -4982,35 +4989,27 @@ GCRuntime::beginSweepingZoneGroup()
         if (rt->sweepZoneCallback)
             rt->sweepZoneCallback(zone);
 
         zone->gcLastZoneGroupIndex = zoneGroupIndex;
     }
 
     validateIncrementalMarking();
 
-    /* Clear out this zone group's keys from the weakKeys table, to prevent later accesses. */
-    for (WeakKeyTable::Range r = marker.weakKeys.all(); !r.empty(); ) {
-        auto key(r.front().key);
-        r.popFront();
-        if (gc::TenuredCell::fromPointer(key.asCell())->zone()->isGCSweeping()) {
-            bool found;
-            marker.weakKeys.remove(key, &found);
-            MOZ_ASSERT(found);
-        }
-    }
-
-    /* Clear all weakrefs that point to unmarked things. */
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
+        /* Clear all weakrefs that point to unmarked things. */
         for (auto edge : zone->gcWeakRefs) {
             /* Edges may be present multiple times, so may already be nulled. */
             if (*edge && IsAboutToBeFinalizedDuringSweep(**edge))
                 *edge = nullptr;
         }
         zone->gcWeakRefs.clear();
+
+        /* No need to look up any more weakmap keys from this zone group. */
+        zone->gcWeakKeys.clear();
     }
 
     FreeOp fop(rt);
     SweepAtomsTask sweepAtomsTask(rt);
     SweepInnerViewsTask sweepInnerViewsTask(rt);
     SweepCCWrappersTask sweepCCWrappersTask(rt);
     SweepBaseShapesTask sweepBaseShapesTask(rt);
     SweepInitialShapesTask sweepInitialShapesTask(rt);
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -31,45 +31,16 @@ WeakMapBase::WeakMapBase(JSObject* memOf
 }
 
 WeakMapBase::~WeakMapBase()
 {
     MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
 }
 
 void
-WeakMapBase::trace(JSTracer* tracer)
-{
-    MOZ_ASSERT(isInList());
-    if (tracer->isMarkingTracer()) {
-        marked = true;
-        if (tracer->weakMapAction() == DoNotTraceWeakMaps) {
-            // Do not trace any WeakMap entries at this time. Just record the
-            // fact that the WeakMap has been marked. Entries are marked in the
-            // iterative marking phase by markAllIteratively(), after as many
-            // keys as possible have been marked already.
-        } else {
-            MOZ_ASSERT(tracer->weakMapAction() == ExpandWeakMaps);
-            markEphemeronEntries(tracer);
-        }
-    } else {
-        // If we're not actually doing garbage collection, the keys won't be marked
-        // nicely as needed by the true ephemeral marking algorithm --- custom tracers
-        // such as the cycle collector must use their own means for cycle detection.
-        // So here we do a conservative approximation: pretend all keys are live.
-        if (tracer->weakMapAction() == DoNotTraceWeakMaps)
-            return;
-
-        nonMarkingTraceValues(tracer);
-        if (tracer->weakMapAction() == TraceWeakMapKeysValues)
-            nonMarkingTraceKeys(tracer);
-    }
-}
-
-void
 WeakMapBase::unmarkZone(JS::Zone* zone)
 {
     for (WeakMapBase* m : zone->gcWeakMapList)
         m->marked = false;
 }
 
 void
 WeakMapBase::markAll(JS::Zone* zone, JSTracer* tracer)
@@ -82,17 +53,17 @@ WeakMapBase::markAll(JS::Zone* zone, JST
     }
 }
 
 bool
 WeakMapBase::markZoneIteratively(JS::Zone* zone, JSTracer* tracer)
 {
     bool markedAny = false;
     for (WeakMapBase* m : zone->gcWeakMapList) {
-        if (m->marked && m->markIteratively(tracer))
+        if (m->marked && m->traceEntries(tracer))
             markedAny = true;
     }
     return markedAny;
 }
 
 bool
 WeakMapBase::findInterZoneEdges(JS::Zone* zone)
 {
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -22,39 +22,37 @@ namespace js {
 
 class WeakMapBase;
 
 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
 // a key is collected, the table entry disappears, dropping its reference to the value.
 //
 // More precisely:
 //
-//     A WeakMap entry is collected if and only if either the WeakMap or the entry's key
-//     is collected. If an entry is not collected, it remains in the WeakMap and it has a
-//     strong reference to the value.
+//     A WeakMap entry is live if and only if both the WeakMap and the entry's key
+//     are live. An entry holds a strong reference to its value.
 //
-// You must call this table's 'trace' method when the object of which it is a part is
-// reached by the garbage collection tracer. Once a table is known to be live, the
-// implementation takes care of the iterative marking needed for weak tables and removing
-// table entries when collection is complete.
+// You must call this table's 'trace' method when its owning object is reached
+// by the garbage collection tracer. Once a table is known to be live, the
+// implementation takes care of the special weak marking (ie, marking through
+// the implicit edges stored in the map) and of removing (sweeping) table
+// entries when collection is complete.
 
 typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy> WeakMapSet;
 
-// Common base class for all WeakMap specializations. The collector uses this to call
-// their markIteratively and sweep methods.
+// Common base class for all WeakMap specializations, used for calling
+// subclasses' GC-related methods.
 class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
 {
-    friend void js::GCMarker::enterWeakMarkingMode();
+    friend class js::GCMarker;
 
   public:
     WeakMapBase(JSObject* memOf, JS::Zone* zone);
     virtual ~WeakMapBase();
 
-    void trace(JSTracer* tracer);
-
     // Garbage collector entry points.
 
     // Unmark all weak maps in a zone.
     static void unmarkZone(JS::Zone* zone);
 
     // Mark all the weakmaps in a zone.
     static void markAll(JS::Zone* zone, JSTracer* tracer);
 
@@ -75,33 +73,32 @@ class WeakMapBase : public mozilla::Link
     static void traceAllMappings(WeakMapTracer* tracer);
 
     // Save information about which weak maps are marked for a zone.
     static bool saveZoneMarkedWeakMaps(JS::Zone* zone, WeakMapSet& markedWeakMaps);
 
     // Restore information about which weak maps are marked for many zones.
     static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
 
-    // Any weakmap key types that want to participate in the non-iterative
-    // ephemeron marking must override this method.
-    virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
-
-    virtual void markEphemeronEntries(JSTracer* trc) = 0;
-
   protected:
     // Instance member functions called by the above. Instantiations of WeakMap override
     // these with definitions appropriate for their Key and Value types.
-    virtual void nonMarkingTraceKeys(JSTracer* tracer) = 0;
-    virtual void nonMarkingTraceValues(JSTracer* tracer) = 0;
-    virtual bool markIteratively(JSTracer* tracer) = 0;
+    virtual void trace(JSTracer* tracer) = 0;
     virtual bool findZoneEdges() = 0;
     virtual void sweep() = 0;
     virtual void traceMappings(WeakMapTracer* tracer) = 0;
     virtual void finish() = 0;
 
+    // Any weakmap key types that want to participate in the non-iterative
+    // ephemeron marking must override this method.
+    virtual void traceEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
+
+    virtual bool traceEntries(JSTracer* trc) = 0;
+
+  protected:
     // Object that this weak map is part of, if any.
     HeapPtrObject memberOf;
 
     // Zone containing this weak map.
     JS::Zone* zone;
 
     // Whether this object has been traced during garbage collection.
     bool marked;
@@ -115,40 +112,42 @@ static T extractUnbarriered(WriteBarrier
 template <typename T>
 static T* extractUnbarriered(T* v)
 {
     return v;
 }
 
 template <class Key, class Value,
           class HashPolicy = DefaultHasher<Key> >
-class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
+class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
+                public WeakMapBase
 {
   public:
     typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
     typedef typename Base::Enum Enum;
     typedef typename Base::Lookup Lookup;
+    typedef typename Base::Entry Entry;
     typedef typename Base::Range Range;
     typedef typename Base::Ptr Ptr;
     typedef typename Base::AddPtr AddPtr;
 
     explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr)
         : Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()->zone()) { }
 
     bool init(uint32_t len = 16) {
         if (!Base::init(len))
             return false;
         zone->gcWeakMapList.insertFront(this);
         marked = JS::IsIncrementalGCInProgress(zone->runtimeFromMainThread());
         return true;
     }
 
     // Overwritten to add a read barrier to prevent an incorrectly gray value
-    // from escaping the weak map. See the comment before UnmarkGrayChildren in
-    // gc/Marking.cpp
+    // from escaping the weak map. See the UnmarkGrayTracer::onChild comment in
+    // gc/Marking.cpp.
     Ptr lookup(const Lookup& l) const {
         Ptr p = Base::lookup(l);
         if (p)
             exposeGCThingToActiveJS(p->value());
         return p;
     }
 
     AddPtr lookupForAdd(const Lookup& l) const {
@@ -163,113 +162,144 @@ class WeakMap : public HashMap<Key, Valu
         if (p)
             exposeGCThingToActiveJS(p->value());
         return p;
     }
 
     // Resolve ambiguity with LinkedListElement<>::remove.
     using Base::remove;
 
-    // The WeakMap and some part of the key are marked. If the entry is marked
-    // according to the exact semantics of this WeakMap, then mark the value.
-    // (For a standard WeakMap, the entry is marked if either the key its
-    // delegate is marked.)
-    void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey) override
+    // Trace a WeakMap entry based on 'markedCell' getting marked, where
+    // 'origKey' is the key in the weakmap. These will probably be the same,
+    // but can be different eg when markedCell is a delegate for origKey.
+    //
+    // This implementation does not use 'markedCell'; it looks up origKey and
+    // checks the mark bits on everything it cares about, one of which will be
+    // markedCell. But a subclass might use it to optimize the liveness check.
+    void traceEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey) override
     {
         MOZ_ASSERT(marked);
 
         gc::Cell* l = origKey.asCell();
         Ptr p = Base::lookup(reinterpret_cast<Lookup&>(l));
         MOZ_ASSERT(p.found());
 
         Key key(p->key());
+        MOZ_ASSERT((markedCell == extractUnbarriered(key)) || (markedCell == getDelegate(key)));
         if (gc::IsMarked(&key)) {
             TraceEdge(trc, &p->value(), "ephemeron value");
         } else if (keyNeedsMark(key)) {
             TraceEdge(trc, &p->value(), "WeakMap ephemeron value");
             TraceEdge(trc, &key, "proxy-preserved WeakMap ephemeron key");
             MOZ_ASSERT(key == p->key()); // No moving
         }
         key.unsafeSet(nullptr); // Prevent destructor from running barriers.
     }
 
+    void trace(JSTracer* trc) override {
+        MOZ_ASSERT(isInList());
+
+        if (trc->isMarkingTracer())
+            marked = true;
+
+        if (trc->weakMapAction() == DoNotTraceWeakMaps)
+            return;
+
+        if (!trc->isMarkingTracer()) {
+            // Trace keys only if weakMapAction() says to.
+            if (trc->weakMapAction() == TraceWeakMapKeysValues) {
+                for (Enum e(*this); !e.empty(); e.popFront()) {
+                    Key key(e.front().key());
+                    TraceEdge(trc, &key, "WeakMap entry key");
+                    if (key != e.front().key())
+                        e.rekeyFront(key);
+                }
+            }
+
+            // Always trace all values (unless weakMapAction() is
+            // DoNotTraceWeakMaps).
+            for (Range r = Base::all(); !r.empty(); r.popFront())
+                TraceEdge(trc, &r.front().value(), "WeakMap entry value");
+
+            return;
+        }
+
+        // Marking tracer
+        MOZ_ASSERT(trc->weakMapAction() == ExpandWeakMaps);
+        (void) traceEntries(trc);
+    }
+
   protected:
     static void addWeakEntry(JSTracer* trc, JS::GCCellPtr key, gc::WeakMarkable markable)
     {
         GCMarker& marker = *static_cast<GCMarker*>(trc);
+        Zone* zone = key.asCell()->asTenured().zone();
 
-        auto p = marker.weakKeys.get(key);
+        auto p = zone->gcWeakKeys.get(key);
         if (p) {
             gc::WeakEntryVector& weakEntries = p->value;
             if (!weakEntries.append(Move(markable)))
                 marker.abortLinearWeakMarking();
         } else {
             gc::WeakEntryVector weakEntries;
             MOZ_ALWAYS_TRUE(weakEntries.append(Move(markable)));
-            if (!marker.weakKeys.put(JS::GCCellPtr(key), Move(weakEntries)))
+            if (!zone->gcWeakKeys.put(JS::GCCellPtr(key), Move(weakEntries)))
                 marker.abortLinearWeakMarking();
         }
     }
 
-    void markEphemeronEntries(JSTracer* trc) override {
+    bool traceEntries(JSTracer* trc) override {
         MOZ_ASSERT(marked);
+
+        bool markedAny = false;
+
         for (Enum e(*this); !e.empty(); e.popFront()) {
-            Key key(e.front().key());
+            Entry& entry(e.front());
+            Key key(entry.key());
 
             // If the entry is live, ensure its key and value are marked.
-            if (gc::IsMarked(&key)) {
-                (void) markValue(trc, &e.front().value());
-                MOZ_ASSERT(key == e.front().key()); // No moving
-            } else if (keyNeedsMark(key)) {
-                TraceEdge(trc, &e.front().value(), "WeakMap entry value");
+
+            bool keyIsMarked = gc::IsMarked(&key);
+            if (!keyIsMarked && keyNeedsMark(key)) {
                 TraceEdge(trc, &key, "proxy-preserved WeakMap entry key");
-                MOZ_ASSERT(key == e.front().key()); // No moving
+                keyIsMarked = true;
+                markedAny = true;
+            }
+
+            if (keyIsMarked) {
+                if (!gc::IsMarked(&entry.value())) {
+                    TraceEdge(trc, &entry.value(), "WeakMap entry value");
+                    markedAny = true;
+                }
+
+                if (e.front().key() != key)
+                    e.rekeyFront(key);
             } else if (trc->isWeakMarkingTracer()) {
-                // Entry is not yet known to be live. Record it in the list of
-                // weak keys. Or rather, record this weakmap and the lookup key
-                // so we can repeat the lookup when we need to (to allow
-                // incremental weak marking, we can't just store a pointer to
-                // the entry.) Also record the delegate, if any, because
-                // marking the delegate must also mark the entry.
+                // Entry is not yet known to be live. Record this weakmap and
+                // the lookup key in the list of weak keys. Also record the
+                // delegate, if any, because marking the delegate also marks
+                // the entry.
                 JS::GCCellPtr weakKey(extractUnbarriered(key));
                 gc::WeakMarkable markable(this, weakKey);
                 addWeakEntry(trc, weakKey, markable);
                 if (JSObject* delegate = getDelegate(key))
                     addWeakEntry(trc, JS::GCCellPtr(delegate), markable);
+                MOZ_ASSERT(key == e.front().key()); // No moving
             }
+
             key.unsafeSet(nullptr); // Prevent destructor from running barriers.
         }
+
+        return markedAny;
     }
 
   private:
     void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
 
-    bool markValue(JSTracer* trc, Value* x) {
-        if (gc::IsMarked(x))
-            return false;
-        TraceEdge(trc, x, "WeakMap entry value");
-        MOZ_ASSERT(gc::IsMarked(x));
-        return true;
-    }
-
-    void nonMarkingTraceKeys(JSTracer* trc) override {
-        for (Enum e(*this); !e.empty(); e.popFront()) {
-            Key key(e.front().key());
-            TraceEdge(trc, &key, "WeakMap entry key");
-            if (key != e.front().key())
-                entryMoved(e, key);
-        }
-    }
-
-    void nonMarkingTraceValues(JSTracer* trc) override {
-        for (Range r = Base::all(); !r.empty(); r.popFront())
-            TraceEdge(trc, &r.front().value(), "WeakMap entry value");
-    }
-
     JSObject* getDelegate(JSObject* key) const {
         JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp;
         return op ? op(key) : nullptr;
     }
 
     JSObject* getDelegate(gc::Cell* cell) const {
         return nullptr;
     }
@@ -282,51 +312,29 @@ class WeakMap : public HashMap<Key, Valu
          */
         return delegate && gc::IsMarkedUnbarriered(&delegate);
     }
 
     bool keyNeedsMark(gc::Cell* cell) const {
         return false;
     }
 
-    bool markIteratively(JSTracer* trc) override {
-        bool markedAny = false;
-        for (Enum e(*this); !e.empty(); e.popFront()) {
-            /* If the entry is live, ensure its key and value are marked. */
-            Key key(e.front().key());
-            if (gc::IsMarked(const_cast<Key*>(&key))) {
-                if (markValue(trc, &e.front().value()))
-                    markedAny = true;
-                if (e.front().key() != key)
-                    entryMoved(e, key);
-            } else if (keyNeedsMark(key)) {
-                TraceEdge(trc, &e.front().value(), "WeakMap entry value");
-                TraceEdge(trc, &key, "proxy-preserved WeakMap entry key");
-                if (e.front().key() != key)
-                    entryMoved(e, key);
-                markedAny = true;
-            }
-            key.unsafeSet(nullptr); // Prevent destructor from running barriers.
-        }
-        return markedAny;
-    }
-
     bool findZoneEdges() override {
         // This is overridden by ObjectValueMap.
         return true;
     }
 
     void sweep() override {
         /* Remove all entries whose keys remain unmarked. */
         for (Enum e(*this); !e.empty(); e.popFront()) {
             Key k(e.front().key());
             if (gc::IsAboutToBeFinalized(&k))
                 e.removeFront();
             else if (k != e.front().key())
-                entryMoved(e, k);
+                e.rekeyFront(k);
         }
         /*
          * Once we've swept, all remaining edges should stay within the
          * known-live part of the graph.
          */
         assertEntriesNotAboutToBeFinalized();
     }
 
@@ -342,21 +350,16 @@ class WeakMap : public HashMap<Key, Valu
             if (key && value) {
                 tracer->trace(memberOf,
                               JS::GCCellPtr(r.front().key().get()),
                               JS::GCCellPtr(r.front().value().get()));
             }
         }
     }
 
-    /* Rekey an entry when moved, ensuring we do not trigger barriers. */
-    void entryMoved(Enum& e, const Key& k) {
-        e.rekeyFront(k);
-    }
-
   protected:
     void assertEntriesNotAboutToBeFinalized() {
 #if DEBUG
         for (Range r = Base::all(); !r.empty(); r.popFront()) {
             Key k(r.front().key());
             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k));
             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
             MOZ_ASSERT(k == r.front().key());
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1311,18 +1311,23 @@ Evaluate(JSContext* cx, unsigned argc, V
         }
     }
 
     if (saveBytecode) {
         // If we are both loading and saving, we assert that we are going to
         // replace the current bytecode by the same stream of bytes.
         if (loadBytecode && assertEqBytecode) {
             if (saveLength != loadLength) {
+                char loadLengthStr[16];
+                JS_snprintf(loadLengthStr, sizeof(loadLengthStr), "%u", loadLength);
+                char saveLengthStr[16];
+                JS_snprintf(saveLengthStr, sizeof(saveLengthStr), "%u", saveLength);
+
                 JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_CACHE_EQ_SIZE_FAILED,
-                                     loadLength, saveLength);
+                                     loadLengthStr, saveLengthStr);
                 return false;
             }
 
             if (!PodEqual(loadBuffer, saveBuffer.get(), loadLength)) {
                 JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                                      JSSMSG_CACHE_EQ_CONTENT_FAILED);
                 return false;
             }
@@ -5229,19 +5234,20 @@ PrintStackTrace(JSContext* cx, HandleVal
         exnObj = UncheckedUnwrap(exnObj);
         ac.emplace(cx, exnObj);
     }
 
     // Ignore non-ErrorObject thrown by |throw| statement.
     if (!exnObj->is<ErrorObject>())
         return true;
 
+    // Exceptions thrown while compiling top-level script have no stack.
     RootedObject stackObj(cx, exnObj->as<ErrorObject>().stack());
     if (!stackObj)
-        return false;
+        return true;
 
     RootedString stackStr(cx);
     if (!BuildStackString(cx, stackObj, &stackStr, 2))
         return false;
 
     UniquePtr<char[], JS::FreePolicy> stack(JS_EncodeStringToUTF8(cx, stackStr));
     if (!stack)
         return false;
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Global/bug-320887.js
@@ -0,0 +1,15 @@
+// `var x` should not call the getter of an existing global property.
+
+var hit = 0;
+Object.defineProperty(this, "x", {
+    get: function () { return ++hit; },
+    configurable: true
+});
+eval("var x;");
+assertEq(hit, 0);
+
+// The declaration should not have redefined the global x, either.
+assertEq(x, 1);
+assertEq(x, 2);
+
+reportCompare(0, 0);
--- a/js/src/tests/ecma_6/Class/derivedConstructorInlining.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorInlining.js
@@ -1,8 +1,10 @@
+var test = `
+
 // Since we (for now!) can't emit jitcode for derived class statements. Make
 // sure we can correctly invoke derived class constructors.
 
 class foo extends null {
     constructor() {
         // Anything that tests |this| should throw, so just let it run off the
         // end.
     }
@@ -10,10 +12,15 @@ class foo extends null {
 
 function intermediate() {
     new foo();
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(intermediate, "|this|");
 
+`;
+
+if (classesEnabled())
+    eval(test);
+
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorReturnPrimitive.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorReturnPrimitive.js
@@ -1,15 +1,22 @@
+var test = `
+
 class foo extends null {
     constructor() {
         // Returning a primitive is a TypeError in derived constructors. This
         // ensures that super() can take the return value directly, without
         // checking it. Use |null| here, as a tricky check to make sure we
         // didn't lump it in with the object check, somehow.
         return null;
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "return");
 
+`;
+
+if (classesEnabled())
+    eval(test);
+
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZExplicitThis.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZExplicitThis.js
@@ -1,18 +1,19 @@
-function pleaseRunMyCode() { }
+var test = `
 
 class foo extends null {
     constructor() {
-        // Just bareword |this| is DCEd by the BytecodeEmitter. Your guess as
-        // to why we think this is a good idea is as good as mine. In order to
-        // combat this inanity, make it a function arg, so we have to compute
-        // it.
-        pleaseRunMyCode(this);
+        this;
         assertEq(false, true);
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "|this|");
 
+`;
+
+if (classesEnabled())
+    eval(test);
+
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZOffEdge.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZOffEdge.js
@@ -1,11 +1,18 @@
+var test = `
+
 class foo extends null {
     constructor() {
         // Let it fall off the edge and throw.
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "|this|");
 
+`;
+
+if (classesEnabled())
+    eval(test);
+
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnObject.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnObject.js
@@ -1,13 +1,20 @@
+var test = `
+
 class foo extends null {
     constructor() {
         // If you return an object, we don't care that |this| went
         // uninitialized
         return {};
     }
 }
 
 for (let i = 0; i < 1100; i++)
     new foo();
 
+`;
+
+if (classesEnabled())
+    eval(test);
+
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnUndefined.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnUndefined.js
@@ -1,13 +1,20 @@
+var test = `
+
 class foo extends null {
     constructor() {
         // Explicit returns of undefined should act the same as falling off the
         // end of the function. That is to say, they should throw.
         return undefined;
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "|this|");
 
+`;
+
+if (classesEnabled())
+    eval(test);
+
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Statements/try-completion.js
@@ -0,0 +1,482 @@
+var BUGNUMBER = 819125;
+var summary = "try block should return try value if finally returned normally";
+
+print(BUGNUMBER + ": " + summary);
+
+function expectTryValue(code, isUndefined) {
+  assertEq(eval(code), isUndefined ? undefined : 'try');
+}
+
+function expectCatchValue(code, isUndefined) {
+  assertEq(eval(code), isUndefined ? undefined : 'catch');
+}
+
+function expectFinallyValue(code, isUndefined) {
+  assertEq(eval(code), isUndefined ? undefined : 'finally');
+}
+
+// ==== finally: normal ====
+
+// try: normal
+// finally: normal
+expectTryValue(`
+try {
+  'try';
+} finally {
+  'finally';
+}
+`);
+
+// try: normal without value
+// finally: normal
+expectTryValue(`
+try {
+} finally {
+  'finally';
+}
+`, true);
+
+// try: break
+// finally: normal
+expectTryValue(`
+while (true) {
+  try {
+    'try';
+    break;
+  } finally {
+    'finally';
+  }
+}
+`);
+
+// try: break without value
+// finally: normal
+expectTryValue(`
+while (true) {
+  try {
+    break;
+  } finally {
+    'finally';
+  }
+}
+`, true);
+
+// try: continue
+// finally: normal
+expectTryValue(`
+do {
+  try {
+    'try';
+    continue;
+  } finally {
+    'finally';
+  }
+} while (false);
+`);
+
+// try: continue without value
+// finally: normal
+expectTryValue(`
+do {
+  try {
+    continue;
+  } finally {
+    'finally';
+  }
+} while (false);
+`, true);
+
+// try: throw
+// catch: normal
+// finally: normal
+expectCatchValue(`
+try {
+  'try';
+  throw 'exception';
+} catch (e) {
+  'catch';
+} finally {
+  'finally';
+}
+`);
+
+// try: throw
+// catch: normal
+// finally: normal
+expectCatchValue(`
+try {
+  'try';
+  throw 'exception';
+} catch (e) {
+  'catch';
+} finally {
+  'finally';
+}
+`);
+
+// try: throw
+// catch: normal without value
+// finally: normal
+expectCatchValue(`
+try {
+  'try';
+  throw 'exception';
+} catch (e) {
+} finally {
+  'finally';
+}
+`, true);
+
+// try: throw
+// catch: normal without value
+// finally: normal
+expectCatchValue(`
+try {
+  'try';
+  throw 'exception';
+} catch (e) {
+} finally {
+  'finally';
+}
+`, true);
+
+// try: throw
+// catch: break
+// finally: normal
+expectCatchValue(`
+while (true) {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    'catch';
+    break;
+  } finally {
+    'finally';
+  }
+}
+`);
+
+// try: throw
+// catch: break without value
+// finally: normal
+expectCatchValue(`
+while (true) {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    break;
+  } finally {
+    'finally';
+  }
+}
+`, true);
+
+// try: throw
+// catch: continue
+// finally: normal
+expectCatchValue(`
+do {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    'catch';
+    continue;
+  } finally {
+    'finally';
+  }
+} while (false);
+`);
+
+// try: throw
+// catch: continue without value
+// finally: normal
+expectCatchValue(`
+do {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    continue;
+  } finally {
+    'finally';
+  }
+} while (false);
+`, true);
+
+// ==== finally: break ====
+
+// try: normal
+// finally: break
+expectFinallyValue(`
+while (true) {
+  try {
+    'try';
+  } finally {
+    'finally';
+    break;
+  }
+}
+`);
+
+// try: normal
+// finally: break without value
+expectFinallyValue(`
+while (true) {
+  try {
+    'try';
+  } finally {
+    break;
+  }
+}
+`, true);
+
+// try: break
+// finally: break
+expectFinallyValue(`
+while (true) {
+  try {
+    'try';
+    break;
+  } finally {
+    'finally';
+    break;
+  }
+}
+`);
+
+// try: break
+// finally: break without value
+expectFinallyValue(`
+while (true) {
+  try {
+    'try';
+    break;
+  } finally {
+    break;
+  }
+}
+`, true);
+
+// try: continue
+// finally: break
+expectFinallyValue(`
+do {
+  try {
+    'try';
+    continue;
+  } finally {
+    'finally';
+    break;
+  }
+} while (false);
+`);
+
+// try: continue
+// finally: break without value
+expectFinallyValue(`
+do {
+  try {
+    'try';
+    continue;
+  } finally {
+    break;
+  }
+} while (false);
+`, true);
+
+// try: throw
+// catch: normal
+// finally: break
+expectFinallyValue(`
+while (true) {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    'catch';
+  } finally {
+    'finally';
+    break;
+  }
+}
+`, false);
+
+// try: throw
+// catch: normal
+// finally: break without value
+expectFinallyValue(`
+while (true) {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    'catch';
+  } finally {
+    break;
+  }
+}
+`, true);
+
+// ==== finally: continue ====
+
+// try: normal
+// finally: continue
+expectFinallyValue(`
+do {
+  try {
+    'try';
+  } finally {
+    'finally';
+    continue;
+  }
+} while (false);
+`);
+
+// try: normal
+// finally: continue without value
+expectFinallyValue(`
+do {
+  try {
+    'try';
+  } finally {
+    continue;
+  }
+} while (false);
+`, true);
+
+// try: break
+// finally: continue
+expectFinallyValue(`
+do {
+  try {
+    'try';
+    break;
+  } finally {
+    'finally';
+    continue;
+  }
+} while (false);
+`);
+
+// try: break
+// finally: continue without value
+expectFinallyValue(`
+do {
+  try {
+    'try';
+    break;
+  } finally {
+    continue;
+  }
+} while (false);
+`, true);
+
+// try: continue
+// finally: continue
+expectFinallyValue(`
+do {
+  try {
+    'try';
+    continue;
+  } finally {
+    'finally';
+    continue;
+  }
+} while (false);
+`);
+
+// try: continue
+// finally: continue without value
+expectFinallyValue(`
+do {
+  try {
+    'try';
+    continue;
+  } finally {
+    continue;
+  }
+} while (false);
+`, true);
+
+// ==== without finally ====
+
+// try: throw
+// catch: normal
+expectCatchValue(`
+try {
+  'try';
+  throw 'exception';
+} catch (e) {
+  'catch';
+}
+`);
+
+// try: throw
+// catch: normal without value
+expectCatchValue(`
+try {
+  'try';
+  throw 'exception';
+} catch (e) {
+}
+`, true);
+
+// try: throw
+// catch: break
+expectCatchValue(`
+while (true) {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    'catch';
+    break;
+  }
+}
+`);
+
+// try: throw
+// catch: break without value
+expectCatchValue(`
+while (true) {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    break;
+  }
+}
+`, true);
+
+// try: throw
+// catch: continue
+expectCatchValue(`
+do {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    'catch';
+    continue;
+  }
+} while (false);
+`);
+
+// try: throw
+// catch: continue without value
+expectCatchValue(`
+do {
+  try {
+    'try';
+    throw 'exception';
+  } catch (e) {
+    continue;
+  }
+} while (false);
+`, true);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1794,16 +1794,17 @@ CASE(EnableInterruptsPseudoOpcode)
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED14)
 CASE(JSOP_BACKPATCH)
+CASE(JSOP_UNUSED145)
 CASE(JSOP_UNUSED171)
 CASE(JSOP_UNUSED172)
 CASE(JSOP_UNUSED173)
 CASE(JSOP_UNUSED174)
 CASE(JSOP_UNUSED175)
 CASE(JSOP_UNUSED176)
 CASE(JSOP_UNUSED177)
 CASE(JSOP_UNUSED178)
@@ -2134,26 +2135,16 @@ CASE(JSOP_PICK)
     unsigned i = GET_UINT8(REGS.pc);
     MOZ_ASSERT(REGS.stackDepth() >= i + 1);
     Value lval = REGS.sp[-int(i + 1)];
     memmove(REGS.sp - (i + 1), REGS.sp - i, sizeof(Value) * i);
     REGS.sp[-1] = lval;
 }
 END_CASE(JSOP_PICK)
 
-CASE(JSOP_BINDINTRINSIC)
-{
-    NativeObject* holder = GlobalObject::getIntrinsicsHolder(cx, cx->global());
-    if (!holder)
-        goto error;
-
-    PUSH_OBJECT(*holder);
-}
-END_CASE(JSOP_BINDINTRINSIC)
-
 CASE(JSOP_BINDGNAME)
 CASE(JSOP_BINDNAME)
 {
     JSOp op = JSOp(*REGS.pc);
     ReservedRooted<JSObject*> scopeChain(&rootObject0);
     if (op == JSOP_BINDNAME || script->hasNonSyntacticScope())
         scopeChain.set(REGS.fp()->scopeChain());
     else
@@ -2567,19 +2558,16 @@ CASE(JSOP_GETXPROP)
 END_CASE(JSOP_GETXPROP)
 
 CASE(JSOP_SETINTRINSIC)
 {
     HandleValue value = REGS.stackHandleAt(-1);
 
     if (!SetIntrinsicOperation(cx, script, REGS.pc, value))
         goto error;
-
-    REGS.sp[-2] = REGS.sp[-1];
-    REGS.sp--;
 }
 END_CASE(JSOP_SETINTRINSIC)
 
 CASE(JSOP_SETGNAME)
 CASE(JSOP_STRICTSETGNAME)
 CASE(JSOP_SETNAME)
 CASE(JSOP_STRICTSETNAME)
 {
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1469,34 +1469,24 @@ 1234567890123456789012345678901234567890
      * intrinsic functions the runtime doesn't give client JS code access to.
      *   Category: Variables and Scopes
      *   Type: Intrinsics
      *   Operands: uint32_t nameIndex
      *   Stack: => intrinsic[name]
      */ \
     macro(JSOP_GETINTRINSIC,  143, "getintrinsic",  NULL, 5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
     /*
-     * Pops the top two values on the stack as 'val' and 'scope', sets intrinsic
-     * as 'val', and pushes 'val' onto the stack.
-     *
-     * 'scope' is not used.
+     * Stores the top stack value in the specified intrinsic.
      *   Category: Variables and Scopes
      *   Type: Intrinsics
      *   Operands: uint32_t nameIndex
-     *   Stack: scope, val => val
+     *   Stack: val => val
      */ \
-    macro(JSOP_SETINTRINSIC,  144, "setintrinsic",  NULL, 5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \
-    /*
-     * Pushes 'intrinsicHolder' onto the stack.
-     *   Category: Variables and Scopes
-     *   Type: Intrinsics
-     *   Operands: uint32_t nameIndex
-     *   Stack: => intrinsicHolder
-     */ \
-    macro(JSOP_BINDINTRINSIC, 145, "bindintrinsic", NULL, 5,  0,  1, JOF_ATOM|JOF_NAME|JOF_SET) \
+    macro(JSOP_SETINTRINSIC,  144, "setintrinsic",  NULL, 5,  1,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    macro(JSOP_UNUSED145,     145, "unused145",     NULL, 1,  0,  0, JOF_BYTE) \
     /*
      * Initialize a non-configurable, non-writable, non-enumerable data-property on an object.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 317;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 318;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 418,
+static_assert(JSErr_Limit == 419,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/layout/base/ZoomConstraintsClient.cpp
+++ b/layout/base/ZoomConstraintsClient.cpp
@@ -41,17 +41,17 @@ ZoomConstraintsClient::ZoomConstraintsCl
 ZoomConstraintsClient::~ZoomConstraintsClient()
 {
 }
 
 static nsIWidget*
 GetWidget(nsIPresShell* aShell)
 {
   if (nsIFrame* rootFrame = aShell->GetRootFrame()) {
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
     return rootFrame->GetNearestWidget();
 #else
     if (nsView* view = rootFrame->GetView()) {
       return view->GetWidget();
     }
 #endif
   }
   return nullptr;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -4144,17 +4144,17 @@ nsDocumentViewer::ShouldAttachToTopLevel
   nsCOMPtr<nsIDocShellTreeItem> containerItem(mContainer);
   if (!containerItem)
     return false;
 
   // We always attach when using puppet widgets
   if (nsIWidget::UsePuppetWidgets())
     return true;
 
-#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
   // On windows, in the parent process we also attach, but just to
   // chrome items
   nsWindowType winType = mParentWidget->WindowType();
   if ((winType == eWindowType_toplevel ||
        winType == eWindowType_dialog ||
        winType == eWindowType_invisible) &&
       containerItem->ItemType() == nsIDocShellTreeItem::typeChrome) {
     return true;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2682,17 +2682,17 @@ nsLayoutUtils::GetLayerTransformForFrame
   // If the caller doesn't care about the value, early-return to skip
   // overhead below.
   if (!aTransform) {
     return true;
   }
 
   nsDisplayListBuilder builder(root, nsDisplayListBuilder::OTHER,
                                false/*don't build caret*/);
-  nsDisplayList list;  
+  nsDisplayList list;
   nsDisplayTransform* item =
     new (&builder) nsDisplayTransform(&builder, aFrame, &list, nsRect());
 
   *aTransform = item->GetTransform();
   item->~nsDisplayTransform();
 
   return true;
 }
@@ -3264,17 +3264,17 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
 
     aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
   }
 
   nsIAtom* frameType = aFrame->GetType();
 
   // For the viewport frame in print preview/page layout we want to paint
   // the grey background behind the page, not the canvas color.
-  if (frameType == nsGkAtoms::viewportFrame && 
+  if (frameType == nsGkAtoms::viewportFrame &&
       nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
     nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame),
                            aFrame->GetSize());
     nsDisplayListBuilder::AutoBuildingDisplayList
       buildingDisplayList(&builder, aFrame, bounds, false);
     presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds);
   } else if (frameType != nsGkAtoms::pageFrame) {
     // For printing, this function is first called on an nsPageFrame, which
@@ -4828,17 +4828,17 @@ nsLayoutUtils::ComputeISizeValue(
                    "have unconstrained inline-size; this should only result from "
                    "very large sizes, not attempts at intrinsic inline-size "
                    "calculation");
   NS_PRECONDITION(aContainingBlockISize >= 0,
                   "inline-size less than zero");
 
   nscoord result;
   if (aCoord.IsCoordPercentCalcUnit()) {
-    result = nsRuleNode::ComputeCoordPercentCalc(aCoord, 
+    result = nsRuleNode::ComputeCoordPercentCalc(aCoord,
                                                  aContainingBlockISize);
     // The result of a calc() expression might be less than 0; we
     // should clamp at runtime (below).  (Percentages and coords that
     // are less than 0 have already been dropped by the parser.)
     result -= aContentEdgeToBoxSizing;
   } else {
     MOZ_ASSERT(eStyleUnit_Enumerated == aCoord.GetUnit());
     // If aFrame is a container for font size inflation, then shrink
@@ -5846,17 +5846,17 @@ nsLayoutUtils::CalculateContentBEnd(Writ
     if (blockFrame) {
       contentBEnd =
         std::max(contentBEnd, CalculateBlockContentBEnd(aWM, blockFrame));
       skip |= nsIFrame::kPrincipalList;
     }
     nsIFrame::ChildListIterator lists(aFrame);
     for (; !lists.IsDone(); lists.Next()) {
       if (!skip.Contains(lists.CurrentID())) {
-        nsFrameList::Enumerator childFrames(lists.CurrentList()); 
+        nsFrameList::Enumerator childFrames(lists.CurrentList());
         for (; !childFrames.AtEnd(); childFrames.Next()) {
           nsIFrame* child = childFrames.get();
           nscoord offset =
             child->GetLogicalNormalPosition(aWM,
                                             aFrame->GetSize()).B(aWM);
           contentBEnd = std::max(contentBEnd,
                                  CalculateContentBEnd(aWM, child) + offset);
         }
@@ -7661,17 +7661,17 @@ nsLayoutUtils::FontSizeInflationEnabled(
   if (!presShell) {
     return false;
   }
 
   return presShell->FontSizeInflationEnabled();
 }
 
 /* static */ nsRect
-nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame, 
+nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame,
                                         const nsSize& aFrameSize)
 {
   nsCSSShadowArray* boxShadows = aFrame->StyleBorder()->mBoxShadow;
   if (!boxShadows) {
     return nsRect();
   }
 
   bool nativeTheme;
@@ -7683,17 +7683,17 @@ nsLayoutUtils::GetBoxShadowRectForFrame(
     nativeTheme = transparency != nsITheme::eOpaque;
   } else {
     nativeTheme = false;
   }
 
   nsRect frameRect = nativeTheme ?
     aFrame->GetVisualOverflowRectRelativeToSelf() :
     nsRect(nsPoint(0, 0), aFrameSize);
-  
+
   nsRect shadows;
   int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
   for (uint32_t i = 0; i < boxShadows->Length(); ++i) {
     nsRect tmpRect = frameRect;
     nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
 
     // inset shadows are never painted outside the frame
     if (shadow->mInset)
@@ -7807,17 +7807,17 @@ UpdateCompositionBoundsForRCDRSF(ParentL
                                  bool aScaleContentViewerSize,
                                  const LayoutDeviceToLayerScale2D& aCumulativeResolution)
 {
   nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
   if (!rootFrame) {
     return false;
   }
 
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
   nsIWidget* widget = rootFrame->GetNearestWidget();
 #else
   nsView* view = rootFrame->GetView();
   nsIWidget* widget = view ? view->GetWidget() : nullptr;
 #endif
 
   if (widget) {
     nsIntRect widgetBounds;
@@ -8140,17 +8140,17 @@ void StrokeLineWithSnapping(const nsPoin
   Point p2 = NSPointToPoint(aP2, aAppUnitsPerDevPixel);
   SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
                                     aStrokeOptions.mLineWidth);
   aDrawTarget.StrokeLine(p1, p2, aPattern, aStrokeOptions, aDrawOptions);
 }
 
 namespace layout {
 
-  
+
 void
 MaybeSetupTransactionIdAllocator(layers::LayerManager* aManager, nsView* aView)
 {
   if (aManager->GetBackendType() == layers::LayersBackend::LAYERS_CLIENT) {
     layers::ClientLayerManager *manager = static_cast<layers::ClientLayerManager*>(aManager);
     nsRefreshDriver *refresh = aView->GetViewManager()->GetPresShell()->GetPresContext()->RefreshDriver();
     manager->SetTransactionIdAllocator(refresh);
   }
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -226,17 +226,17 @@ static void Shutdown();
 #include "nsIMobileMessageService.h"
 #include "nsIMobileMessageDatabaseService.h"
 #include "nsIPowerManagerService.h"
 #include "nsIAlarmHalService.h"
 #include "nsIMediaManager.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
 
 #include "AudioChannelService.h"
-#include "mozilla/net/WebSocketFrameService.h"
+#include "mozilla/net/WebSocketEventService.h"
 
 #include "mozilla/dom/DataStoreService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/alarm/AlarmHalService.h"
 #include "mozilla/dom/time/TimeService.h"
 #include "StreamingProtocolService.h"
 
@@ -628,27 +628,27 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMScri
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Geolocation, Init)
 
 #define NS_GEOLOCATION_SERVICE_CID \
   { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } }
 
 #define NS_AUDIOCHANNEL_SERVICE_CID \
   { 0xf712e983, 0x048a, 0x443f, { 0x88, 0x02, 0xfc, 0xc3, 0xd9, 0x27, 0xce, 0xac }}
 
-#define NS_WEBSOCKETFRAME_SERVICE_CID \
-  { 0x5973dd8f, 0xed2c, 0x41ff, { 0x9e, 0x64, 0x25, 0x1f, 0xf5, 0x5a, 0x67, 0xb9 }}
+#define NS_WEBSOCKETEVENT_SERVICE_CID \
+  { 0x31689828, 0xda66, 0x49a6, { 0x87, 0x0c, 0xdf, 0x62, 0xb8, 0x3f, 0xe7, 0x89 }}
 
 #define NS_DATASTORE_SERVICE_CID \
   { 0x0d4285fe, 0xf1b3, 0x49fa, { 0xbc, 0x51, 0xa4, 0xa8, 0x3f, 0x0a, 0xaf, 0x85 }}
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreate)
 
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WebSocketFrameService, WebSocketFrameService::GetOrCreate)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WebSocketEventService, WebSocketEventService::GetOrCreate)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DataStoreService, DataStoreService::GetOrCreate)
 
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
 NS_GENERIC_FACTORY_CONSTRUCTOR(FakeSpeechRecognitionService)
 #endif
 #ifdef MOZ_WEBSPEECH_POCKETSPHINX
 NS_GENERIC_FACTORY_CONSTRUCTOR(PocketSphinxSpeechRecognitionService)
@@ -790,17 +790,17 @@ NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTSERVICESDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_CID);
 NS_DEFINE_NAMED_CID(NS_AUDIOCHANNEL_SERVICE_CID);
-NS_DEFINE_NAMED_CID(NS_WEBSOCKETFRAME_SERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_WEBSOCKETEVENT_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_DATASTORE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_FOCUSMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_CONTENTSECURITYMANAGER_CID);
 NS_DEFINE_NAMED_CID(CSPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_CSPCONTEXT_CID);
 NS_DEFINE_NAMED_CID(NS_MIXEDCONTENTBLOCKER_CID);
 NS_DEFINE_NAMED_CID(NS_EVENTLISTENERSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GLOBALMESSAGEMANAGER_CID);
@@ -1098,17 +1098,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_EDITORCONTROLLER_CID, false, nullptr, nsEditorControllerConstructor },
   { &kNS_EDITINGCONTROLLER_CID, false, nullptr, nsEditingControllerConstructor },
   { &kNS_EDITORCOMMANDTABLE_CID, false, nullptr, nsEditorCommandTableConstructor },
   { &kNS_EDITINGCOMMANDTABLE_CID, false, nullptr, nsEditingCommandTableConstructor },
   { &kNS_TEXTSERVICESDOCUMENT_CID, false, nullptr, nsTextServicesDocumentConstructor },
   { &kNS_GEOLOCATION_SERVICE_CID, false, nullptr, nsGeolocationServiceConstructor },
   { &kNS_GEOLOCATION_CID, false, nullptr, GeolocationConstructor },
   { &kNS_AUDIOCHANNEL_SERVICE_CID, false, nullptr, AudioChannelServiceConstructor },
-  { &kNS_WEBSOCKETFRAME_SERVICE_CID, false, nullptr, WebSocketFrameServiceConstructor },
+  { &kNS_WEBSOCKETEVENT_SERVICE_CID, false, nullptr, WebSocketEventServiceConstructor },
   { &kNS_DATASTORE_SERVICE_CID, false, nullptr, DataStoreServiceConstructor },
   { &kNS_FOCUSMANAGER_CID, false, nullptr, CreateFocusManager },
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
   { &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID, false, nullptr, FakeSpeechRecognitionServiceConstructor },
 #endif
 #ifdef MOZ_WEBSPEECH_POCKETSPHINX
   { &kNS_POCKETSPHINX_SPEECH_RECOGNITION_SERVICE_CID, false, nullptr, PocketSphinxSpeechRecognitionServiceConstructor },
 #endif
@@ -1266,17 +1266,17 @@ static const mozilla::Module::ContractID
   { NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
   { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
   { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },
   { "@mozilla.org/textservices/textservicesdocument;1", &kNS_TEXTSERVICESDOCUMENT_CID },
   { "@mozilla.org/geolocation/service;1", &kNS_GEOLOCATION_SERVICE_CID },
   { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID },
   { "@mozilla.org/audiochannel/service;1", &kNS_AUDIOCHANNEL_SERVICE_CID },
-  { "@mozilla.org/websocketframe/service;1", &kNS_WEBSOCKETFRAME_SERVICE_CID },
+  { "@mozilla.org/websocketevent/service;1", &kNS_WEBSOCKETEVENT_SERVICE_CID },
   { "@mozilla.org/datastore-service;1", &kNS_DATASTORE_SERVICE_CID },
   { "@mozilla.org/focus-manager;1", &kNS_FOCUSMANAGER_CID },
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
   { NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake", &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID },
 #endif
 #ifdef MOZ_WEBSPEECH_POCKETSPHINX
   { NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "pocketsphinx-en-US", &kNS_POCKETSPHINX_SPEECH_RECOGNITION_SERVICE_CID },
 #endif
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -1536,245 +1536,39 @@ nsPluginFrame::BuildLayer(nsDisplayListB
   return layer.forget();
 }
 
 void
 nsPluginFrame::PaintPlugin(nsDisplayListBuilder* aBuilder,
                            nsRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect, const nsRect& aPluginRect)
 {
-#if defined(XP_MACOSX)
-  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
-#endif
-
 #if defined(MOZ_WIDGET_ANDROID)
   if (mInstanceOwner) {
     gfxRect frameGfxRect =
       PresContext()->AppUnitsToGfxUnits(aPluginRect);
     gfxRect dirtyGfxRect =
       PresContext()->AppUnitsToGfxUnits(aDirtyRect);
 
     gfxContext* ctx = aRenderingContext.ThebesContext();
 
     mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect);
     return;
   }
-#endif
-
-  // Screen painting code
-#if defined(XP_MACOSX)
-  // delegate all painting to the plugin instance.
+#else
+# if defined(DEBUG)
+  // On Desktop, we should have built a layer as we no longer support in-process
+  // plugins or synchronous painting. We can only get here for windowed plugins
+  // (which draw themselves), or via some error/unload state.
   if (mInstanceOwner) {
-    if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreGraphics ||
-        mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation ||
-        mInstanceOwner->GetDrawingModel() == 
-                                  NPDrawingModelInvalidatingCoreAnimation) {
-      int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
-      // Clip to the content area where the plugin should be drawn. If
-      // we don't do this, the plugin can draw outside its bounds.
-      nsIntRect contentPixels = aPluginRect.ToNearestPixels(appUnitsPerDevPixel);
-      nsIntRect dirtyPixels = aDirtyRect.ToOutsidePixels(appUnitsPerDevPixel);
-      nsIntRect clipPixels;
-      clipPixels.IntersectRect(contentPixels, dirtyPixels);
-
-      // Don't invoke the drawing code if the clip is empty.
-      if (clipPixels.IsEmpty())
-        return;
-
-      gfxRect nativeClipRect(clipPixels.x, clipPixels.y,
-                             clipPixels.width, clipPixels.height);
-      gfxContext* ctx = aRenderingContext.ThebesContext();
-
-      gfxContextAutoSaveRestore save(ctx);
-      ctx->NewPath();
-      ctx->Rectangle(nativeClipRect);
-      ctx->Clip();
-      gfxPoint offset(contentPixels.x, contentPixels.y);
-      ctx->SetMatrix(
-        ctx->CurrentMatrix().Translate(offset));
-
-      gfxQuartzNativeDrawing nativeDrawing(aDrawTarget,
-                                           ToRect(nativeClipRect - offset));
-
-      CGContextRef cgContext = nativeDrawing.BeginNativeDrawing();
-      if (!cgContext) {
-        NS_WARNING("null CGContextRef during PaintPlugin");
-        return;
-      }
-
-      RefPtr<nsNPAPIPluginInstance> inst;
-      GetPluginInstance(getter_AddRefs(inst));
-      if (!inst) {
-        NS_WARNING("null plugin instance during PaintPlugin");
-        nativeDrawing.EndNativeDrawing();
-        return;
-      }
-      NPWindow* window;
-      mInstanceOwner->GetWindow(window);
-      if (!window) {
-        NS_WARNING("null plugin window during PaintPlugin");
-        nativeDrawing.EndNativeDrawing();
-        return;
-      }
-      NP_CGContext* cgPluginPortCopy =
-                static_cast<NP_CGContext*>(mInstanceOwner->GetPluginPortCopy());
-      if (!cgPluginPortCopy) {
-        NS_WARNING("null plugin port copy during PaintPlugin");
-        nativeDrawing.EndNativeDrawing();
-        return;
-      }
-
-      mInstanceOwner->BeginCGPaint();
-      if (mInstanceOwner->GetDrawingModel() == NPDrawingModelCoreAnimation ||
-          mInstanceOwner->GetDrawingModel() == 
-                                   NPDrawingModelInvalidatingCoreAnimation) {
-        // CoreAnimation is updated, render the layer and perform a readback.
-        mInstanceOwner->RenderCoreAnimation(cgContext, window->width, window->height);
-      } else {
-        mInstanceOwner->Paint(nativeClipRect - offset, cgContext);
-      }
-      mInstanceOwner->EndCGPaint();
-
-      nativeDrawing.EndNativeDrawing();
-    } else {
-      gfxContext* ctx = aRenderingContext.ThebesContext();
-
-      // Translate the context:
-      gfxPoint devPixelPt =
-        nsLayoutUtils::PointToGfxPoint(aPluginRect.TopLeft(),
-                                       PresContext()->AppUnitsPerDevPixel());
-
-      gfxContextMatrixAutoSaveRestore autoSR(ctx);
-      ctx->SetMatrix(ctx->CurrentMatrix().Translate(devPixelPt));
-
-      // FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
-
-      // this rect is used only in the CoreGraphics drawing model
-      gfxRect tmpRect(0, 0, 0, 0);
-      mInstanceOwner->Paint(tmpRect, nullptr);
-    }
-  }
-#elif defined(MOZ_X11)
-  if (mInstanceOwner) {
-    NPWindow *window;
+    NPWindow *window = nullptr;
     mInstanceOwner->GetWindow(window);
-    if (window->type == NPWindowTypeDrawable) {
-      gfxRect frameGfxRect =
-        PresContext()->AppUnitsToGfxUnits(aPluginRect);
-      gfxRect dirtyGfxRect =
-        PresContext()->AppUnitsToGfxUnits(aDirtyRect);
-      gfxContext* ctx = aRenderingContext.ThebesContext();
-
-      mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect);
-    }
+    MOZ_ASSERT(!window || window->type == NPWindowTypeWindow);
   }
-#elif defined(XP_WIN)
-  RefPtr<nsNPAPIPluginInstance> inst;
-  GetPluginInstance(getter_AddRefs(inst));
-  if (inst) {
-    gfxRect frameGfxRect =
-      PresContext()->AppUnitsToGfxUnits(aPluginRect);
-    gfxRect dirtyGfxRect =
-      PresContext()->AppUnitsToGfxUnits(aDirtyRect);
-    gfxContext *ctx = aRenderingContext.ThebesContext();
-    gfxMatrix currentMatrix = ctx->CurrentMatrix();
-
-    if (ctx->UserToDevicePixelSnapped(frameGfxRect, false)) {
-      dirtyGfxRect = ctx->UserToDevice(dirtyGfxRect);
-      ctx->SetMatrix(gfxMatrix());
-    }
-    dirtyGfxRect.RoundOut();
-
-    // Look if it's windowless
-    NPWindow *window;
-    mInstanceOwner->GetWindow(window);
-
-    if (window->type == NPWindowTypeDrawable) {
-      // the offset of the DC
-      nsPoint origin;
-
-      gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect);
-      if (nativeDraw.IsDoublePass()) {
-        // OOP plugin specific: let the shim know before we paint if we are doing a
-        // double pass render. If this plugin isn't oop, the register window message
-        // will be ignored.
-        NPEvent pluginEvent;
-        pluginEvent.event = plugins::DoublePassRenderingEvent();
-        pluginEvent.wParam = 0;
-        pluginEvent.lParam = 0;
-        if (pluginEvent.event)
-          inst->HandleEvent(&pluginEvent, nullptr);
-      }
-      do {
-        HDC hdc = nativeDraw.BeginNativeDrawing();
-        if (!hdc)
-          return;
-
-        RECT dest;
-        nativeDraw.TransformToNativeRect(frameGfxRect, dest);
-        RECT dirty;
-        nativeDraw.TransformToNativeRect(dirtyGfxRect, dirty);
-
-        window->window = hdc;
-        window->x = dest.left;
-        window->y = dest.top;
-        window->clipRect.left = 0;
-        window->clipRect.top = 0;
-        // if we're painting, we're visible.
-        window->clipRect.right = window->width;
-        window->clipRect.bottom = window->height;
-
-        // Windowless plugins on windows need a special event to update their location,
-        // see bug 135737.
-        //
-        // bug 271442: note, the rectangle we send is now purely the bounds of the plugin
-        // relative to the window it is contained in, which is useful for the plugin to
-        // correctly translate mouse coordinates.
-        //
-        // this does not mesh with the comments for bug 135737 which imply that the rectangle
-        // must be clipped in some way to prevent the plugin attempting to paint over areas
-        // it shouldn't.
-        //
-        // since the two uses of the rectangle are mutually exclusive in some cases, and
-        // since I don't see any incorrect painting (at least with Flash and ViewPoint -
-        // the originator of bug 135737), it seems that windowless plugins are not relying
-        // on information here for clipping their drawing, and we can safely use this message
-        // to tell the plugin exactly where it is in all cases.
-
-        nsIntPoint origin = GetWindowOriginInPixels(true);
-        nsIntRect winlessRect = nsIntRect(origin, nsIntSize(window->width, window->height));
-
-        if (!mWindowlessRect.IsEqualEdges(winlessRect)) {
-          mWindowlessRect = winlessRect;
-
-          WINDOWPOS winpos;
-          memset(&winpos, 0, sizeof(winpos));
-          winpos.x = mWindowlessRect.x;
-          winpos.y = mWindowlessRect.y;
-          winpos.cx = mWindowlessRect.width;
-          winpos.cy = mWindowlessRect.height;
-
-          // finally, update the plugin by sending it a WM_WINDOWPOSCHANGED event
-          NPEvent pluginEvent;
-          pluginEvent.event = WM_WINDOWPOSCHANGED;
-          pluginEvent.wParam = 0;
-          pluginEvent.lParam = (LPARAM)&winpos;
-          inst->HandleEvent(&pluginEvent, nullptr);
-        }
-
-        inst->SetWindow(window);
-
-        mInstanceOwner->Paint(dirty, hdc);
-        nativeDraw.EndNativeDrawing();
-      } while (nativeDraw.ShouldRenderAgain());
-      nativeDraw.PaintToContext();
-    }
-
-    ctx->SetMatrix(currentMatrix);
-  }
+# endif
 #endif
 }
 
 nsresult
 nsPluginFrame::HandleEvent(nsPresContext* aPresContext,
                            WidgetGUIEvent* anEvent,
                            nsEventStatus* anEventStatus)
 {
--- a/layout/reftests/css-blending/background-blending-moz-element-ref.html
+++ b/layout/reftests/css-blending/background-blending-moz-element-ref.html
@@ -4,17 +4,17 @@
 -->
 <!DOCTYPE html>
 <html>
 <head>
 <style>
 
 .c {
     height: 10px;
-    background: rgb(0,255,0,.5);
+    background: rgba(0,255,0,.5);
 }
 
 </style>
 </head>
 <body>
 <div class="c"></div>
 <div class="c"></div>
 </body>
--- a/layout/reftests/css-blending/background-blending-moz-element.html
+++ b/layout/reftests/css-blending/background-blending-moz-element.html
@@ -7,17 +7,17 @@
 <head>
 <style>
 
 div {
 	    height: 10px;
 }
 
 #b {
-	background: rgb(0,255,0,.5);
+	background: rgba(0,255,0,.5);
 }
 #c {
     background: -moz-element(#b);
     background-blend-mode: difference;
 }
 
 </style>
 </head>
--- a/layout/reftests/xul/reftest.list
+++ b/layout/reftests/xul/reftest.list
@@ -1,16 +1,16 @@
 skip-if((B2G&&browserIsRemote)||Mulet) == menuitem-key.xul menuitem-key-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 # these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
 random-if(Android||B2G) skip-if((B2G&&browserIsRemote)||Mulet) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 random-if(Android||B2G) fails-if(winWidget) skip-if((B2G&&browserIsRemote)||Mulet) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) == textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658 # Initial mulet triage: parity with B2G/B2G Desktop
 # accesskeys are not normally displayed on Mac, so skip this test
 skip-if(cocoaWidget) skip-if((B2G&&browserIsRemote)||Mulet) == accesskey.xul accesskey-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
-fails-if(cocoaWidget) skip-if((B2G&&browserIsRemote)||Mulet) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,11) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
+fails-if(cocoaWidget) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,11) skip-if((B2G&&browserIsRemote)||Mulet) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) != tree-row-outline-1.xul tree-row-outline-1-notref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) == text-small-caps-1.xul text-small-caps-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) == inactive-fixed-bg-bug1205630.xul inactive-fixed-bg-bug1205630-ref.html
 
 # Tests for XUL <image> with 'object-fit' & 'object-position':
 # These tests should be very similar to tests in our w3c-css/submitted/images3
 # reftest directory. They live here because they use XUL, and it
 # wouldn't be fair of us to make a W3C testsuite implicitly depend on XUL.
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -297,17 +297,17 @@ public:
   /*
    * Mark this style context's rule node (and its ancestors) to prevent
    * it from being garbage collected.
    */
   void Mark();
 
   /*
    * Get the style data for a style struct.  This is the most important
-   * member function of nsIStyleContext.  It fills in a const pointer
+   * member function of nsStyleContext.  It fills in a const pointer
    * to a style data struct that is appropriate for the style context's
    * frame.  This struct may be shared with other contexts (either in
    * the rule tree or the style context tree), so it should not be
    * modified.
    *
    * This function will NOT return null (even when out of memory) when
    * given a valid style struct ID, so the result does not need to be
    * null-checked.
@@ -328,17 +328,17 @@ public:
   #define STYLE_STRUCT(name_, checkdata_cb_)              \
     const nsStyle##name_ * Style##name_() {               \
       return DoGetStyle##name_<true>();                   \
     }
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
   /**
-   * PeekStyle* is like GetStyle* but doesn't trigger style
+   * PeekStyle* is like Style* but doesn't trigger style
    * computation if the data is not cached on either the style context
    * or the rule node.
    *
    * Perhaps this shouldn't be a public nsStyleContext API.
    */
   #define STYLE_STRUCT(name_, checkdata_cb_)              \
     const nsStyle##name_ * PeekStyle##name_() {           \
       return DoGetStyle##name_<false>();                  \
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/byteorder-mod.patch
@@ -0,0 +1,37 @@
+diff --git a/media/libstagefright/binding/byteorder/mod.rs b/media/libstagefright/binding/byteorder/mod.rs
+index 59ba692..9d2d1d5 100644
+--- a/media/libstagefright/binding/byteorder/mod.rs
++++ b/media/libstagefright/binding/byteorder/mod.rs
+@@ -36,16 +36,16 @@ assert_eq!(wtr, vec![5, 2, 0, 3]);
+ ```
+ */
+ 
+-#![crate_name = "byteorder"]
+ #![doc(html_root_url = "http://burntsushi.net/rustdoc/byteorder")]
+ 
+ #![deny(missing_docs)]
+ 
+ use std::mem::transmute;
+ 
+-pub use new::{ReadBytesExt, WriteBytesExt, Error, Result};
++pub use byteorder::new::{ReadBytesExt, WriteBytesExt, Error, Result};
+ 
+-mod new;
++// Re-export new so gecko can build us as a mod intead of a crate.
++pub mod new;
+ 
+ #[inline]
+ fn extend_sign(val: u64, nbytes: usize) -> i64 {
+diff --git a/media/libstagefright/binding/byteorder/new.rs b/media/libstagefright/binding/byteorder/new.rs
+index bbef0cd..a2e5393 100644
+--- a/media/libstagefright/binding/byteorder/new.rs
++++ b/media/libstagefright/binding/byteorder/new.rs
+@@ -3,7 +3,7 @@ use std::fmt;
+ use std::io;
+ use std::result;
+ 
+-use ByteOrder;
++use byteorder::ByteOrder;
+ 
+ /// A short-hand for `result::Result<T, byteorder::Error>`.
+ pub type Result<T> = result::Result<T, Error>;
--- a/media/libstagefright/binding/byteorder/mod.rs
+++ b/media/libstagefright/binding/byteorder/mod.rs
@@ -31,28 +31,28 @@ use byteorder::{LittleEndian, WriteBytes
 
 let mut wtr = vec![];
 wtr.write_u16::<LittleEndian>(517).unwrap();
 wtr.write_u16::<LittleEndian>(768).unwrap();
 assert_eq!(wtr, vec![5, 2, 0, 3]);
 ```
 */
 
-#![crate_name = "byteorder"]
 #![doc(html_root_url = "http://burntsushi.net/rustdoc/byteorder")]
 
 #![deny(missing_docs)]
 
 use std::mem::transmute;
 
 pub use byteorder::new::{ReadBytesExt, WriteBytesExt, Error, Result};
 
-// Re-export new so gecko and build us as a mod intead of a crate.
+// Re-export new so gecko can build us as a mod intead of a crate.
 pub mod new;
 
+#[inline]
 fn extend_sign(val: u64, nbytes: usize) -> i64 {
     let shift  = (8 - nbytes) * 8;
     (val << shift) as i64 >> shift
 }
 
 /// ByteOrder describes types that can serialize integers as bytes.
 ///
 /// Note that `Self` does not appear anywhere in this trait's definition!
@@ -118,87 +118,98 @@ pub trait ByteOrder {
     /// Writes an unsigned 64 bit integer `n` to `buf`.
     ///
     /// Panics when `buf.len() < 8`.
     fn write_u64(buf: &mut [u8], n: u64);
 
     /// Reads a signed 16 bit integer from `buf`.
     ///
     /// Panics when `buf.len() < 2`.
+    #[inline]
     fn read_i16(buf: &[u8]) -> i16 {
         Self::read_u16(buf) as i16
     }
 
     /// Reads a signed 32 bit integer from `buf`.
     ///
     /// Panics when `buf.len() < 4`.