Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Jun 2014 17:18:45 -0400
changeset 187285 62d33e3ba5148906105567837faf78491802ed1d
parent 187284 ba028f407ebfd62dd7ab9bf3025c0d8031ac84c2 (current diff)
parent 187145 078b39cfcf44bb6513d3ffbd2f38ecd00c4bce94 (diff)
child 187286 9aa487e0bce42de277a91d3a9f19e9933058efe4
child 187351 788a10fde35c672b88862f2d371d2771d44d5ec6
child 187355 fa54ecd90c6be37f03aa1200f45f4921c2d7aaef
child 187368 cfd971d26d87bd5e5f8209bc55514fb4d31fb9b5
push id44549
push userryanvm@gmail.com
push dateFri, 06 Jun 2014 21:34:21 +0000
treeherdermozilla-inbound@9aa487e0bce4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone32.0a1
first release with
nightly linux32
62d33e3ba514 / 32.0a1 / 20140607030208 / files
nightly linux64
62d33e3ba514 / 32.0a1 / 20140607030208 / files
nightly mac
62d33e3ba514 / 32.0a1 / 20140607030208 / files
nightly win32
62d33e3ba514 / 32.0a1 / 20140607030208 / files
nightly win64
62d33e3ba514 / 32.0a1 / 20140607030208 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 978238 needs clobber because of changes to js.msg (see bug 1019955).
+Bug 904723 (Array.from) needs a clobber once again because of changes to js.msg
+and self-hosted code (see bug 1019955).
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -421,17 +421,17 @@ nsAccessibilityService::TreeViewChanged(
                                         nsIContent* aContent,
                                         nsITreeView* aView)
 {
   DocAccessible* document = GetDocAccessible(aPresShell);
   if (document) {
     Accessible* accessible = document->GetAccessible(aContent);
     if (accessible) {
       XULTreeAccessible* treeAcc = accessible->AsXULTree();
-      if (treeAcc) 
+      if (treeAcc)
         treeAcc->TreeViewChanged(aView);
     }
   }
 }
 
 void
 nsAccessibilityService::RangeValueChanged(nsIPresShell* aPresShell,
                                           nsIContent* aContent)
@@ -1170,17 +1170,17 @@ nsAccessibilityService::CreateAccessible
 
   if (role.IsEmpty() || role.EqualsLiteral("none"))
     return nullptr;
 
   if (role.EqualsLiteral("outerdoc")) {
     nsRefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc);
     return accessible.forget();
   }
- 
+
   nsRefPtr<Accessible> accessible;
 #ifdef MOZ_XUL
   // XUL controls
   if (role.EqualsLiteral("xul:alert")) {
     accessible = new XULAlertAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:button")) {
     accessible = new XULButtonAccessible(aContent, aDoc);
@@ -1553,28 +1553,32 @@ nsAccessibilityService::CreateAccessible
       // when reading by AT.
       if (aContext->IsHTMLTableRow() || aContext->IsHTMLTable())
         newAcc = new HTMLTableCellAccessibleWrap(aContent, document);
       else
         newAcc = new HyperTextAccessibleWrap(aContent, document);
       break;
 
     case eHTMLTableRowType: {
-      // Accessible HTML table row must be a child of tbody/tfoot/thead of
-      // accessible HTML table or must be a child of accessible of HTML table.
-      if (aContext->IsTable()) {
+      // Accessible HTML table row may be a child of tbody/tfoot/thead of
+      // accessible HTML table or a direct child of accessible of HTML table.
+      Accessible* table = aContext->IsTable() ?
+        aContext :
+        (aContext->Parent()->IsTable() ? aContext->Parent() : nullptr);
+
+      if (table) {
         nsIContent* parentContent = aContent->GetParent();
         nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
-        if (parentFrame->GetType() == nsGkAtoms::tableRowGroupFrame) {
+        if (parentFrame->GetType() != nsGkAtoms::tableOuterFrame) {
           parentContent = parentContent->GetParent();
           parentFrame = parentContent->GetPrimaryFrame();
         }
 
         if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame &&
-            aContext->GetContent() == parentContent) {
+            table->GetContent() == parentContent) {
           newAcc = new HTMLTableRowAccessible(aContent, document);
         }
       }
       break;
     }
     case eHTMLTextFieldType:
       newAcc = new HTMLTextFieldAccessible(aContent, document);
       break;
@@ -1644,17 +1648,17 @@ nsAccessibilityService::RemoveNativeRoot
 /**
  * Return accessibility service; creating one if necessary.
  */
 nsresult
 NS_GetAccessibilityService(nsIAccessibilityService** aResult)
 {
   NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
   *aResult = nullptr;
- 
+
   if (nsAccessibilityService::gAccessibilityService) {
     NS_ADDREF(*aResult = nsAccessibilityService::gAccessibilityService);
     return NS_OK;
   }
 
   nsRefPtr<nsAccessibilityService> service = new nsAccessibilityService();
   NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY);
 
--- a/accessible/tests/mochitest/table/test_indexes_table.html
+++ b/accessible/tests/mochitest/table/test_indexes_table.html
@@ -340,17 +340,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       <tr>
          <td colspan="2">4</td>
         <td rowspan="2">5</td>
         </tr>
       <tr>
         <td>6</td>
       </tr>
       <tr>
-        <td colspan="3">7</td>       
+        <td colspan="3">7</td>
       </tr>
 
     </tbody>
   </table>
 
   <table border="1" id="tableinsane5">
     <caption>just a crazy table</caption>
     <thead>
--- a/accessible/tests/mochitest/tree/test_table.html
+++ b/accessible/tests/mochitest/tree/test_table.html
@@ -133,26 +133,40 @@
           }
         ]
       };
 
       testAccessibleTree("table3", accTree);
 
       /////////////////////////////////////////////////////////////////////////
       // table4 (display: table-row)
-      accTree = 
+      accTree =
         { TABLE: [
           { ROW: [
             { CELL: [
               { TEXT_LEAF: [ ] }
             ] }
           ] } ]
         };
       testAccessibleTree("table4", accTree);
 
+      /////////////////////////////////////////////////////////////////////////
+      // table5 (intermediate accessible for tbody)
+      accTree =
+        { TABLE: [
+          { TEXT_CONTAINER: [
+            { ROW: [
+              { CELL: [
+                { TEXT_LEAF: [ ] }
+              ] }
+            ] }
+          ] } ]
+        };
+      testAccessibleTree("table5", accTree);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -237,10 +251,18 @@
     </tr>
   </table>
 
   <table id="table4">
     <div style="display: table-row">
       <td>cell1</td>
     </div>
   </table>
+
+  <table id="table5">
+    <tbody style="display:block;overflow:auto;">
+      <tr>
+        <td>bla</td>
+      </tr>
+    </tbody>
+  </table>
 </body>
 </html>
--- a/browser/locales/en-US/chrome/browser/translation.dtd
+++ b/browser/locales/en-US/chrome/browser/translation.dtd
@@ -39,16 +39,18 @@
 <!ENTITY translation.translatedToSuffix.label   "">
 
 <!ENTITY translation.showOriginal.button        "Show Original">
 <!ENTITY translation.showTranslation.button     "Show Translation">
 
 <!ENTITY translation.errorTranslating.label     "There has been an error translating this page.">
 <!ENTITY translation.tryAgain.button            "Try Again">
 
+<!ENTITY translation.serviceUnavailable.label   "Translation is not available at the moment. Please try again later.">
+
 <!ENTITY translation.options.menu               "Options">
 <!-- LOCALIZATION NOTE (translation.options.neverForSite.accesskey,
   -                     translation.options.preferences.accesskey):
   -  The accesskey values used here should not clash with the value used for
   -  translation.options.neverForLanguage.accesskey in translation.properties
   -->
 <!ENTITY translation.options.neverForSite.label "Never translate this site">
 <!ENTITY translation.options.neverForSite.accesskey "e">
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -4041,17 +4041,17 @@ CanvasRenderingContext2D::PutImageData(I
 
 nsresult
 CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                                 dom::Uint8ClampedArray* aArray,
                                                 bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
                                                 int32_t dirtyWidth, int32_t dirtyHeight)
 {
   if (w == 0 || h == 0) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   IntRect dirtyRect;
   IntRect imageDataRect(0, 0, w, h);
 
   if (hasDirtyRect) {
     // fix up negative dimensions
     if (dirtyWidth < 0) {
@@ -4095,17 +4095,17 @@ CanvasRenderingContext2D::PutImageData_e
   }
 
   aArray->ComputeLengthAndData();
 
   uint32_t dataLen = aArray->Length();
 
   uint32_t len = w * h * 4;
   if (dataLen != len) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
                                                           gfxImageFormat::ARGB32,
                                                           false);
   if (!imgsurf || imgsurf->CairoStatus()) {
     return NS_ERROR_FAILURE;
   }
--- a/content/media/MediaData.cpp
+++ b/content/media/MediaData.cpp
@@ -225,17 +225,18 @@ VideoData* VideoData::Create(VideoInfo& 
   if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
       aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
     NS_ERROR("C planes with different sizes");
     return nullptr;
   }
 
   // The following situations could be triggered by invalid input
   if (aPicture.width <= 0 || aPicture.height <= 0) {
-    NS_WARNING("Empty picture rect");
+    // In debug mode, makes the error more noticeable
+    MOZ_ASSERT(false, "Empty picture rect");
     return nullptr;
   }
   if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
       !ValidatePlane(aBuffer.mPlanes[2])) {
     NS_WARNING("Invalid plane size");
     return nullptr;
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13048,8 +13048,34 @@ nsDocShell::IsInvisible()
     return mInvisible;
 }
 
 void
 nsDocShell::SetInvisible(bool aInvisible)
 {
     mInvisible = aInvisible;
 }
+
+void
+nsDocShell::SetOpener(nsITabParent* aOpener)
+{
+  mOpener = do_GetWeakReference(aOpener);
+}
+
+nsITabParent*
+nsDocShell::GetOpener()
+{
+  nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
+  return opener;
+}
+
+void
+nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote)
+{
+  mOpenedRemote = do_GetWeakReference(aOpenedRemote);
+}
+
+nsITabParent*
+nsDocShell::GetOpenedRemote()
+{
+  nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
+  return openedRemote;
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -37,16 +37,17 @@
 #include "nsIWebPageDescriptor.h"
 #include "nsIWebProgressListener.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsILoadContext.h"
 #include "nsIWebShellServices.h"
 #include "nsILinkHandler.h"
 #include "nsIClipboardCommands.h"
+#include "nsITabParent.h"
 #include "nsCRT.h"
 #include "prtime.h"
 #include "nsRect.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
 }
@@ -897,16 +898,18 @@ private:
     nsCString         mForcedCharset;
     nsCString         mParentCharset;
     int32_t           mParentCharsetSource;
     nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
     nsTObserverArray<nsWeakPtr> mPrivacyObservers;
     nsTObserverArray<nsWeakPtr> mReflowObservers;
     nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
+    nsWeakPtr mOpener;
+    nsWeakPtr mOpenedRemote;
 
     // Separate function to do the actual name (i.e. not _top, _self etc.)
     // searching for FindItemWithName.
     nsresult DoFindItemWithName(const char16_t* aName,
                                 nsISupports* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
                                 nsIDocShellTreeItem** _retval);
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -37,20 +37,21 @@ interface nsISecureBrowserUI;
 interface nsIScriptGlobalObject;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
-
+interface nsITabParent;
+ 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(3ca96c12-b69d-4b54-83c5-25a18d32a22b)]
+[scriptable, builtinclass, uuid(9c65a466-9814-48f8-a4ca-c4600b03b15d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -953,9 +954,25 @@ interface nsIDocShell : nsIDocShellTreeI
    *
    * Used by the Responsive Design View and B2G Simulator.
    *
    * Default is False.
    * Default value can be overriden with
    * docshell.device_size_is_page_size pref.
    */
   [infallible] attribute boolean deviceSizeIsPageSize;
+
+  /**
+   * Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
+   * for notxpcom, so we're relegated to using explicit gets / sets. This
+   * should be fine, considering that these methods should only ever be
+   * called from native code.
+   */
+  [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
+  [noscript,notxpcom,nostdcall] nsITabParent getOpener();
+
+  /**
+   * See the documentation for setOpener and getOpener about why we
+   * don't use attribute here instead.
+   */
+  [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote);
+  [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote();
 };
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11639,17 +11639,17 @@ nsGlobalWindow::OpenInternal(const nsASt
     // dialog is open.
     nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
     if (!aCalledNoScript) {
       // We asserted at the top of this function that aNavigate is true for
       // !aCalledNoScript.
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ true,
-                                aDialog, aNavigate, argv,
+                                aDialog, aNavigate, nullptr, argv,
                                 getter_AddRefs(domReturn));
     } else {
       // Force a system caller here so that the window watcher won't screw us
       // up.  We do NOT want this case looking at the JS context on the stack
       // when searching.  Compare comments on
       // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
 
       // Note: Because nsWindowWatcher is so broken, it's actually important
@@ -11659,17 +11659,17 @@ nsGlobalWindow::OpenInternal(const nsASt
       Maybe<AutoNoJSAPI> nojsapi;
       if (!aContentModal) {
         nojsapi.construct();
       }
 
 
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ false,
-                                aDialog, aNavigate, aExtraArgument,
+                                aDialog, aNavigate, nullptr, aExtraArgument,
                                 getter_AddRefs(domReturn));
 
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // success!
--- a/dom/bindings/CallbackInterface.cpp
+++ b/dom/bindings/CallbackInterface.cpp
@@ -8,25 +8,28 @@
 #include "jsapi.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
-CallbackInterface::GetCallableProperty(JSContext* cx, const char* aPropName,
+CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
                                        JS::MutableHandle<JS::Value> aCallable)
 {
-  if (!JS_GetProperty(cx, CallbackPreserveColor(), aPropName, aCallable)) {
+  if (!JS_GetPropertyById(cx, CallbackPreserveColor(), aPropId, aCallable)) {
     return false;
   }
   if (!aCallable.isObject() ||
       !JS_ObjectIsCallable(cx, &aCallable.toObject())) {
-    nsPrintfCString description("Property '%s'", aPropName);
+    char* propName =
+      JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
+    nsPrintfCString description("Property '%s'", propName);
+    JS_free(cx, propName);
     ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
     return false;
   }
 
   return true;
 }
 
 } // namespace dom
--- a/dom/bindings/CallbackInterface.h
+++ b/dom/bindings/CallbackInterface.h
@@ -26,17 +26,17 @@ class CallbackInterface : public Callbac
 public:
   explicit CallbackInterface(JS::Handle<JSObject*> aCallback,
                              nsIGlobalObject *aIncumbentGlobal)
     : CallbackObject(aCallback, aIncumbentGlobal)
   {
   }
 
 protected:
-  bool GetCallableProperty(JSContext* cx, const char* aPropName,
+  bool GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
                            JS::MutableHandle<JS::Value> aCallable);
 
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CallbackFunction_h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -237,17 +237,17 @@ class CGNativePropertyHooks(CGThing):
             // We declare this as an array so that retrieving a pointer to this
             // binding's property hooks only requires compile/link-time resolvable
             // address arithmetic.  Declaring it as a pointer instead would require
             // doing a run-time load to fetch a pointer to this binding's property
             // hooks.  And then structures which embedded a pointer to this structure
             // would require a run-time load for proper initialization, which would
             // then induce static constructors.  Lots of static constructors.
             extern const NativePropertyHooks sNativePropertyHooks[];
-            """).rstrip()  # BOGUS strip newline from the last line here (!)
+            """)
 
     def define(self):
         if self.descriptor.workers:
             return ""
         if self.descriptor.concrete and self.descriptor.proxy:
             resolveOwnProperty = "ResolveOwnProperty"
             enumerateOwnProperties = "EnumerateOwnProperties"
         elif self.descriptor.needsXrayResolveHooks():
@@ -372,18 +372,17 @@ JS_NULL_OBJECT_OPS
   nullptr, /* getGeneric  */
   nullptr, /* getProperty */
   nullptr, /* getElement */
   nullptr, /* setGeneric */
   nullptr, /* setProperty */
   nullptr, /* setElement */
   nullptr, /* getGenericAttributes */
   nullptr, /* setGenericAttributes */
-  nullptr, /* deleteProperty */
-  nullptr, /* deleteElement */
+  nullptr, /* deleteGeneric */
   nullptr, /* watch */
   nullptr, /* unwatch */
   nullptr, /* slice */
   nullptr, /* enumerate */
   JS_ObjectToOuterObject /* thisObject */
 }
 """
         else:
@@ -396,19 +395,18 @@ JS_NULL_OBJECT_OPS
         elif self.descriptor.interface.getExtendedAttribute("Global"):
             newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = "mozilla::dom::EnumerateGlobal"
         else:
             newResolveHook = "JS_ResolveStub"
             enumerateHook = "JS_EnumerateStub"
 
-        return fill(  # BOGUS extra blank line at the top
-            """
-
+        return fill(
+            """
             static const DOMJSClass Class = {
               { "${name}",
                 ${flags},
                 ${addProperty}, /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
                 JS_PropertyStub,       /* getProperty */
                 JS_StrictPropertyStub, /* setProperty */
                 ${enumerate}, /* enumerate */
@@ -458,19 +456,18 @@ class CGDOMProxyJSClass(CGThing):
     def define(self):
         flags = ["JSCLASS_IS_DOMJSCLASS"]
         # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
         # we don't want people ever adding that to any interface other than
         # HTMLAllCollection.  So just hardcode it here.
         if self.descriptor.interface.identifier.name == "HTMLAllCollection":
             flags.append("JSCLASS_EMULATES_UNDEFINED")
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
-        return fill(  # BOGUS extra blank line at the top
-            """
-
+        return fill(
+            """
             static const DOMJSClass Class = {
               PROXY_CLASS_DEF("${name}",
                               0, /* extra slots */
                               ${flags},
                               ${call}, /* call */
                               nullptr  /* construct */),
               $*{descriptor}
             };
@@ -632,19 +629,18 @@ class CGInterfaceObjectJSClass(CGThing):
             hasinstance = "InterfaceHasInstance"
         else:
             hasinstance = "nullptr"
         prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
         slotCount = "DOM_INTERFACE_SLOTS_BASE"
         if len(self.descriptor.interface.namedConstructors) > 0:
             slotCount += (" + %i /* slots for the named constructors */" %
                           len(self.descriptor.interface.namedConstructors))
-        return fill(  # BOGUS extra newline at the top
-            """
-
+        return fill(
+            """
             static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
               {
                 "Function",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 JS_PropertyStub,       /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
                 JS_PropertyStub,       /* getProperty */
                 JS_StrictPropertyStub, /* setProperty */
@@ -1415,18 +1411,17 @@ class CGAbstractClassHook(CGAbstractStat
     Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
     'this' unwrapping as it assumes that the unwrapped type is always known.
     """
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
                                         args)
 
     def definition_body_prologue(self):
-        return ("\n"  # BOGUS extra blank line at start of function
-                "%s* self = UnwrapDOMObject<%s>(obj);\n" %
+        return ("%s* self = UnwrapDOMObject<%s>(obj);\n" %
                 (self.descriptor.nativeType, self.descriptor.nativeType))
 
     def definition_body(self):
         return self.definition_body_prologue() + self.generate_code()
 
     def generate_code(self):
         assert False  # Override me!
 
@@ -1537,19 +1532,18 @@ class CGClassConstructor(CGAbstractStati
         # the name JS sees is the interface name; for named constructors
         # identifier.name is the actual name.
         name = self._ctor.identifier.name
         if name != "constructor":
             ctorName = name
         else:
             ctorName = self.descriptor.interface.identifier.name
 
-        preamble = fill(  # BOGUS extra blank line at beginning of function body
-            """
-
+        preamble = fill(
+            """
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
             JS::Rooted<JSObject*> obj(cx, &args.callee());
             $*{chromeOnlyCheck}
             bool mayInvoke = args.isConstructing();
             #ifdef RELEASE_BUILD
             mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome();
             #endif // RELEASE_BUILD
             if (!mayInvoke) {
@@ -1561,17 +1555,17 @@ class CGClassConstructor(CGAbstractStati
             chromeOnlyCheck=chromeOnlyCheck,
             ctorName=ctorName)
 
         name = self._ctor.identifier.name
         nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
         callGenerator = CGMethodCall(nativeName, True, self.descriptor,
                                      self._ctor, isConstructor=True,
                                      constructorName=ctorName)
-        return preamble + callGenerator.define()
+        return preamble + "\n" + callGenerator.define()
 
 
 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
 class CGConstructNavigatorObjectHelper(CGAbstractStaticMethod):
     """
     Construct a new JS-implemented WebIDL DOM object, for use on navigator.
     """
     def __init__(self, descriptor):
@@ -1703,19 +1697,17 @@ class CGClassHasInstanceHook(CGAbstractS
         if not NeedsGeneratedHasInstance(self.descriptor):
             return ""
         return CGAbstractStaticMethod.define(self)
 
     def definition_body(self):
         return self.generate_code()
 
     def generate_code(self):
-        # BOGUS extra blank line at start of function
         header = dedent("""
-
             if (!vp.isObject()) {
               *bp = false;
               return true;
             }
 
             JS::Rooted<JSObject*> instance(cx, &vp.toObject());
             """)
         if self.descriptor.interface.hasInterfacePrototypeObject():
@@ -1734,18 +1726,17 @@ class CGClassHasInstanceHook(CGAbstractS
 
                     // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
                     nsISupports* native =
                       nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
                                                                       js::UncheckedUnwrap(instance, /* stopAtOuter = */ false));
                     nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
                     *bp = !!qiResult;
                     return true;
-
-                    """,  # BOGUS extra blank line at end of function
+                    """,
                     nativeType=self.descriptor.nativeType,
                     name=self.descriptor.interface.identifier.name))
 
         hasInstanceCode = dedent("""
 
             const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtOuter = */ false));
             *bp = false;
             if (!domClass) {
@@ -2351,18 +2342,17 @@ class CGNativeProperties(CGList):
                 generateNativeProperties("sChromeOnlyNativeProperties", True))
 
         CGList.__init__(self, nativeProperties, "\n")
 
     def declare(self):
         return ""
 
     def define(self):
-        # BOGUSly strip off a newline
-        return CGList.define(self).rstrip()
+        return CGList.define(self)
 
 
 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
     """
     Generate the CreateInterfaceObjects method for an interface descriptor.
 
     properties should be a PropertyArrays instance.
     """
@@ -2581,25 +2571,23 @@ class CGGetPerInterfaceObject(CGAbstract
     def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aGlobal')] + extraArgs
         CGAbstractMethod.__init__(self, descriptor, name,
                                   'JS::Handle<JSObject*>', args)
         self.id = idPrefix + "id::" + self.descriptor.name
 
     def definition_body(self):
-        # BOGUS extra blank line at the beginning of the code below
-        # BOGUS - should be a blank line between an if-block and following comment below
         return fill(
             """
-
             /* Make sure our global is sane.  Hopefully we can remove this sometime */
             if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
               return JS::NullPtr();
             }
+
             /* Check to see whether the interface objects are already installed */
             ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
             if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
               CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal);
             }
 
             /*
              * The object might _still_ be null, but that's OK.
@@ -2617,41 +2605,39 @@ class CGGetProtoObjectMethod(CGGetPerInt
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
                                          "prototypes::")
 
     def definition_body(self):
-        # BOGUS extra blank line at start of method
         return dedent("""
-
             /* Get the interface prototype object for this class.  This will create the
                object as needed. */
             bool aDefineOnGlobal = true;
+
             """) + CGGetPerInterfaceObject.definition_body(self)
 
 
 class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface constructor object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(
             self, descriptor, "GetConstructorObject",
             "constructors::",
             extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
 
     def definition_body(self):
-        # BOGUS extra blank line at start of method
         return dedent("""
-
             /* Get the interface object for this class.  This will create the object as
                needed. */
+
             """) + CGGetPerInterfaceObject.definition_body(self)
 
 
 class CGDefineDOMInterfaceMethod(CGAbstractMethod):
     """
     A method for resolve hooks to try to lazily define the interface object for
     a given interface.
     """
@@ -2817,18 +2803,17 @@ def InitUnforgeableProperties(descriptor
     """
     unforgeableAttrs = properties.unforgeableAttrs
     if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
         return ""
 
     if descriptor.proxy:
         unforgeableProperties = CGGeneric(
             "// Unforgeable properties on proxy-based bindings are stored in an object held\n"
-            "// by the interface prototype object.\n"
-            "\n")  # BOGUS extra blank line
+            "// by the interface prototype object.\n")
     else:
         unforgeableProperties = CGWrapper(
             InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
             pre=(
                 "// Important: do unforgeable property setup after we have handed\n"
                 "// over ownership of the C++ object to obj as needed, so that if\n"
                 "// we fail and it ends up GCed it won't have problems in the\n"
                 "// finalizer trying to drop its ownership of the C++ object.\n"))
@@ -2854,17 +2839,17 @@ def InitMemberSlots(descriptor, wrapperC
     Initialize member slots on our JS object if we're supposed to have some.
 
     Note that this is called after the SetWrapper() call in the
     wrapperCache case, since that can affect how our getters behave
     and we plan to invoke them here.  So if we fail, we need to
     ClearWrapper.
     """
     if not descriptor.interface.hasMembersInSlots():
-        return "\n"  # BOGUS blank line only if this returns empty
+        return ""
     if wrapperCache:
         clearWrapper = "  aCache->ClearWrapper();\n"
     else:
         clearWrapper = ""
     return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n"
             "%s"
             "  return nullptr;\n"
             "}\n" % clearWrapper)
@@ -4216,18 +4201,17 @@ def getJSToNativeConversionInfo(type, de
             return handleJSObjectType(type, isMember, failureCode)
 
         if descriptor.interface.isCallback():
             name = descriptor.interface.identifier.name
             if type.nullable() or isCallbackReturnValue:
                 declType = CGGeneric("nsRefPtr<%s>" % name)
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
-            # BOGUS extra blank line here turns out to be at the end of a block:
-            conversion = indent(CGCallbackTempRoot(name).define()) + "\n"
+            conversion = indent(CGCallbackTempRoot(name).define())
 
             template = wrapObjectTemplate(conversion, type,
                                           "${declName} = nullptr;\n",
                                           failureCode)
             return JSToNativeConversionInfo(template, declType=declType,
                                             dealWithOptional=isOptional)
 
         # This is an interface that we implement as a concrete class
@@ -4360,18 +4344,17 @@ def getJSToNativeConversionInfo(type, de
         # Again, this is a bit strange since we are actually building a
         # template string here. ${objRef} and $*{badType} below are filled in
         # right now; $${val} expands to ${val}, to be filled in later.
         template = fill(
             """
             if (!${objRef}.Init(&$${val}.toObject())) {
               $*{badType}
             }
-
-            """,  # BOGUS extra blank line
+            """,
             objRef=objRef,
             badType=onFailureBadType(failureCode, type.name).define())
         template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
                                       failureCode)
         if not isMember:
             # This is a bit annoying.  In a union we don't want to have a
             # holder, since unions don't support that.  But if we're optional we
             # want to have a holder, so that the callee doesn't see
@@ -4450,18 +4433,17 @@ def getJSToNativeConversionInfo(type, de
             return JSToNativeConversionInfo(
                 fill(
                     """
                     {
                       binding_detail::FakeDependentString str;
                       $*{convert}
                       $*{assign}
                     }
-
-                    """,  # BOGUS extra newline
+                    """,
                     convert=getConversionCode("str"),
                     assign=assignString),
                 declType=declType,
                 dealWithOptional=isOptional)
 
         if isOptional:
             declType = "Optional<nsAString>"
             holderType = CGGeneric("binding_detail::FakeDependentString")
@@ -4591,17 +4573,17 @@ def getJSToNativeConversionInfo(type, de
                 "}\n")
         elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
             if not isDefinitelyObject:
                 haveObject = "${val}.isObject()"
                 if defaultValue is not None:
                     assert(isinstance(defaultValue, IDLNullValue))
                     haveObject = "${haveValue} && " + haveObject
                 template = CGIfElseWrapper(haveObject,
-                                           CGGeneric(conversion + "\n"),  # BOGUS extra blank line
+                                           CGGeneric(conversion),
                                            CGGeneric("${declName} = nullptr;\n")).define()
             else:
                 template = conversion
         else:
             template = wrapObjectTemplate(
                 "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" +
                 conversion +
                 "} else {\n" +
@@ -6354,19 +6336,18 @@ class CGMethodCall(CGThing):
             # Special case: we can just do a per-signature method call
             # here for our one signature and not worry about switching
             # on anything.
             signature = signatures[0]
             self.cgRoot = CGList([getPerSignatureCall(signature)])
             requiredArgs = requiredArgCount(signature)
 
             if requiredArgs > 0:
-                code = fill(  # BOGUS extra blank line
+                code = fill(
                     """
-
                     if (args.length() < ${requiredArgs}) {
                       return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
                     }
                     """,
                     requiredArgs=requiredArgs,
                     methodName=methodName)
                 self.cgRoot.prepend(CGGeneric(code))
             return
@@ -6699,39 +6680,42 @@ class CGMethodCall(CGThing):
 
         overloadCGThings = []
         overloadCGThings.append(
             CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
                       maxArgCount))
         overloadCGThings.append(
             CGSwitch("argcount",
                      argCountCases,
-                     # BOGUS extra blank line at end of default block
-                     CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' %
+                     CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' %
                                methodName)))
         overloadCGThings.append(
             CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
                       'return false;\n'))
-        self.cgRoot = CGWrapper(CGList(overloadCGThings), pre="\n")
+        self.cgRoot = CGList(overloadCGThings)
 
     def define(self):
         return self.cgRoot.define()
 
 
 class CGGetterCall(CGPerSignatureCall):
     """
     A class to generate a native object getter call for a particular IDL
     getter.
     """
     def __init__(self, returnType, nativeMethodName, descriptor, attr):
         CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
                                     attr.isStatic(), descriptor, attr,
                                     getter=True)
 
 
+class FakeIdentifier():
+    def __init__(self, name):
+        self.name = name
+
 class FakeArgument():
     """
     A class that quacks like an IDLArgument.  This is used to make
     setters look like method calls or for special operations.
     """
     def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False):
         self.type = type
         self.optional = False
@@ -6741,20 +6725,17 @@ class FakeArgument():
         self.treatNullAs = interfaceMember.treatNullAs
         if isinstance(interfaceMember, IDLAttribute):
             self.enforceRange = interfaceMember.enforceRange
             self.clamp = interfaceMember.clamp
         else:
             self.enforceRange = False
             self.clamp = False
 
-        class FakeIdentifier():
-            def __init__(self):
-                self.name = name
-        self.identifier = FakeIdentifier()
+        self.identifier = FakeIdentifier(name)
 
     def allowTreatNonCallableAsNull(self):
         return self._allowTreatNonCallableAsNull
 
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
@@ -7854,46 +7835,42 @@ class CGEnum(CGThing):
 
     def stringsNamespace(self):
         return self.enum.identifier.name + "Values"
 
     def nEnumStrings(self):
         return len(self.enum.values()) + 1
 
     def declare(self):
-        decl = fill(  # BOGUS extra newline at top
-            """
-
+        decl = fill(
+            """
             MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t)
               $*{enums}
               EndGuard_
             MOZ_END_ENUM_CLASS(${name})
             """,
             name=self.enum.identifier.name,
             enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n")
         strings = CGNamespace(self.stringsNamespace(),
                               CGGeneric(declare="extern const EnumEntry %s[%d];\n"
                                         % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())))
         return decl + "\n" + strings.declare()
 
     def define(self):
-        strings = fill(  # BOGUS extra newline at top
-            """
-
+        strings = fill(
+            """
             extern const EnumEntry ${name}[${count}] = {
               $*{entries}
               { nullptr, 0 }
             };
             """,
             name=ENUM_ENTRY_VARIABLE_NAME,
             count=self.nEnumStrings(),
             entries=''.join('{"%s", %d},\n' % (val, len(val))
                             for val in self.enum.values()))
-        # BOGUS - CGNamespace automatically indents; the extra indent() below causes
-        # the output to be indented 4 spaces.
         return CGNamespace(self.stringsNamespace(),
                            CGGeneric(define=indent(strings))).define()
 
     def deps(self):
         return self.enum.getDeps()
 
 
 def getUnionAccessorSignatureType(type, descriptorProvider):
@@ -8261,22 +8238,20 @@ class CGUnionStruct(CGThing):
                 body=CGSwitch("mType", toJSValCases,
                               default=CGGeneric("return false;\n")).define(),
                 const=True))
 
         constructors = [ctor]
         selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
         if self.ownsMembers:
             if traceCases:
-                # BOGUS blank line in default case
                 traceBody = CGSwitch("mType", traceCases,
-                                     default=CGGeneric("\n")).define()
-            else:
-                # BOGUS blank line in method
-                traceBody = "\n"
+                                     default=CGGeneric("")).define()
+            else:
+                traceBody = ""
             methods.append(ClassMethod("TraceUnion", "void",
                                        [Argument("JSTracer*", "trc")],
                                        body=traceBody))
             if CGUnionStruct.isUnionCopyConstructible(self.type):
                 constructors.append(
                     ClassConstructor(
                         [Argument("const %s&" % selfName, "aOther")],
                         bodyInHeader=True,
@@ -8711,17 +8686,17 @@ class ClassDestructor(ClassItem):
             ${decorators}
             ${className}::~${className}()
             {
               $*{body}
             }
             """,
             decorators=self.getDecorators(False),
             className=cgClass.getNameString(),
-            body=self.getBody() or "\n")  # BOGUS extra blank line if empty
+            body=self.getBody())
 
 
 class ClassMember(ClassItem):
     def __init__(self, name, type, visibility="private", static=False,
                  body=None):
         self.type = type
         self.static = static
         self.body = body
@@ -8953,18 +8928,17 @@ class CGResolveOwnProperty(CGAbstractSta
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::Handle<jsid>', 'id'),
                 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
                 ]
         CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty",
                                         "bool", args)
 
     def definition_body(self):
-        # BOGUS extra blank line at end of function
-        return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n"
+        return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n"
 
 
 class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod):
     """
     An implementation of Xray ResolveOwnProperty stuff for things that have a
     newresolve hook.
     """
     def __init__(self, descriptor):
@@ -9012,18 +8986,17 @@ class CGEnumerateOwnProperties(CGAbstrac
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'wrapper'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::AutoIdVector&', 'props')]
         CGAbstractStaticMethod.__init__(self, descriptor,
                                         "EnumerateOwnProperties", "bool", args)
 
     def definition_body(self):
-        # BOGUS extra newline
-        return "return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n"
+        return "return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n"
 
 
 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
     """
     An implementation of Xray EnumerateOwnProperties stuff for things
     that have a newresolve hook.
     """
     def __init__(self, descriptor):
@@ -9520,39 +9493,37 @@ class CGDOMJSProxyHandler_defineProperty
                 name=self.descriptor.name)
 
         if UseHolderForUnforgeable(self.descriptor):
             defineOnUnforgeable = ("bool hasUnforgeable;\n"
                                    "if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
                                    "  return false;\n"
                                    "}\n"
                                    "if (hasUnforgeable) {\n"
-                                   "  *defined = true;"  # SUPER BOGUS missing newline
+                                   "  *defined = true;\n"
                                    "  bool unused;\n"
                                    "  return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n"
-                                   "}\n"
-                                   "\n")  # BOGUS extra blank line at end of block or method
+                                   "}\n")
             set += CallOnUnforgeableHolder(self.descriptor,
                                            defineOnUnforgeable,
                                            "xpc::WrapperFactory::IsXrayWrapper(proxy)")
 
         namedSetter = self.descriptor.operations['NamedSetter']
         if namedSetter:
             if self.descriptor.operations['NamedCreator'] is not namedSetter:
                 raise TypeError("Can't handle creator that's different from the setter")
             # If we support indexed properties, we won't get down here for
             # indices, so we can just do our setter unconditionally here.
             set += fill(
                 """
                 *defined = true;
                 $*{callSetter}
 
                 return true;
-
-                """,  # BOGUS extra blank line at end of method
+                """,
                 callSetter=CGProxyNamedSetter(self.descriptor).define())
         else:
             if self.descriptor.supportsNamedProperties():
                 set += fill(
                     """
                     $*{presenceChecker}
 
                     if (found) {
@@ -9647,22 +9618,20 @@ class CGDOMJSProxyHandler_delete(ClassMe
         namedBody = getDeleterBody("Named")
         if namedBody is not None:
             # We always return above for an index id in the case when we support
             # indexed properties, so we can just treat the id as a name
             # unconditionally here.
             delete += (namedBody +
                        "if (found) {\n"
                        "  return true;\n"
-                       "}\n\n")  # BOGUS extra blank line
+                       "}\n")
             if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
                 delete = CGIfWrapper(CGGeneric(delete),
                                      "!HasPropertyOnPrototype(cx, proxy, id)").define()
-        else:
-            delete += "\n"  # BOGUS extra blank line
 
         delete += dedent("""
 
             return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);
             """)
 
         return delete
 
@@ -9785,18 +9754,17 @@ class CGDOMJSProxyHandler_hasOwn(ClassMe
 
         if self.descriptor.supportsNamedProperties():
             # If we support indexed properties we always return above for index
             # property names, so no need to check for those here.
             named = (CGProxyNamedPresenceChecker(self.descriptor).define() +
                      "\n" +
                      "*bp = found;\n")
             if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
-                # BOGUS extra blank line at end of block
-                named = CGIfWrapper(CGGeneric(named + "return true;\n\n"),
+                named = CGIfWrapper(CGGeneric(named + "return true;\n"),
                                     "!HasPropertyOnPrototype(cx, proxy, id)").define()
                 named += "*bp = false;\n"
             else:
                 named += "\n"
         else:
             named = "*bp = false;\n"
 
         return fill(
@@ -10470,17 +10438,39 @@ class CGNamespacedEnum(CGThing):
         self.node = curr
 
     def declare(self):
         return self.node.declare()
 
     def define(self):
         return ""
 
-
+def initIdsClassMethod(identifiers, atomCacheName):
+    idinit = ['!atomsCache->%s.init(cx, "%s")' %
+              (CGDictionary.makeIdName(id),
+               id)
+              for id in identifiers]
+    idinit.reverse()
+    body = fill(
+        """
+        MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
+
+        // Initialize these in reverse order so that any failure leaves the first one
+        // uninitialized.
+        if (${idinit}) {
+          return false;
+        }
+        return true;
+        """,
+        idinit=" ||\n    ".join(idinit))
+    return ClassMethod("InitIds", "bool", [
+        Argument("JSContext*", "cx"),
+        Argument("%s*" % atomCacheName, "atomsCache")
+    ], static=True, body=body, visibility="private")
+        
 class CGDictionary(CGThing):
     def __init__(self, dictionary, descriptorProvider):
         self.dictionary = dictionary
         self.descriptorProvider = descriptorProvider
         self.needToInitIds = len(dictionary.members) > 0
         self.memberInfo = [
             (member,
              getJSToNativeConversionInfo(
@@ -10635,65 +10625,40 @@ class CGDictionary(CGThing):
                 }
                 rval.set(JS::ObjectValue(*obj));
 
                 """)
 
         if self.memberInfo:
             body += "\n".join(self.getMemberDefinition(m).define()
                               for m in self.memberInfo)
-        else:
-            body += "\n"  # BOGUS extra blank line
         body += "\nreturn true;\n"
 
         return ClassMethod("ToObject", "bool", [
             Argument('JSContext*', 'cx'),
             Argument('JS::MutableHandle<JS::Value>', 'rval'),
         ], const=True, body=body)
 
     def initIdsMethod(self):
         assert self.needToInitIds
-        idinit = ['!atomsCache->%s.init(cx, "%s")' %
-                  (CGDictionary.makeIdName(m.identifier.name),
-                   m.identifier.name)
-                  for m in self.dictionary.members]
-        idinit.reverse()
-        body = fill(
-            """
-            MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
-
-            // Initialize these in reverse order so that any failure leaves the first one
-            // uninitialized.
-            if (${idinit}) {
-              return false;
-            }
-            return true;
-            """,
-            idinit=" ||\n    ".join(idinit))
-
-        return ClassMethod("InitIds", "bool", [
-            Argument("JSContext*", "cx"),
-            Argument("%sAtoms*" % self.makeClassName(self.dictionary),
-                     "atomsCache"),
-        ], static=True, body=body, visibility="private")
+        return initIdsClassMethod([m.identifier.name for m in self.dictionary.members],
+                                  "%sAtoms" % self.makeClassName(self.dictionary))             
 
     def traceDictionaryMethod(self):
         body = ""
         if self.dictionary.parent:
             cls = self.makeClassName(self.dictionary.parent)
             body += "%s::TraceDictionary(trc);\n" % cls
 
         memberTraces = [self.getMemberTrace(m)
                         for m in self.dictionary.members
                         if typeNeedsRooting(m.type)]
 
         if memberTraces:
             body += "\n".join(memberTraces)
-        else:
-            body += "\n"  # BOGUS extra newline
 
         return ClassMethod("TraceDictionary", "void", [
             Argument("JSTracer*", "trc"),
         ], body=body)
 
     def assignmentOperator(self):
         body = CGList([])
         if self.dictionary.parent:
@@ -10716,17 +10681,17 @@ class CGDictionary(CGThing):
             else:
                 memberAssign = CGGeneric(
                     "%s = aOther.%s;\n" % (memberName, memberName))
             body.append(memberAssign)
         return ClassMethod(
             "operator=", "void",
             [Argument("const %s&" % self.makeClassName(self.dictionary),
                       "aOther")],
-            body=body.define() or "\n")  # BOGUS blank line when empty
+            body=body.define())
 
     def getStructs(self):
         d = self.dictionary
         selfName = self.makeClassName(d)
         members = [ClassMember(self.makeMemberName(m[0].identifier.name),
                                self.getMemberType(m),
                                visibility="public",
                                body=self.getMemberInitializer(m))
@@ -11212,29 +11177,33 @@ class CGForwardDeclarations(CGWrapper):
 
         # Needed for at least Wrap.
         for d in descriptors:
             builder.add(d.nativeType)
 
         # We just about always need NativePropertyHooks
         builder.addInMozillaDom("NativePropertyHooks")
         builder.addInMozillaDom("ProtoAndIfaceCache")
+        # Add the atoms cache type, even if we don't need it.
+        for d in descriptors:
+            builder.add(d.nativeType + "Atoms", isStruct=True)
 
         for callback in mainCallbacks:
             forwardDeclareForType(callback)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='mainthreadonly')
 
         for callback in workerCallbacks:
             forwardDeclareForType(callback)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='workeronly')
 
         for d in callbackInterfaces:
             builder.add(d.nativeType)
+            builder.add(d.nativeType + "Atoms", isStruct=True)
             for t in getTypesFromDescriptor(d):
                 forwardDeclareForType(t)
 
         for d in dictionaries:
             if len(d.members) > 0:
                 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
             for t in getTypesFromDictionary(d):
                 forwardDeclareForType(t)
@@ -11278,27 +11247,28 @@ class CGBindingRoot(CGThing):
         bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
         hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
                                                    workers=True)) != 0
         bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff
         bindingHeaders["nsThreadUtils.h"] = hasWorkerStuff
 
         dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
         bindingHeaders["nsCxPusher.h"] = dictionaries
-        bindingHeaders["AtomList.h"] = any(
+        hasNonEmptyDictionaries = any(
             len(dict.members) > 0 for dict in dictionaries)
         mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                             workers=False)
         workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                               workers=True)
         callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
                                                     isCallback=True)
         jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
                                               isJSImplemented=True)
         bindingHeaders["nsPIDOMWindow.h"] = jsImplemented
+        bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
 
         def addHeaderBasedOnTypes(header, typeChecker):
             bindingHeaders[header] = (
                 bindingHeaders.get(header, False) or
                 any(map(typeChecker,
                         getAllTypes(descriptors + callbackDescriptors,
                                     dictionaries,
                                     mainCallbacks + workerCallbacks))))
@@ -12564,17 +12534,17 @@ class CGCallback(CGClass):
         # For our public methods that needThisHandling we want most of the
         # same args and the same return type as what CallbackMember
         # generates.  So we want to take advantage of all its
         # CGNativeMember infrastructure, but that infrastructure can't deal
         # with templates and most especially template arguments.  So just
         # cheat and have CallbackMember compute all those things for us.
         realMethods = []
         for method in methods:
-            if not method.needThisHandling:
+            if not isinstance(method, CallbackMember) or not method.needThisHandling:
                 realMethods.append(method)
             else:
                 realMethods.extend(self.getMethodImpls(method))
         realMethods.append(
             ClassMethod("operator==", "bool",
                         [Argument("const %s&" % name, "aOther")],
                         inline=True, bodyInHeader=True,
                         const=True,
@@ -12704,23 +12674,31 @@ class CGCallbackInterface(CGCallback):
                    if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
         methods = [CallbackOperation(m, sig, descriptor) for m in methods
                    for sig in m.signatures()]
         if iface.isJSImplemented() and iface.ctor():
             sigs = descriptor.interface.ctor().signatures()
             if len(sigs) != 1:
                 raise TypeError("We only handle one constructor.  See bug 869268.")
             methods.append(CGJSImplInitOperation(sigs[0], descriptor))
+        if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()):
+            methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name)
+                                               for m in iface.members
+                                               if m.isAttr() or m.isMethod()] +
+                                              (["__init"] if iface.isJSImplemented() and iface.ctor() else []),
+                                              iface.identifier.name + "Atoms"))
         CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
                             methods, getters=getters, setters=setters)
 
 
 class FakeMember():
-    def __init__(self):
+    def __init__(self, name=None):
         self.treatNullAs = "Default"
+        if name is not None:
+            self.identifier = FakeIdentifier(name)
 
     def isStatic(self):
         return False
 
     def isAttr(self):
         return False
 
     def isMethod(self):
@@ -12821,30 +12799,26 @@ class CallbackMember(CGNativeMember):
                                         exceptionCode=self.exceptionCode,
                                         isCallbackReturnValue=isCallbackReturnValue,
                                         sourceDescription=sourceDescription),
             replacements)
         assignRetval = string.Template(
             self.getRetvalInfo(self.retvalType,
                                False)[2]).substitute(replacements)
         type = convertType.define()
-        if type == "":
-            type = "\n"  # BOGUS extra blank line
-        if assignRetval == "":
-            assignRetval = "\n"  # BOGUS extra blank line
         return type + assignRetval
 
     def getArgConversions(self):
         # Just reget the arglist from self.originalSig, because our superclasses
         # just have way to many members they like to clobber, so I can't find a
         # safe member name to store it in.
         argConversions = [self.getArgConversion(i, arg)
                           for i, arg in enumerate(self.originalSig[1])]
         if not argConversions:
-            return "\n\n"  # BOGUS extra blank line
+            return "\n"
 
         # Do them back to front, so our argc modifications will work
         # correctly, because we examine trailing arguments first.
         argConversions.reverse()
         # Wrap each one in a scope so that any locals it has don't leak out, and
         # also so that we can just "break;" for our successCode.
         argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
                                     pre="do {\n",
@@ -13067,22 +13041,25 @@ class CallbackOperationBase(CallbackMeth
             """)
 
     def getThisVal(self):
         return "thisValue"
 
     def getCallableDecl(self):
         getCallableFromProp = fill(
             """
-            if (!GetCallableProperty(cx, "${methodName}", &callable)) {
+            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+            if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+                !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
-            methodName=self.methodName,
+            methodAtomName=CGDictionary.makeIdName(self.methodName),
+            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
             errorReturn=self.getDefaultRetval())
         if not self.singleOperation:
             return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
         return fill(
             """
             bool isCallable = JS_ObjectIsCallable(cx, mCallback);
             JS::Rooted<JS::Value> callable(cx);
             if (isCallable) {
@@ -13141,22 +13118,25 @@ class CallbackGetter(CallbackAccessor):
 
     def getRvalDecl(self):
         return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
 
     def getCall(self):
         return fill(
             """
             JS::Rooted<JSObject *> callback(cx, mCallback);
-            if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) {
+            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+            if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+                !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
-            attrName=self.descriptorProvider.binaryNameFor(self.attrName),
+            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+            attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
             errorReturn=self.getDefaultRetval())
 
 
 class CallbackSetter(CallbackAccessor):
     def __init__(self, attr, descriptor):
         CallbackAccessor.__init__(self, attr,
                                   (BuiltinTypes[IDLBuiltinType.Types.void],
                                    [FakeArgument(attr.type, attr)]),
@@ -13166,22 +13146,25 @@ class CallbackSetter(CallbackAccessor):
     def getRvalDecl(self):
         # We don't need an rval
         return ""
 
     def getCall(self):
         return fill(
             """
             MOZ_ASSERT(argv.length() == 1);
-            if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv[0])) {
+            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+            if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+                !JS_SetPropertyById(cx, CallbackPreserveColor(), atomsCache->${attrAtomName}, argv[0])) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
-            attrName=self.descriptorProvider.binaryNameFor(self.attrName),
+            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+            attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
             errorReturn=self.getDefaultRetval())
 
     def getArgcDecl(self):
         return None
 
 
 class CGJSImplInitOperation(CallbackOperationBase):
     """
@@ -13206,31 +13189,48 @@ class GlobalGenRoots():
 
     @staticmethod
     def GeneratedAtomList(config):
         # Atom enum
         dictionaries = config.dictionaries
 
         structs = []
 
+        def memberToAtomCacheMember(binaryNameFor, m):
+            binaryMemberName = binaryNameFor(m.identifier.name)
+            return ClassMember(CGDictionary.makeIdName(binaryMemberName),
+                               "InternedStringId", visibility="public")
+        def buildAtomCacheStructure(idlobj, binaryNameFor, members):
+            classMembers = [memberToAtomCacheMember(binaryNameFor, m)
+                            for m in members]
+            structName = idlobj.identifier.name + "Atoms"
+            return (structName,
+                    CGWrapper(CGClass(structName,
+                                      bases=None,
+                                      isStruct=True,
+                                      members=classMembers), post='\n'))
+
         for dict in dictionaries:
-            dictMembers = dict.members
-            if len(dictMembers) == 0:
+            if len(dict.members) == 0:
                 continue
 
-            classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name),
-                                        "InternedStringId",
-                                        visibility="public") for m in dictMembers]
-
-            structName = dict.identifier.name + "Atoms"
-            structs.append((structName,
-                            CGWrapper(CGClass(structName,
-                                              bases=None,
-                                              isStruct=True,
-                                              members=classMembers), post='\n')))
+            structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members))
+
+        for d in (config.getDescriptors(isJSImplemented=True) +
+                  config.getDescriptors(isCallback=True)):
+            members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
+            if d.interface.isJSImplemented() and d.interface.ctor():
+                # We'll have an __init() method.
+                members.append(FakeMember('__init'))
+            if len(members) == 0:
+                continue
+
+            structs.append(buildAtomCacheStructure(d.interface,
+                                                   lambda x: d.binaryNameFor(x),
+                                                   members))
 
         structs.sort()
         generatedStructs = [struct for structName, struct in structs]
         structNames = [structName for structName, struct in structs]
 
         mainStruct = CGWrapper(CGClass("PerThreadAtomCache",
                                        bases=[ClassBase(structName) for structName in structNames],
                                        isStruct=True),
--- a/dom/bindings/OwningNonNull.h
+++ b/dom/bindings/OwningNonNull.h
@@ -38,16 +38,21 @@ public:
     return mPtr;
   }
 
   void operator=(T* aValue)
   {
     init(aValue);
   }
 
+  void operator=(T& aValue)
+  {
+    init(&aValue);
+  }
+
   void operator=(const already_AddRefed<T>& aValue)
   {
     init(aValue);
   }
 
   already_AddRefed<T> forget()
   {
 #ifdef DEBUG
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -195,17 +195,21 @@ private:
 
 namespace mozilla {
 namespace dom {
 
 TabParent* sEventCapturer;
 
 TabParent *TabParent::mIMETabParent = nullptr;
 
-NS_IMPL_ISUPPORTS(TabParent, nsITabParent, nsIAuthPromptProvider, nsISecureBrowserUI)
+NS_IMPL_ISUPPORTS(TabParent,
+                  nsITabParent,
+                  nsIAuthPromptProvider,
+                  nsISecureBrowserUI,
+                  nsISupportsWeakReference)
 
 TabParent::TabParent(ContentParent* aManager, const TabContext& aContext, uint32_t aChromeFlags)
   : TabContext(aContext)
   , mFrameElement(nullptr)
   , mIMESelectionAnchor(0)
   , mIMESelectionFocus(0)
   , mIMEComposing(false)
   , mIMECompositionEnding(false)
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -12,16 +12,17 @@
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "nsIXULBrowserWindow.h"
+#include "nsWeakReference.h"
 #include "Units.h"
 #include "js/TypeDecls.h"
 
 class nsFrameLoader;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 class nsIWidget;
@@ -45,16 +46,17 @@ class ClonedMessageData;
 class ContentParent;
 class Element;
 struct StructuredCloneData;
 
 class TabParent : public PBrowserParent 
                 , public nsITabParent 
                 , public nsIAuthPromptProvider
                 , public nsISecureBrowserUI
+                , public nsSupportsWeakReference
                 , public TabContext
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
 
 public:
     // nsITabParent
     NS_DECL_NSITABPARENT
--- a/embedding/base/nsIWindowCreator2.idl
+++ b/embedding/base/nsIWindowCreator2.idl
@@ -12,20 +12,21 @@
  * @see nsIWindowCreator
  * @see nsIWindowWatcher
  *
  * @status
  */
 
 #include "nsIWindowCreator.idl"
 
+interface nsITabParent;
 interface nsIURI;
 interface nsIWebBrowserChrome;
 
-[scriptable, uuid(f673ec81-a4b0-11d6-964b-eb5a2bf216fc)]
+[scriptable, uuid(e28f810b-9b49-4927-a4be-62a74fadfe21)]
 
 interface nsIWindowCreator2 : nsIWindowCreator {
 
   /**
    * Definitions for contextFlags
    */
 
   // Likely that the window is an advertising popup. 
@@ -39,20 +40,23 @@ interface nsIWindowCreator2 : nsIWindowC
                     to the underlying OS).
       @param chromeFlags Chrome features from nsIWebBrowserChrome
       @param contextFlags Flags about the context of the window being created.
       @param uri The URL for which this window is intended. It can be null
                  or zero-length. The implementation of this interface
                  may use the URL to help determine what sort of window
                  to open or whether to cancel window creation. It will not
                  load the URL.
+      @param aOpeningTab The TabParent that is trying to open this new chrome
+                         window. Can be nullptr.
       @param cancel Return |true| to reject window creation. If true the
                     implementation has determined the window should not
                     be created at all. The caller should not default
                     to any possible backup scheme for creating the window.
       @return the new window. Will be null if canceled or an error occurred.
   */
   nsIWebBrowserChrome createChromeWindow2(in nsIWebBrowserChrome parent,
                                           in uint32_t chromeFlags,
                                           in uint32_t contextFlags,
                                           in nsIURI uri,
+                                          in nsITabParent aOpeningTab,
                                           out boolean cancel);
 };
--- a/embedding/components/windowwatcher/public/nsPIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/public/nsPIWindowWatcher.idl
@@ -10,18 +10,19 @@
 
 #include "nsISupports.idl"
 
 interface nsIDOMWindow;
 interface nsISimpleEnumerator;
 interface nsIWebBrowserChrome;
 interface nsIDocShellTreeItem;
 interface nsIArray;
+interface nsITabParent;
 
-[uuid(00788A84-152F-4BD8-A814-FD8EB545DB29)]
+[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d2)]
 
 interface nsPIWindowWatcher : nsISupports
 {
   /** A window has been created. Add it to our list.
       @param aWindow the window to add
       @param aChrome the corresponding chrome window. The DOM window
                      and chrome will be mapped together, and the corresponding
                      chrome can be retrieved using the (not private)
@@ -46,31 +47,35 @@ interface nsPIWindowWatcher : nsISupport
       @param aName window name from JS window.open. can be null.  If a window
              with this name already exists, the openWindow call may just load
              aUrl in it (if aUrl is not null) and return it.
       @param aFeatures window features from JS window.open. can be null.
       @param aCalledFromScript true if we were called from script.
       @param aDialog use dialog defaults (see nsIDOMWindow::openDialog)
       @param aNavigate true if we should navigate the new window to the
              specified URL.
+      @param aOpeningTab the nsITabParent that is opening the new window. The
+                         nsITabParent is a remote tab belonging to aParent. Can
+                         be nullptr if this window is not being opened from a tab.
       @param aArgs Window argument
       @return the new window
 
       @note This method may examine the JS context stack for purposes of
             determining the security context to use for the search for a given
             window named aName.
       @note This method should try to set the default charset for the new
             window to the default charset of the document in the calling window
             (which is determined based on the JS stack and the value of
             aParent).  This is not guaranteed, however.
   */
   nsIDOMWindow openWindow2(in nsIDOMWindow aParent, in string aUrl,
                            in string aName, in string aFeatures,
                            in boolean aCalledFromScript, in boolean aDialog,
-                           in boolean aNavigate, in nsISupports aArgs);
+                           in boolean aNavigate, in nsITabParent aOpeningTab,
+                           in nsISupports aArgs);
 
   /**
    * Find a named docshell tree item amongst all windows registered
    * with the window watcher.  This may be a subframe in some window,
    * for example.
    *
    * @param aName the name of the window.  Must not be null.
    * @param aRequestor the tree item immediately making the request.
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -337,17 +337,17 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow
   uint32_t argc = 0;
   if (argv) {
     argv->GetLength(&argc);
   }
   bool dialog = (argc != 0);
 
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
                             /* calledFromJS = */ false, dialog,
-                            /* navigate = */ true, argv, _retval);
+                            /* navigate = */ true, nullptr, argv, _retval);
 }
 
 struct SizeSpec {
   SizeSpec() :
     mLeftSpecified(false),
     mTopSpecified(false),
     mOuterWidthSpecified(false),
     mOuterHeightSpecified(false),
@@ -389,16 +389,17 @@ struct SizeSpec {
 NS_IMETHODIMP
 nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent,
                               const char *aUrl,
                               const char *aName,
                               const char *aFeatures,
                               bool aCalledFromScript,
                               bool aDialog,
                               bool aNavigate,
+                              nsITabParent *aOpeningTab,
                               nsISupports *aArguments,
                               nsIDOMWindow **_retval)
 {
   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
 
   uint32_t argc = 0;
   if (argv) {
     argv->GetLength(&argc);
@@ -409,27 +410,28 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindo
   // called from script.  Fixing this is bug 779939.
   bool dialog = aDialog;
   if (!aCalledFromScript) {
     dialog = argc > 0;
   }
 
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
                             aCalledFromScript, dialog,
-                            aNavigate, argv, _retval);
+                            aNavigate, aOpeningTab, argv, _retval);
 }
 
 nsresult
 nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
                                     const char *aUrl,
                                     const char *aName,
                                     const char *aFeatures,
                                     bool aCalledFromJS,
                                     bool aDialog,
                                     bool aNavigate,
+                                    nsITabParent *aOpeningTab,
                                     nsIArray *argv,
                                     nsIDOMWindow **_retval)
 {
   nsresult                        rv = NS_OK;
   bool                            nameSpecified,
                                   featuresSpecified,
                                   isNewToplevelWindow = false,
                                   windowIsNew = false,
@@ -693,17 +695,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
         }
 
         if (popupConditions)
           contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
 
         bool cancel = false;
         rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
                                                  contextFlags, uriToLoad,
-                                                 &cancel,
+                                                 aOpeningTab, &cancel,
                                                  getter_AddRefs(newChrome));
         if (NS_SUCCEEDED(rv) && cancel) {
           newChrome = 0; // just in case
           rv = NS_ERROR_ABORT;
         }
       }
       else
         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.h
@@ -10,16 +10,17 @@
 #define NS_WINDOWWATCHER_CID \
  {0xa21bfa01, 0xf349, 0x4394, {0xa8, 0x4c, 0x8d, 0xe5, 0xcf, 0x7, 0x37, 0xd0}}
 
 #include "nsCOMPtr.h"
 #include "mozilla/Mutex.h"
 #include "nsIWindowCreator.h" // for stupid compilers
 #include "nsIWindowWatcher.h"
 #include "nsIPromptFactory.h"
+#include "nsITabParent.h"
 #include "nsPIWindowWatcher.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 
 class  nsIURI;
 class  nsIDocShellTreeItem;
 class  nsIDocShellTreeOwner;
 class nsPIDOMWindow;
@@ -72,16 +73,17 @@ protected:
   // (which means called from script) or called via OpenWindow.
   nsresult OpenWindowInternal(nsIDOMWindow *aParent,
                               const char *aUrl,
                               const char *aName,
                               const char *aFeatures,
                               bool aCalledFromJS,
                               bool aDialog,
                               bool aNavigate,
+                              nsITabParent *aOpeningTab,
                               nsIArray *argv,
                               nsIDOMWindow **_retval);
 
   static JSContext *GetJSContextFromWindow(nsIDOMWindow *aWindow);
   static nsresult   URIfromURL(const char *aURL,
                                nsIDOMWindow *aParent,
                                nsIURI **aURI);
   
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -6,41 +6,46 @@
 #include "DrawTargetCairo.h"
 
 #include "SourceSurfaceCairo.h"
 #include "PathCairo.h"
 #include "HelpersCairo.h"
 #include "ScaledFontBase.h"
 #include "BorrowedContext.h"
 #include "FilterNodeSoftware.h"
+#include "mozilla/Scoped.h"
 
 #include "cairo.h"
 #include "cairo-tee.h"
 #include <string.h>
 
 #include "Blur.h"
 #include "Logging.h"
 #include "Tools.h"
 
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
 #include "cairo-quartz.h"
 #include <ApplicationServices/ApplicationServices.h>
 #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"
 #endif
 
 #include <algorithm>
 
 namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
+
 namespace gfx {
 
 cairo_surface_t *DrawTargetCairo::mDummySurface;
 
 namespace {
 
 // An RAII class to prepare to draw a context and optional path. Saves and
 // restores the context on construction/destruction.
@@ -1052,19 +1057,116 @@ DrawTargetCairo::CreateSourceSurfaceFrom
   CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat));
 
   RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
   cairo_surface_destroy(surf);
 
   return source_surf;
 }
 
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static cairo_user_data_key_t gDestroyPixmapKey;
+
+struct DestroyPixmapClosure {
+  DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
+  ~DestroyPixmapClosure() {
+    XFreePixmap(DisplayOfScreen(mScreen), mPixmap);
+  }
+  Drawable mPixmap;
+  Screen *mScreen;
+};
+
+static void
+DestroyPixmap(void *data)
+{
+  delete static_cast<DestroyPixmapClosure*>(data);
+}
+#endif
+
 TemporaryRef<SourceSurface>
 DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
 {
+#ifdef CAIRO_HAS_XLIB_SURFACE
+  if (cairo_surface_get_type(mSurface) != CAIRO_SURFACE_TYPE_XLIB) {
+    return aSurface;
+  }
+
+  IntSize size = aSurface->GetSize();
+  if (!size.width || !size.height) {
+    return aSurface;
+  }
+
+  // Although the dimension parameters in the xCreatePixmapReq wire protocol are
+  // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
+  // either dimension cannot be represented by a 16-bit *signed* integer.
+  #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
+
+  if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+      size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+    return aSurface;
+  }
+
+  SurfaceFormat format = aSurface->GetFormat();
+  Screen *screen = cairo_xlib_surface_get_screen(mSurface);
+  Display *dpy = DisplayOfScreen(screen);
+  XRenderPictFormat* xrenderFormat = nullptr;
+  switch (format) {
+  case SurfaceFormat::B8G8R8A8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+    break;
+  case SurfaceFormat::B8G8R8X8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
+    break;
+  case SurfaceFormat::A8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
+    break;
+  default:
+    return aSurface;
+  }
+  if (!xrenderFormat) {
+    return aSurface;
+  }
+
+  Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
+                                  size.width, size.height,
+                                  xrenderFormat->depth);
+  if (!pixmap) {
+    return aSurface;
+  }
+
+  ScopedDeletePtr<DestroyPixmapClosure> closure(
+    new DestroyPixmapClosure(pixmap, screen));
+
+  ScopedCairoSurface csurf(
+    cairo_xlib_surface_create_with_xrender_format(dpy, pixmap,
+                                                  screen, xrenderFormat,
+                                                  size.width, size.height));
+  if (!csurf || cairo_surface_status(csurf)) {
+    return aSurface;
+  }
+
+  cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
+                              closure.forget(), DestroyPixmap);
+
+  RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
+  if (!dt->Init(csurf, size, &format)) {
+    return aSurface;
+  }
+
+  dt->CopySurface(aSurface,
+                  IntRect(0, 0, size.width, size.height),
+                  IntPoint(0, 0));
+  dt->Flush();
+
+  RefPtr<SourceSurface> surf =
+    new SourceSurfaceCairo(csurf, size, format);
+
+  return surf;
+#endif
+
   return aSurface;
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
   if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
     if (aSurface.mSize.width <= 0 ||
--- a/gfx/doc/MozSurface.md
+++ b/gfx/doc/MozSurface.md
@@ -9,59 +9,34 @@ We need to be able to safely and efficie
 MozSurface is a cross-process and backend-independent Surface API and not a stream API.
 
 ## Owner
 
 Nicolas Silva
 
 ## Definitions
 
-* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
-
 ## Use cases
 
 Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
 
 ## Requirement
 
 * It must be possible to efficiently share a MozSurface with a separate thread or process through IPDL
 * It must be possible to obtain read access a MozSurface on both the client and the host side at the same time.
 * The creation, update and destrution of surfaces must be safe and race-free. In particular, the ownership of the shared data must be clearly defined.
 * MozSurface must be a cross-backend/cross-platform abstraction that we will use on all of the supported platforms.
 * It must be possible to efficiently draw into a MozSurface using Moz2D.
 * While it should be possible to share MozSurfaces accross processes, it should not be limited to that. MozSurface should also be the preferred abstraction for use with surfaces that are not shared with the compositor process.
 
 ## TextureClient and TextureHost
 
-TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
-Inline documentation about TextureClient and TextureHost can be found in:
-
-* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
-* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
-
-TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
-
-A MozSurface containing data that is shared between a client process and a host process exists in the following form:
+TextureClient and TextureHost are the closest abstractions we currently have to MozSurface. The current plan is to evolve TextureClient into MozSurface. In its current state, TextureClient doesn't meet all the requirements and desisgn decisions of MozSurface yet.
 
-```
-                                 .
-            Client process       .      Host process
-                                 .
-     ________________      ______________      ______________
-    |                |    |              |    |              |
-    | TextureClient  +----+ <SharedData> +----+ TextureHost  |
-    |________________|    |______________|    |______________|
-                                 .
-                                 .
-                                 .
-    Figure 1) A Surface as seen by the client and the host processes
-```
-
-The above figure is a logical representation, not a class diagram.
-`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
+In particular, TextureClient/TextureHost are designed around cross-process sharing specifically. See the SharedMozSurface design document for more information about TextureClient and TextureHost.
 
 ## Locking semantics
 
 In order to access the shared surface data users of MozSurface must acquire and release a lock on the surface, specifying the open mode (read/write/read+write).
 
     bool Lock(OpenMode aMode);
     void Unlock();
 
@@ -104,125 +79,44 @@ While MozSurface handles memory manageme
 TODO: We need to figure this out. Right now we have a Gonk specific implementation, but no cross-platform abstraction/design.
 
 ## Ownership of the shared data
 
 MozSurface (TextureClient/TextureHost in its current form) defines ownership rules that depend on the configuration of the surface, in order to satisy efficiency and safety requirements.
 
 These rules rely on the fact that the underlying shared data is strictly owned by the MozSurface. This means that keeping direct references to the shared data is illegal and unsafe.
 
-## Deallocation protocol
-
-The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
-The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
-When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
-
-
-```
-           client side                .         host side
-                                      .
-    (A) Client: Send Remove     -.    .
-                                  \   .
-                                   \  .   ... can receive and send ...
-                                    \
-        Can receive                  `--> (B) Host: Receive Remove
-        Can't send                         |
-                                      .-- (C) Host: Send Delete
-                                     /
-                                    / .   ... can't receive nor send ...
-                                   /  .
-    (D) Client: Receive Delete <--'   .
-                                      .
-    Figure 2) MozSurface deallocation handshake
-```
-
-This handshake protocol is twofold:
-
-* It defines where and when it is possible to deallocate the shared data without races
-* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
-
-### Deallocating on the host side
-
-In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
-
-### Deallocating on the client side
-
-In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
-
-In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
-Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
-
-### Sharing state
-
-The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
-
-The sharing state of a MozSurface can be one of the following:
-
-* (1) Uninitialized (it doesn't have any shared data)
-* (2) Local (it isn't shared with the another thread/process)
-* (3) Shared (the state you would expect it to be most of the time)
-* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
-
-Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
-
-The deallocation protocol above, applies to the Shared state (3).
-In the other cases:
-
-* (1) Unitilialized: There is nothing to do.
-* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
-* (4) Invalid: There is nothing to do (deallocation has already happenned).
-
 ## Internal buffers / direct texturing
 
 Some MozSurface implementations use CPU-side shared memory to share the texture data accross processes, and require a GPU texture upload when interfacing with a TextureSource. In this case we say that the surface has an internal buffer (because it is implicitly equivalent to double buffering where the shared data is the back buffer and the GPU side texture is the front buffer). We also say that it doesn't do "direct texturing" meaning that we don't draw directly into the GPU-side texture.
 
 Examples:
 
  * Shmem MozSurface + OpenGL TextureSource: Has an internal buffer (no direct texturing)
  * Gralloc MozSurface + Gralloc TextureSource: No internal buffer (direct texturing)
 
 While direct texturing is usually the most efficient way, it is not always available depending on the platform and the required allocation size or format. Textures with internal buffers have less restrictions around locking since the host side will only need to read from the MozSurface once per update, meaning that we can often get away with single buffering where we would need double buffering with direct texturing.
 
 ## Alternative solutions
 
-### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
-
-The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
-
-Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
-
-We need a given surface to be accessible on both the client and host for the following reasons:
-
-* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
-* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
-* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
-
 ## Backends
 
 We have MozSurface implementaions (classes inheriting from TextureClient/TextureHost) for OpenGL, Software, D3D9, and D3D11 backends.
 Some implemtations can be used with any backend (ex. ShmemTextureClient/Host).
 
 ## Users of MozSurface
 
 MozSurface is the mechanism used by layers to share surfaces with the compositor, but it is not limited to layers. It should be used by anything that draws into a surface that may be shared with the compositor thread.
 
 ## Testing
 
 TODO - How can we make MozSurface more testable and what should we test?
 
 ## Future work
 
-### Rename TextureClient/TextureHost
-
-The current terminology is very confusing.
-
-### Unify TextureClient and TextureHost
-
-TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
-
 ### Using a MozSurface as a source for Drawing
 
 MozSurface should be able to expose a borrowed Moz2D SourceSurface that is valid between Lock and Unlock similarly to how it exposes a DrawTarget.
 
 ## Comparison with other APIs
 
 MozSurface is somewhat equivalent to Gralloc on Android/Gonk: it is a reference counted cross-process surface with locking semantics. While Gralloc can interface itself with OpenGL textures for compositing, MozSurface can interface itself to TextureSource objects.
 
new file mode 100644
--- /dev/null
+++ b/gfx/doc/SharedMozSurface.md
@@ -0,0 +1,147 @@
+Shared MozSurface {#mozsurface}
+==========
+
+**This document is work in progress.  Some information may be missing or incomplete.**
+
+Shared MozSurfaces represent an important use case of MozSurface, anything that is in the MozSurface design document also applies to shared MozSurfaces.
+
+## Goals
+
+We need to be able to safely and efficiently render web content into surfaces that may be shared accross processes.
+MozSurface is a cross-process and backend-independent Surface API and not a stream API.
+
+## Owner
+
+Nicolas Silva
+
+## Definitions
+
+* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
+
+## Use cases
+
+Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
+
+## Requirement
+
+Shared MozSurfaces represent an important use case of MozSurface, it has the same requirements as MozSurface.
+
+## TextureClient and TextureHost
+
+TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
+Inline documentation about TextureClient and TextureHost can be found in:
+
+* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
+* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
+
+TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
+
+A MozSurface containing data that is shared between a client process and a host process exists in the following form:
+
+```
+                                 .
+            Client process       .      Host process
+                                 .
+     ________________      ______________      ______________
+    |                |    |              |    |              |
+    | TextureClient  +----+ <SharedData> +----+ TextureHost  |
+    |________________|    |______________|    |______________|
+                                 .
+                                 .
+                                 .
+    Figure 1) A Surface as seen by the client and the host processes
+```
+
+The above figure is a logical representation, not a class diagram.
+`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
+
+## Deallocation protocol
+
+The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
+The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
+When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
+
+
+```
+           client side                .         host side
+                                      .
+    (A) Client: Send Remove     -.    .
+                                  \   .
+                                   \  .   ... can receive and send ...
+                                    \
+        Can receive                  `--> (B) Host: Receive Remove
+        Can't send                         |
+                                      .-- (C) Host: Send Delete
+                                     /
+                                    / .   ... can't receive nor send ...
+                                   /  .
+    (D) Client: Receive Delete <--'   .
+                                      .
+    Figure 2) MozSurface deallocation handshake
+```
+
+This handshake protocol is twofold:
+
+* It defines where and when it is possible to deallocate the shared data without races
+* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
+
+### Deallocating on the host side
+
+In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
+
+### Deallocating on the client side
+
+In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
+
+In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
+Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
+
+### Sharing state
+
+The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
+
+The sharing state of a MozSurface can be one of the following:
+
+* (1) Uninitialized (it doesn't have any shared data)
+* (2) Local (it isn't shared with the another thread/process)
+* (3) Shared (the state you would expect it to be most of the time)
+* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
+
+Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
+
+The deallocation protocol above, applies to the Shared state (3).
+In the other cases:
+
+* (1) Unitilialized: There is nothing to do.
+* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
+* (4) Invalid: There is nothing to do (deallocation has already happenned).
+
+## Alternative solutions
+
+### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
+
+The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
+
+Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
+
+We need a given surface to be accessible on both the client and host for the following reasons:
+
+* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
+* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
+* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
+
+## Testing
+
+TODO - How can we make shared MozSurfaces more testable and what should we test?
+
+## Future work
+
+### Rename TextureClient/TextureHost
+
+The current terminology is very confusing.
+
+### Unify TextureClient and TextureHost
+
+TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
+
+## Comparison with other APIs
--- a/gfx/layers/ipc/CompositorBench.cpp
+++ b/gfx/layers/ipc/CompositorBench.cpp
@@ -10,16 +10,20 @@
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/Effects.h"
 #include "mozilla/TimeStamp.h"
 #include "gfxColor.h"
 #include "gfxPrefs.h"
 #include <math.h>
 #include "GeckoProfiler.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include "mozilla/layers/GrallocTextureHost.h"
+#endif
+
 #define TEST_STEPS 1000
 #define DURATION_THRESHOLD 30
 #define THRESHOLD_ABORT_COUNT 5
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
@@ -41,16 +45,55 @@ public:
   virtual void Teardown(Compositor* aCompositor) {}
   virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) = 0;
 
   const char* ToString() { return mTestName; }
 private:
   const char* mTestName;
 };
 
+static void
+DrawFrameTrivialQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects) {
+  for (size_t i = 0; i < aStep * 10; i++) {
+    const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
+                                      (int)(i / aScreenRect.height),
+                                      1, 1);
+    const gfx::Rect& clipRect = aScreenRect;
+
+    float opacity = 1.f;
+
+    gfx::Matrix transform2d;
+
+    gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+    aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+  }
+}
+
+static void
+DrawFrameStressQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects)
+{
+  for (size_t i = 0; i < aStep * 10; i++) {
+    const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
+                                      aScreenRect.height * SimplePseudoRandom(i, 1),
+                                      aScreenRect.width * SimplePseudoRandom(i, 2),
+                                      aScreenRect.height * SimplePseudoRandom(i, 3));
+    const gfx::Rect& clipRect = aScreenRect;
+
+    float opacity = 1.f;
+
+    gfx::Matrix transform2d;
+    transform2d = transform2d.Rotate(SimplePseudoRandom(i, 4) * 70.f);
+
+    gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+    aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+  }
+}
+
 class EffectSolidColorBench : public BenchTest {
 public:
   EffectSolidColorBench()
     : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)")
   {}
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
     float red;
@@ -75,76 +118,58 @@ public:
 
 class EffectSolidColorTrivialBench : public BenchTest {
 public:
   EffectSolidColorTrivialBench()
     : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)")
   {}
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
-    for (size_t i = 0; i < aStep * 10; i++) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  TemporaryRef<Effect> CreateEffect(size_t i) {
       float red;
       float tmp;
       red = modf(i * 0.03f, &tmp);
       EffectChain effects;
       gfxRGBA color(red, 0.4f, 0.4f, 1.0f);
-      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r,
-                                                               color.g,
-                                                               color.b,
-                                                               color.a));
-
-      const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
-                                        (int)(i / aScreenRect.height),
-                                        1, 1);
-      const gfx::Rect& clipRect = aScreenRect;
-
-      float opacity = 1.f;
-
-      gfx::Matrix transform2d;
-
-      gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
-
-      aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
-    }
+      return new EffectSolidColor(gfx::Color(color.r,
+                                             color.g,
+                                             color.b,
+                                             color.a));
   }
 };
 
 class EffectSolidColorStressBench : public BenchTest {
 public:
   EffectSolidColorStressBench()
     : BenchTest("EffectSolidColorStressBench (10s various EffectSolidColor)")
   {}
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
-    for (size_t i = 0; i < aStep * 10; i++) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  TemporaryRef<Effect> CreateEffect(size_t i) {
       float red;
       float tmp;
       red = modf(i * 0.03f, &tmp);
       EffectChain effects;
       gfxRGBA color(red, 0.4f, 0.4f, 1.0f);
-      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r,
-                                                               color.g,
-                                                               color.b,
-                                                               color.a));
-
-      const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
-                                        aScreenRect.height * SimplePseudoRandom(i, 1),
-                                        aScreenRect.width * SimplePseudoRandom(i, 2),
-                                        aScreenRect.height * SimplePseudoRandom(i, 3));
-      const gfx::Rect& clipRect = aScreenRect;
-
-      float opacity = 0.3f;
-
-      gfx::Matrix transform2d;
-      transform2d = transform2d.Rotate(SimplePseudoRandom(i, 4) * 70.f);
-
-      gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
-
-      aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
-    }
+      return new EffectSolidColor(gfx::Color(color.r,
+                                             color.g,
+                                             color.b,
+                                             color.a));
   }
 };
 
 class UploadBench : public BenchTest {
 public:
   UploadBench()
     : BenchTest("Upload Bench (10s 256x256 upload)")
   {}
@@ -172,24 +197,204 @@ public:
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
     for (size_t i = 0; i < aStep * 10; i++) {
       mTexture->Update(mSurface);
     }
   }
 };
 
+class TrivialTexturedQuadBench : public BenchTest {
+public:
+  TrivialTexturedQuadBench()
+    : BenchTest("Trvial Textured Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  RefPtr<DataSourceSurface> mSurface;
+  RefPtr<DataTextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    int bytesPerPixel = 4;
+    size_t w = 256;
+    size_t h = 256;
+    mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+    for (size_t i = 0; i < w * h; i++) {
+      mBuf[i] = 0xFF00008F;
+    }
+
+    mSurface = Factory::CreateWrappingDataSourceSurface(
+      reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+    mTexture = aCompositor->CreateDataTextureSource();
+    mTexture->Update(mSurface);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mSurface = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+
+class StressTexturedQuadBench : public BenchTest {
+public:
+  StressTexturedQuadBench()
+    : BenchTest("Stress Textured Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  RefPtr<DataSourceSurface> mSurface;
+  RefPtr<DataTextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    int bytesPerPixel = 4;
+    size_t w = 256;
+    size_t h = 256;
+    mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+    for (size_t i = 0; i < w * h; i++) {
+      mBuf[i] = 0xFF00008F;
+    }
+
+    mSurface = Factory::CreateWrappingDataSourceSurface(
+      reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+    mTexture = aCompositor->CreateDataTextureSource();
+    mTexture->Update(mSurface);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mSurface = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  virtual TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+
+#ifdef MOZ_WIDGET_GONK
+class TrivialGrallocQuadBench : public BenchTest {
+public:
+  TrivialGrallocQuadBench()
+    : BenchTest("Travial Gralloc Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  android::sp<android::GraphicBuffer> mGralloc;
+  RefPtr<TextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    mBuf = nullptr;
+    int w = 256;
+    int h = 256;
+    int32_t format = android::PIXEL_FORMAT_RGBA_8888;;
+    mGralloc = new android::GraphicBuffer(w, h,
+                                 format,
+                                 android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                 android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                                 android::GraphicBuffer::USAGE_HW_TEXTURE);
+    mTexture = new mozilla::layers::GrallocTextureSourceOGL((CompositorOGL*)aCompositor, mGralloc.get(), SurfaceFormat::B8G8R8A8);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mGralloc = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  virtual TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+
+class StressGrallocQuadBench : public BenchTest {
+public:
+  StressGrallocQuadBench()
+    : BenchTest("Stress Gralloc Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  android::sp<android::GraphicBuffer> mGralloc;
+  RefPtr<TextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    mBuf = nullptr;
+    int w = 256;
+    int h = 256;
+    int32_t format = android::PIXEL_FORMAT_RGBA_8888;;
+    mGralloc = new android::GraphicBuffer(w, h,
+                                 format,
+                                 android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                 android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                                 android::GraphicBuffer::USAGE_HW_TEXTURE);
+    mTexture = new mozilla::layers::GrallocTextureSourceOGL((CompositorOGL*)aCompositor, mGralloc.get(), SurfaceFormat::B8G8R8A8);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mGralloc = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  virtual TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+#endif
+
 static void RunCompositorBench(Compositor* aCompositor, const gfx::Rect& aScreenRect)
 {
   std::vector<BenchTest*> tests;
 
   tests.push_back(new EffectSolidColorBench());
+  tests.push_back(new UploadBench());
   tests.push_back(new EffectSolidColorTrivialBench());
   tests.push_back(new EffectSolidColorStressBench());
-  tests.push_back(new UploadBench());
+  tests.push_back(new TrivialTexturedQuadBench());
+  tests.push_back(new StressTexturedQuadBench());
+#ifdef MOZ_WIDGET_GONK
+  tests.push_back(new TrivialGrallocQuadBench());
+  tests.push_back(new StressGrallocQuadBench());
+#endif
 
   for (size_t i = 0; i < tests.size(); i++) {
     BenchTest* test = tests[i];
     std::vector<TimeDuration> results;
     int testsOverThreshold = 0;
     PROFILER_MARKER(test->ToString());
     for (size_t j = 0; j < TEST_STEPS; j++) {
       test->Setup(aCompositor, j);
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -192,17 +192,17 @@ OptimalFillOperator()
 // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
 // the subimage of pixels we're allowed to sample.
 static already_AddRefed<gfxDrawable>
 CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
                                  gfxContext* aContext,
                                  const gfxMatrix& aUserSpaceToImageSpace,
                                  const gfxRect& aSourceRect,
                                  const gfxRect& aSubimage,
-                                 const gfxImageFormat aFormat)
+                                 const SurfaceFormat aFormat)
 {
     PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
       js::ProfileEntry::Category::GRAPHICS);
 
     gfxRect userSpaceClipExtents = aContext->GetClipExtents();
     // This isn't optimal --- if aContext has a rotation then GetClipExtents
     // will have to do a bounding-box computation, and TransformBounds might
     // too, so we could get a better result if we computed image space clip
@@ -228,19 +228,19 @@ CreateSamplingRestrictedDrawable(gfxDraw
     nsRefPtr<gfxDrawable> drawable;
     gfxIntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
 
     nsRefPtr<gfxImageSurface> image = aDrawable->GetAsImageSurface();
     if (image && gfxRect(0, 0, image->GetSize().width, image->GetSize().height).Contains(needed)) {
       nsRefPtr<gfxASurface> temp = image->GetSubimage(needed);
       drawable = new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.TopLeft()));
     } else {
-      mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
+      RefPtr<DrawTarget> target =
         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
-                                                                     ImageFormatToSurfaceFormat(aFormat));
+                                                                     aFormat);
       if (!target) {
         return nullptr;
       }
 
       nsRefPtr<gfxContext> tmpCtx = new gfxContext(target);
       tmpCtx->SetOperator(OptimalFillOperator());
       aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true,
                       GraphicsFilter::FILTER_FAST, gfxMatrix().Translate(needed.TopLeft()));
@@ -398,17 +398,17 @@ static GraphicsFilter ReduceResamplingFi
 /* static */ void
 gfxUtils::DrawPixelSnapped(gfxContext*      aContext,
                            gfxDrawable*     aDrawable,
                            const gfxMatrix& aUserSpaceToImageSpace,
                            const gfxRect&   aSubimage,
                            const gfxRect&   aSourceRect,
                            const gfxRect&   aImageRect,
                            const gfxRect&   aFill,
-                           const gfxImageFormat aFormat,
+                           const SurfaceFormat aFormat,
                            GraphicsFilter aFilter,
                            uint32_t         aImageFlags)
 {
     PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
       js::ProfileEntry::Category::GRAPHICS);
 
     bool doTile = !aImageRect.Contains(aSourceRect) &&
                   !(aImageFlags & imgIContainer::FLAG_CLAMP);
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -60,17 +60,17 @@ public:
      */
     static void DrawPixelSnapped(gfxContext*      aContext,
                                  gfxDrawable*     aDrawable,
                                  const gfxMatrix& aUserSpaceToImageSpace,
                                  const gfxRect&   aSubimage,
                                  const gfxRect&   aSourceRect,
                                  const gfxRect&   aImageRect,
                                  const gfxRect&   aFill,
-                                 const gfxImageFormat aFormat,
+                                 const mozilla::gfx::SurfaceFormat aFormat,
                                  GraphicsFilter aFilter,
                                  uint32_t         aImageFlags = imgIContainer::FLAG_NONE);
 
     /**
      * Clip aContext to the region aRegion.
      */
     static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
 
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -154,21 +154,21 @@ void nsGIFDecoder2::BeginGIF()
   mGIFOpen = true;
 
   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
 }
 
 //******************************************************************************
 void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
 {
-  gfxImageFormat format;
+  gfx::SurfaceFormat format;
   if (mGIFStruct.is_transparent)
-    format = gfxImageFormat::ARGB32;
+    format = gfx::SurfaceFormat::B8G8R8A8;
   else
-    format = gfxImageFormat::RGB24;
+    format = gfx::SurfaceFormat::B8G8R8X8;
 
   MOZ_ASSERT(HasSize());
 
   // Use correct format, RGB for first frame, PAL for following frames
   // and include transparency to allow for optimization of opaque images
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette
     NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
@@ -183,17 +183,17 @@ void nsGIFDecoder2::BeginImageFrame(uint
                                                                 mGIFStruct.width,
                                                                 mGIFStruct.height))) {
     // Regardless of depth of input, image is decoded into 24bit RGB
     NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
                  mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
                  format);
   } else {
     // Our preallocated frame matches up, with the possible exception of alpha.
-    if (format == gfxImageFormat::RGB24) {
+    if (format == gfx::SurfaceFormat::B8G8R8X8) {
       GetCurrentFrame()->SetHasNoAlpha();
     }
   }
 
   mCurrentFrame = mGIFStruct.images_decoded;
 }
 
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -135,27 +135,27 @@ nsPNGDecoder::~nsPNGDecoder()
     if (mTransform)
       qcms_transform_release(mTransform);
   }
 }
 
 // CreateFrame() is used for both simple and animated images
 void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                                int32_t width, int32_t height,
-                               gfxImageFormat format)
+                               gfx::SurfaceFormat format)
 {
   // Our first full frame is automatically created by the image decoding
   // infrastructure. Just use it as long as it matches up.
   MOZ_ASSERT(HasSize());
   if (mNumFrames != 0 ||
       !GetCurrentFrame()->GetRect().IsEqualEdges(nsIntRect(x_offset, y_offset, width, height))) {
     NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
   } else if (mNumFrames == 0) {
     // Our preallocated frame matches up, with the possible exception of alpha.
-    if (format == gfxImageFormat::RGB24) {
+    if (format == gfx::SurfaceFormat::B8G8R8X8) {
       GetCurrentFrame()->SetHasNoAlpha();
     }
   }
 
   mFrameRect.x = x_offset;
   mFrameRect.y = y_offset;
   mFrameRect.width = width;
   mFrameRect.height = height;
@@ -624,19 +624,19 @@ nsPNGDecoder::info_callback(png_structp 
       }
     } else {
       alpha_bits = 8;
     }
   }
 #endif
 
   if (channels == 1 || channels == 3)
-    decoder->format = gfxImageFormat::RGB24;
+    decoder->format = gfx::SurfaceFormat::B8G8R8X8;
   else if (channels == 2 || channels == 4)
-    decoder->format = gfxImageFormat::ARGB32;
+    decoder->format = gfx::SurfaceFormat::B8G8R8A8;
 
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
     png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
                                  nullptr);
 
   if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
     decoder->mFrameIsHidden = true;
@@ -740,17 +740,17 @@ nsPNGDecoder::row_callback(png_structp p
         }
         line = decoder->mCMSLine;
       } else {
         qcms_transform_data(decoder->mTransform, line, line, iwidth);
        }
      }
 
     switch (decoder->format) {
-      case gfxImageFormat::RGB24:
+      case gfx::SurfaceFormat::B8G8R8X8:
       {
         // counter for while() loops below
         uint32_t idx = iwidth;
 
         // copy as bytes until source pointer is 32-bit-aligned
         for (; (NS_PTR_TO_UINT32(line) & 0x3) && idx; --idx) {
           *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]);
           line += 3;
@@ -767,17 +767,17 @@ nsPNGDecoder::row_callback(png_structp p
         // copy remaining pixel(s)
         while (idx--) {
           // 32-bit read of final pixel will exceed buffer, so read bytes
           *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]);
           line += 3;
         }
       }
       break;
-      case gfxImageFormat::ARGB32:
+      case gfx::SurfaceFormat::B8G8R8A8:
       {
         if (!decoder->mDisablePremultipliedAlpha) {
           for (uint32_t x=width; x>0; --x) {
             *cptr32++ = gfxPackedPixel(line[3], line[0], line[1], line[2]);
             if (line[3] != 0xff)
               rowHasNoAlpha = false;
             line += 4;
           }
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -28,17 +28,17 @@ public:
   virtual ~nsPNGDecoder();
 
   virtual void InitInternal();
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy);
   virtual Telemetry::ID SpeedHistogram();
 
   void CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                    int32_t width, int32_t height,
-                   gfxImageFormat format);
+                   gfx::SurfaceFormat format);
   void EndImageFrame();
 
   // Check if PNG is valid ICO (32bpp RGBA)
   // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
   bool IsValidICO() const
   {
     // If there are errors in the call to png_get_IHDR, the error_callback in
     // nsPNGDecoder.cpp is called.  In this error callback we do a longjmp, so
@@ -71,17 +71,17 @@ public:
   png_structp mPNG;
   png_infop mInfo;
   nsIntRect mFrameRect;
   uint8_t *mCMSLine;
   uint8_t *interlacebuf;
   qcms_profile *mInProfile;
   qcms_transform *mTransform;
 
-  gfxImageFormat format;
+  gfx::SurfaceFormat format;
 
   // For size decodes
   uint8_t mSizeBytes[8]; // Space for width and height, both 4 bytes
   uint32_t mHeaderBytesRead;
 
   // whether CMS or premultiplied alpha are forced off
   uint32_t mCMSMode;
 
--- a/image/src/ClippedImage.cpp
+++ b/image/src/ClippedImage.cpp
@@ -250,17 +250,17 @@ ClippedImage::GetFrameInternal(const nsI
       new DrawSingleTileCallback(this, mClip, aViewportSize, aSVGContext, aWhichFrame, aFlags);
     nsRefPtr<gfxDrawable> drawable =
       new gfxCallbackDrawable(drawTileCallback, mClip.Size());
 
     // Actually draw. The callback will end up invoking DrawSingleTile.
     gfxRect imageRect(0, 0, mClip.width, mClip.height);
     gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
                                imageRect, imageRect, imageRect, imageRect,
-                               gfxImageFormat::ARGB32,
+                               SurfaceFormat::B8G8R8A8,
                                GraphicsFilter::FILTER_FAST);
 
     // Cache the resulting surface.
     mCachedSurface = new ClippedImageCachedSurface(target->Snapshot(),
                                                    aViewportSize,
                                                    aSVGContext,
                                                    frameToDraw,
                                                    aFlags);
@@ -335,17 +335,17 @@ ClippedImage::Draw(gfxContext* aContext,
     nsRefPtr<gfxSurfaceDrawable> drawable =
       new gfxSurfaceDrawable(surface, gfxIntSize(mClip.width, mClip.height));
 
     // Draw.
     gfxRect imageRect(0, 0, mClip.width, mClip.height);
     gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
     gfxUtils::DrawPixelSnapped(aContext, drawable, aUserSpaceToImageSpace,
                                subimage, sourceRect, imageRect, aFill,
-                               gfxImageFormat::ARGB32, aFilter);
+                               SurfaceFormat::B8G8R8A8, aFilter);
 
     return NS_OK;
   }
 
   // Determine the appropriate subimage for the inner image.
   nsIntRect innerSubimage(aSubimage);
   innerSubimage.MoveBy(mClip.x, mClip.y);
   innerSubimage.Intersect(mClip);
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -198,18 +198,16 @@ Decoder::FinishSharedDecoder()
 }
 
 nsresult
 Decoder::AllocateFrame()
 {
   MOZ_ASSERT(mNeedsNewFrame);
   MOZ_ASSERT(NS_IsMainThread());
 
-  MarkFrameDirty();
-
   nsresult rv;
   imgFrame* frame = nullptr;
   if (mNewFrameData.mPaletteDepth) {
     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
                             mNewFrameData.mHeight, mNewFrameData.mFormat,
                             mNewFrameData.mPaletteDepth,
                             &mImageData, &mImageDataLength,
@@ -416,33 +414,23 @@ Decoder::PostDecoderError(nsresult aFail
   // XXXbholley - we should report the image URI here, but imgContainer
   // needs to know its URI first
   NS_WARNING("Image decoding error - This is probably a bug!");
 }
 
 void
 Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
                       uint32_t width, uint32_t height,
-                      gfxImageFormat format,
+                      gfx::SurfaceFormat format,
                       uint8_t palette_depth /* = 0 */)
 {
   // Decoders should never call NeedNewFrame without yielding back to Write().
   MOZ_ASSERT(!mNeedsNewFrame);
 
   // We don't want images going back in time or skipping frames.
   MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
 
   mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth);
   mNeedsNewFrame = true;
 }
 
-void
-Decoder::MarkFrameDirty()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mCurrentFrame) {
-    mCurrentFrame->ApplyDirtToSurfaces();
-  }
-}
-
 } // namespace image
 } // namespace mozilla
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -146,29 +146,25 @@ public:
   // is created as an ARGB frame with no offset and with size width * height.
   // If decoders need something different, they must ask for it.
   // This is called by decoders when they need a new frame. These decoders
   // must then save the data they have been sent but not yet processed and
   // return from WriteInternal. When the new frame is created, WriteInternal
   // will be called again with nullptr and 0 as arguments.
   void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset,
                     uint32_t width, uint32_t height,
-                    gfxImageFormat format,
+                    gfx::SurfaceFormat format,
                     uint8_t palette_depth = 0);
 
   virtual bool NeedsNewFrame() const { return mNeedsNewFrame; }
 
   // Try to allocate a frame as described in mNewFrameData and return the
   // status code from that attempt. Clears mNewFrameData.
   virtual nsresult AllocateFrame();
 
-  // Called when a chunk of decoding has been done and the frame needs to be
-  // marked as dirty. Must be called only on the main thread.
-  void MarkFrameDirty();
-
   imgFrame* GetCurrentFrame() const { return mCurrentFrame; }
 
 protected:
 
   /*
    * Internal hooks. Decoder implementations may override these and
    * only these methods.
    */
@@ -245,31 +241,31 @@ private:
 
   struct NewFrameData
   {
     NewFrameData()
     {}
 
     NewFrameData(uint32_t num, uint32_t offsetx, uint32_t offsety,
                  uint32_t width, uint32_t height,
-                 gfxImageFormat format, uint8_t paletteDepth)
+                 gfx::SurfaceFormat format, uint8_t paletteDepth)
       : mFrameNum(num)
       , mOffsetX(offsetx)
       , mOffsetY(offsety)
       , mWidth(width)
       , mHeight(height)
       , mFormat(format)
       , mPaletteDepth(paletteDepth)
     {}
     uint32_t mFrameNum;
     uint32_t mOffsetX;
     uint32_t mOffsetY;
     uint32_t mWidth;
     uint32_t mHeight;
-    gfxImageFormat mFormat;
+    gfx::SurfaceFormat mFormat;
     uint8_t mPaletteDepth;
   };
   NewFrameData mNewFrameData;
   bool mNeedsNewFrame;
   bool mInitialized;
   bool mSizeDecode;
   bool mInFrame;
   bool mIsAnimated;
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -256,17 +256,17 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   }
 
   bool needToBlankComposite = false;
 
   // Create the Compositing Frame
   if (!mAnim->compositingFrame) {
     mAnim->compositingFrame.SetFrame(new imgFrame());
     nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
-                                                gfxImageFormat::ARGB32);
+                                                gfx::SurfaceFormat::B8G8R8A8);
     if (NS_FAILED(rv)) {
       mAnim->compositingFrame.SetFrame(nullptr);
       return false;
     }
     mAnim->compositingFrame.LockAndGetData();
     needToBlankComposite = true;
   } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
 
@@ -386,17 +386,17 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
       (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
     // We are storing the whole image.
     // It would be better if we just stored the area that nextFrame is going to
     // overwrite.
     if (!mAnim->compositingPrevFrame) {
       mAnim->compositingPrevFrame.SetFrame(new imgFrame());
       nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
-                                                      gfxImageFormat::ARGB32);
+                                                      gfx::SurfaceFormat::B8G8R8A8);
       if (NS_FAILED(rv)) {
         mAnim->compositingPrevFrame.SetFrame(nullptr);
         return false;
       }
 
       mAnim->compositingPrevFrame.LockAndGetData();
     }
 
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -95,23 +95,20 @@ OrientedImage::GetFrame(uint32_t aWhichF
   NS_ENSURE_SUCCESS(rv, nullptr);
   if (mOrientation.SwapsWidthAndHeight()) {
     swap(width, height);
   }
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   // Determine an appropriate format for the surface.
   gfx::SurfaceFormat surfaceFormat;
-  gfxImageFormat imageFormat;
   if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
-    imageFormat = gfxImageFormat::ARGB32;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
-    imageFormat = gfxImageFormat::ARGB32;
   }
 
   // Create a surface to draw into.
   mozilla::RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
   if (!target) {
     NS_ERROR("Could not create a DrawTarget");
@@ -126,17 +123,17 @@ OrientedImage::GetFrame(uint32_t aWhichF
   nsRefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
 
   // Draw.
   nsRefPtr<gfxContext> ctx = new gfxContext(target);
   gfxRect imageRect(0, 0, width, height);
   gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
                              imageRect, imageRect, imageRect, imageRect,
-                             imageFormat, GraphicsFilter::FILTER_FAST);
+                             surfaceFormat, GraphicsFilter::FILTER_FAST);
   
   return target->Snapshot();
 }
 
 NS_IMETHODIMP
 OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
 {
   // XXX(seth): We currently don't have a way of orienting the result of
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -207,43 +207,37 @@ public:
     nsRefPtr<RasterImage> image = weakImage.get();
     if (!image) {
       return false;
     }
 
     bool success = false;
     if (!dstLocked) {
       bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData());
+      srcSurface = srcFrame->GetSurface();
+
       dstLocked = NS_SUCCEEDED(dstFrame->LockImageData());
-
-      nsRefPtr<gfxASurface> dstASurf;
-      nsRefPtr<gfxASurface> srcASurf;
-      success = srcLocked && NS_SUCCEEDED(srcFrame->GetSurface(getter_AddRefs(srcASurf)));
-      success = success && dstLocked && NS_SUCCEEDED(dstFrame->GetSurface(getter_AddRefs(dstASurf)));
-
-      success = success && srcLocked && dstLocked && srcASurf && dstASurf;
+      dstSurface = dstFrame->GetSurface();
+
+      success = srcLocked && dstLocked && srcSurface && dstSurface;
 
       if (success) {
-        srcSurface = srcASurf->GetAsImageSurface();
-        dstSurface = dstASurf->GetAsImageSurface();
-        srcData = srcSurface->Data();
-        dstData = dstSurface->Data();
-        srcStride = srcSurface->Stride();
-        dstStride = dstSurface->Stride();
-        srcFormat = mozilla::gfx::ImageFormatToSurfaceFormat(srcFrame->GetFormat());
+        srcData = srcFrame->GetImageData();
+        dstData = dstFrame->GetImageData();
+        srcStride = srcFrame->GetImageBytesPerRow();
+        dstStride = dstFrame->GetImageBytesPerRow();
+        srcFormat = srcFrame->GetFormat();
       }
 
-      // We have references to the Thebes surfaces, so we don't need to leave
+      // We have references to the surfaces, so we don't need to leave
       // the source frame (that we don't own) locked. We'll unlock the
       // destination frame in ReleaseSurfaces(), below.
       if (srcLocked) {
         success = NS_SUCCEEDED(srcFrame->UnlockImageData()) && success;
       }
-
-      success = success && srcSurface && dstSurface;
     }
 
     return success;
   }
 
   // This can only be called on the main thread.
   bool ReleaseSurfaces()
   {
@@ -267,28 +261,28 @@ public:
       dstSurface = nullptr;
     }
     return success;
   }
 
   // These values may only be touched on the main thread.
   WeakPtr<RasterImage> weakImage;
   nsAutoPtr<imgFrame> dstFrame;
-  nsRefPtr<gfxImageSurface> srcSurface;
-  nsRefPtr<gfxImageSurface> dstSurface;
+  RefPtr<SourceSurface> srcSurface;
+  RefPtr<SourceSurface> dstSurface;
 
   // Below are the values that may be touched on the scaling thread.
   gfxSize scale;
   uint8_t* srcData;
   uint8_t* dstData;
   nsIntRect srcRect;
   gfxIntSize dstSize;
   uint32_t srcStride;
   uint32_t dstStride;
-  mozilla::gfx::SurfaceFormat srcFormat;
+  SurfaceFormat srcFormat;
   bool dstLocked;
   bool done;
   // This boolean is accessed from both threads simultaneously without locking.
   // That's safe because stopping a ScaleRequest is strictly an optimization;
   // if we're not cache-coherent, at worst we'll do extra work.
   bool stopped;
 };
 
@@ -330,17 +324,17 @@ public:
   ScaleRunner(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
   {
     nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aScale, aSrcFrame));
 
     // Destination is unconditionally ARGB32 because that's what the scaler
     // outputs.
     request->dstFrame = new imgFrame();
     nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height,
-                                          gfxImageFormat::ARGB32);
+                                          SurfaceFormat::B8G8R8A8);
 
     if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) {
       return;
     }
 
     aImage->ScalingStart(request);
 
     mScaleRequest = request;
@@ -696,20 +690,16 @@ RasterImage::GetDrawableImgFrame(uint32_
     frame = GetImgFrame(framenum);
   }
 
   // We will return a paletted frame if it's not marked as compositing failed
   // so we can catch crashes for reasons we haven't investigated.
   if (frame && frame->GetCompositingFailed())
     return nullptr;
 
-  if (frame) {
-    frame->ApplyDirtToSurfaces();
-  }
-
   return frame;
 }
 
 uint32_t
 RasterImage::GetCurrentImgFrameIndex() const
 {
   if (mAnim)
     return mAnim->GetCurrentAnimationFrameIndex();
@@ -826,73 +816,85 @@ RasterImage::GetFirstFrameDelay()
 
   bool animated = false;
   if (NS_FAILED(GetAnimated(&animated)) || !animated)
     return -1;
 
   return mFrameBlender.GetTimeoutForFrame(0);
 }
 
-nsresult
+TemporaryRef<SourceSurface>
 RasterImage::CopyFrame(uint32_t aWhichFrame,
-                       uint32_t aFlags,
-                       gfxImageSurface **_retval)
+                       uint32_t aFlags)
 {
   if (aWhichFrame > FRAME_MAX_VALUE)
-    return NS_ERROR_INVALID_ARG;
+    return nullptr;
 
   if (mError)
-    return NS_ERROR_FAILURE;
+    return nullptr;
 
   // Disallowed in the API
   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
-    return NS_ERROR_FAILURE;
+    return nullptr;
 
   nsresult rv;
 
   if (!ApplyDecodeFlags(aFlags, aWhichFrame))
-    return NS_ERROR_NOT_AVAILABLE;
+    return nullptr;
 
   // If requested, synchronously flush any data we have lying around to the decoder
   if (aFlags & FLAG_SYNC_DECODE) {
     rv = SyncDecode();
-    CONTAINER_ENSURE_SUCCESS(rv);
+    CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
   }
 
-  NS_ENSURE_ARG_POINTER(_retval);
-
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE
   uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
                         0 : GetCurrentImgFrameIndex();
   imgFrame *frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
-    *_retval = nullptr;
-    return NS_ERROR_FAILURE;
+    return nullptr;
   }
 
-  nsRefPtr<gfxPattern> pattern;
-  frame->GetPattern(getter_AddRefs(pattern));
-  nsIntRect intframerect = frame->GetRect();
-  gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
-
   // Create a 32-bit image surface of our size, but draw using the frame's
   // rect, implicitly padding the frame out to the image's size.
-  nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
-                                                             gfxImageFormat::ARGB32);
-  gfxContext ctx(imgsurface);
-  ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
-  ctx.Rectangle(framerect);
-  ctx.Translate(framerect.TopLeft());
-  ctx.SetPattern(pattern);
-  ctx.Fill();
-
-  imgsurface.forget(_retval);
-  return NS_OK;
+
+  IntSize size(mSize.width, mSize.height);
+  RefPtr<DataSourceSurface> surf =
+    Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+
+  DataSourceSurface::MappedSurface mapping;
+  DebugOnly<bool> success =
+    surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+  NS_ASSERTION(success, "Failed to map surface");
+  RefPtr<DrawTarget> target =
+    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                     mapping.mData,
+                                     size,
+                                     mapping.mStride,
+                                     SurfaceFormat::B8G8R8A8);
+
+  nsIntRect intframerect = frame->GetRect();
+  Rect rect(intframerect.x, intframerect.y,
+            intframerect.width, intframerect.height);
+  if (frame->IsSinglePixel()) {
+    target->FillRect(rect, ColorPattern(frame->SinglePixelColor()),
+                     DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+  } else {
+    RefPtr<SourceSurface> srcsurf = frame->GetSurface();
+    Rect srcrect(0, 0, intframerect.width, intframerect.height);
+    target->DrawSurface(srcsurf, srcrect, rect);
+  }
+
+  target->Flush();
+  surf->Unmap();
+
+  return surf;
 }
 
 //******************************************************************************
 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
  *                                   in uint32_t aFlags); */
 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 RasterImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
@@ -923,25 +925,25 @@ RasterImage::GetFrame(uint32_t aWhichFra
   // FLAG_SYNC_DECODE
   uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
                           0 : GetCurrentImgFrameIndex();
   imgFrame *frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return nullptr;
   }
 
-  nsRefPtr<gfxASurface> framesurf;
+  RefPtr<SourceSurface> framesurf;
 
   // If this frame covers the entire image, we can just reuse its existing
   // surface.
   nsIntRect framerect = frame->GetRect();
   if (framerect.x == 0 && framerect.y == 0 &&
       framerect.width == mSize.width &&
       framerect.height == mSize.height) {
-    frame->GetSurface(getter_AddRefs(framesurf));
+    framesurf = frame->GetSurface();
     if (!framesurf && !frame->IsSinglePixel()) {
       // No reason to be optimized away here - the OS threw out the data
       if (!(aFlags & FLAG_SYNC_DECODE))
         return nullptr;
 
       // Unconditionally call ForceDiscard() here because GetSurface can only
       // return null when we can forcibly discard and redecode. There are two
       // other cases where GetSurface() can return null - when it is a single
@@ -952,37 +954,20 @@ RasterImage::GetFrame(uint32_t aWhichFra
       ForceDiscard();
       return GetFrame(aWhichFrame, aFlags);
     }
   }
 
   // The image doesn't have a surface because it's been optimized away. Create
   // one.
   if (!framesurf) {
-    nsRefPtr<gfxImageSurface> imgsurf;
-    CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
-    framesurf = imgsurf;
+    framesurf = CopyFrame(aWhichFrame, aFlags);
   }
 
-  RefPtr<SourceSurface> result;
-
-  // As far as Moz2D is concerned, SourceSurface contains premultiplied alpha.
-  // If we're abusing it to contain non-premultiplied alpha then we want to
-  // avoid having Moz2D do any conversions on it (like copy to another
-  // surface). Hence why we try to wrap framesurf's data here for
-  // FLAG_DECODE_NO_PREMULTIPLY_ALPHA.
-  if ((aFlags & FLAG_WANT_DATA_SURFACE) != 0 ||
-      (aFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA) != 0) {
-    result = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(framesurf);
-  }
-  if (!result) {
-    result = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
-                                                                    framesurf);
-  }
-  return result.forget();
+  return framesurf;
 }
 
 already_AddRefed<layers::Image>
 RasterImage::GetCurrentImage()
 {
   if (!mDecoded) {
     // We can't call StartDecoding because that can synchronously notify
     // which can cause DOM modification
@@ -1177,17 +1162,17 @@ RasterImage::InternalAddFrameHelper(uint
 
   return NS_OK;
 }
 
 nsresult
 RasterImage::InternalAddFrame(uint32_t framenum,
                               int32_t aX, int32_t aY,
                               int32_t aWidth, int32_t aHeight,
-                              gfxImageFormat aFormat,
+                              SurfaceFormat aFormat,
                               uint8_t aPaletteDepth,
                               uint8_t **imageData,
                               uint32_t *imageLength,
                               uint32_t **paletteData,
                               uint32_t *paletteLength,
                               imgFrame** aRetFrame)
 {
   // We assume that we're in the middle of decoding because we unlock the
@@ -1314,17 +1299,17 @@ RasterImage::SetSize(int32_t aWidth, int
   mFrameBlender.SetSize(mSize);
 
   return NS_OK;
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
-                         gfxImageFormat aFormat,
+                         SurfaceFormat aFormat,
                          uint8_t aPaletteDepth,
                          uint8_t **imageData, uint32_t *imageLength,
                          uint32_t **paletteData, uint32_t *paletteLength,
                          imgFrame** aRetFrame)
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
@@ -1389,17 +1374,17 @@ RasterImage::EnsureFrame(uint32_t aFrame
   return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData,
                                 imageLength, paletteData, paletteLength,
                                 aRetFrame);
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
-                         gfxImageFormat aFormat,
+                         SurfaceFormat aFormat,
                          uint8_t** imageData, uint32_t* imageLength,
                          imgFrame** aFrame)
 {
   return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
                      /* aPaletteDepth = */ 0, imageData, imageLength,
                      /* aPaletteData = */ nullptr,
                      /* aPaletteLength = */ nullptr,
                      aFrame);
@@ -2107,17 +2092,17 @@ RasterImage::InitDecoder(bool aDoSizeDec
   mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver());
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
   if (!aDoSizeDecode) {
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
     mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
-                           gfxImageFormat::ARGB32);
+                           SurfaceFormat::B8G8R8A8);
     mDecoder->AllocateFrame();
   }
   mDecoder->Init();
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
   if (!aDoSizeDecode) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
@@ -2566,17 +2551,16 @@ RasterImage::ScalingDone(ScaleRequest* r
   MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
   MOZ_ASSERT(request);
 
   if (status == SCALE_DONE) {
     MOZ_ASSERT(request->done);
 
     imgFrame *scaledFrame = request->dstFrame.forget();
     scaledFrame->ImageUpdated(scaledFrame->GetRect());
-    scaledFrame->ApplyDirtToSurfaces();
 
     if (mStatusTracker) {
       mStatusTracker->FrameChanged(&request->srcRect);
     }
 
     mScaleResult.status = SCALE_DONE;
     mScaleResult.frame = scaledFrame;
     mScaleResult.scale = request->scale;
@@ -2604,31 +2588,31 @@ RasterImage::DrawWithPreDownscaleIfNeede
 {
   imgFrame *frame = aFrame;
   nsIntRect framerect = frame->GetRect();
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
   gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
   nsIntRect subimage = aSubimage;
-  nsRefPtr<gfxASurface> surf;
+  RefPtr<SourceSurface> surf;
 
   if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
     // If scale factor is still the same that we scaled for and
     // ScaleWorker isn't still working, then we can use pre-downscaled frame.
     // If scale factor has changed, order new request.
     // FIXME: Current implementation doesn't support pre-downscale
     // mechanism for multiple sizes from same src, since we cache
     // pre-downscaled frame only for the latest requested scale.
     // The solution is to cache more than one scaled image frame
     // for each RasterImage.
     bool needScaleReq;
     if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
       // Grab and hold the surface to make sure the OS didn't destroy it
-      mScaleResult.frame->GetSurface(getter_AddRefs(surf));
+      surf = mScaleResult.frame->GetSurface();
       needScaleReq = !surf;
       if (surf) {
         frame = mScaleResult.frame;
         userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width,
                                                          scale.height));
 
         // Since we're switching to a scaled image, we need to transform the
         // area of the subimage to draw accordingly, since imgFrame::Draw()
@@ -3093,18 +3077,16 @@ RasterImage::FinishedSomeDecoding(eShutd
   // destroy it by destroying the decoder.
   nsRefPtr<RasterImage> image(this);
 
   bool done = false;
   bool wasSize = false;
   nsresult rv = NS_OK;
 
   if (image->mDecoder) {
-    image->mDecoder->MarkFrameDirty();
-
     if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
     }
 
     if (!image->mHasSize && image->mDecoder->HasSize()) {
       image->mDecoder->SetSizeOnImage();
     }
 
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -201,31 +201,31 @@ public:
   /**
    * Ensures that a given frame number exists with the given parameters, and
    * returns pointers to the data storage for that frame.
    * It is not possible to create sparse frame arrays; you can only append
    * frames to the current frame array.
    */
   nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                        int32_t aWidth, int32_t aHeight,
-                       gfxImageFormat aFormat,
+                       gfx::SurfaceFormat aFormat,
                        uint8_t aPaletteDepth,
                        uint8_t** imageData,
                        uint32_t* imageLength,
                        uint32_t** paletteData,
                        uint32_t* paletteLength,
                        imgFrame** aFrame);
 
   /**
    * A shorthand for EnsureFrame, above, with aPaletteDepth = 0 and paletteData
    * and paletteLength set to null.
    */
   nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                        int32_t aWidth, int32_t aHeight,
-                       gfxImageFormat aFormat,
+                       gfx::SurfaceFormat aFormat,
                        uint8_t** imageData,
                        uint32_t* imageLength,
                        imgFrame** aFrame);
 
   /* notification that the entire image has been decoded */
   nsresult DecodingComplete();
 
   /**
@@ -552,19 +552,18 @@ private:
   bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage,
                                     uint32_t aFlags);
 
-  nsresult CopyFrame(uint32_t aWhichFrame,
-                     uint32_t aFlags,
-                     gfxImageSurface **_retval);
+  TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
+                                             uint32_t aFlags);
 
   /**
    * Deletes and nulls out the frame in mFrames[framenum].
    *
    * Does not change the size of mFrames.
    *
    * @param framenum The index of the frame to be deleted.
    *                 Must lie in [0, mFrames.Length() )
@@ -582,17 +581,17 @@ private:
 
   void EnsureAnimExists();
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
                                   uint8_t **imageData, uint32_t *imageLength,
                                   uint32_t **paletteData, uint32_t *paletteLength,
                                   imgFrame** aRetFrame);
   nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
-                            gfxImageFormat aFormat, uint8_t aPaletteDepth,
+                            gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth,
                             uint8_t **imageData, uint32_t *imageLength,
                             uint32_t **paletteData, uint32_t *paletteLength,
                             imgFrame** aRetFrame);
 
   nsresult DoImageDataComplete();
 
   bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame);
 
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -908,17 +908,17 @@ VectorImage::CreateDrawableAndShow(const
   nsRefPtr<gfxContext> ctx = new gfxContext(target);
 
   // Actually draw. (We use FILTER_NEAREST since we never scale here.)
   gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
-                             gfxImageFormat::ARGB32,
+                             SurfaceFormat::B8G8R8A8,
                              GraphicsFilter::FILTER_NEAREST, aParams.flags);
 
   // Attempt to cache the resulting surface.
   SurfaceCache::Insert(target,
                        ImageKey(this),
                        SurfaceKey(aParams.imageRect.Size(), aParams.scale,
                                   aParams.svgContext, aParams.animationTime,
                                   aParams.flags));
@@ -935,17 +935,17 @@ VectorImage::CreateDrawableAndShow(const
 void
 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
 {
   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
   gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
                              aParams.userSpaceToImageSpace,
                              aParams.subimage, aParams.sourceRect,
                              ThebesIntRect(aParams.imageRect), aParams.fill,
-                             gfxImageFormat::ARGB32,
+                             SurfaceFormat::B8G8R8A8,
                              aParams.filter, aParams.flags);
 
   MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
   mRenderingObserver->ResumeHonoringInvalidations();
 }
 
 //******************************************************************************
 /* void requestDecode() */
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -17,64 +17,55 @@
 static bool gDisableOptimize = false;
 
 #include "cairo.h"
 #include "GeckoProfiler.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsMargin.h"
 #include "mozilla/CheckedInt.h"
-
-#if defined(XP_WIN)
-
-#include "gfxWindowsPlatform.h"
-
-/* Whether to use the windows surface; only for desktop win32 */
-#define USE_WIN_SURFACE 1
-
-#endif
+#include "mozilla/gfx/Tools.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
-static cairo_user_data_key_t kVolatileBuffer;
+static UserDataKey kVolatileBuffer;
 
 static void
 VolatileBufferRelease(void *vbuf)
 {
   delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
 }
 
-gfxImageSurface *
-LockedImageSurface::CreateSurface(VolatileBuffer *vbuf,
-                                  const gfxIntSize& size,
-                                  gfxImageFormat format)
+static TemporaryRef<DataSourceSurface>
+CreateLockedSurface(VolatileBuffer *vbuf,
+                    const IntSize& size,
+                    SurfaceFormat format)
 {
   VolatileBufferPtr<unsigned char> *vbufptr =
     new VolatileBufferPtr<unsigned char>(vbuf);
   MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
 
-  long stride = gfxImageSurface::ComputeStride(size, format);
-  gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format);
-  if (!img || img->CairoStatus()) {
-    delete img;
+  int32_t stride = size.width * BytesPerPixel(format);
+  RefPtr<DataSourceSurface> surf =
+    Factory::CreateWrappingDataSourceSurface(*vbufptr, stride, size, format);
+  if (!surf) {
     delete vbufptr;
     return nullptr;
   }
 
-  img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
-  return img;
+  surf->AddUserData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
+  return surf;
 }
 
-TemporaryRef<VolatileBuffer>
-LockedImageSurface::AllocateBuffer(const gfxIntSize& size,
-                                   gfxImageFormat format)
+static TemporaryRef<VolatileBuffer>
+AllocateBufferForImage(const IntSize& size, SurfaceFormat format)
 {
-  long stride = gfxImageSurface::ComputeStride(size, format);
+  int32_t stride = size.width * BytesPerPixel(format);
   RefPtr<VolatileBuffer> buf = new VolatileBuffer();
   if (buf->Init(stride * size.height,
                 1 << gfxAlphaRecovery::GoodAlignmentLog2()))
     return buf;
 
   return nullptr;
 }
 
@@ -104,61 +95,29 @@ static bool AllowedImageSize(int32_t aWi
   if (MOZ_UNLIKELY(aHeight > SHRT_MAX)) {
     NS_WARNING("image too big");
     return false;
   }
 #endif
   return true;
 }
 
-// Returns whether we should, at this time, use image surfaces instead of
-// optimized platform-specific surfaces.
-static bool ShouldUseImageSurfaces()
-{
-#if defined(USE_WIN_SURFACE)
-  static const DWORD kGDIObjectsHighWaterMark = 7000;
-
-  if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
-      gfxWindowsPlatform::RENDER_DIRECT2D) {
-    return true;
-  }
-
-  // at 7000 GDI objects, stop allocating normal images to make sure
-  // we never hit the 10k hard limit.
-  // GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik
-  DWORD count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
-  if (count == 0 ||
-      count > kGDIObjectsHighWaterMark)
-  {
-    // either something's broken (count == 0),
-    // or we hit our high water mark; disable
-    // image allocations for a bit.
-    return true;
-  }
-#endif
-
-  return false;
-}
-
 imgFrame::imgFrame() :
   mDecoded(0, 0, 0, 0),
-  mDirtyMutex("imgFrame::mDirty"),
+  mDecodedMutex("imgFrame::mDecoded"),
   mPalettedImageData(nullptr),
-  mSinglePixelColor(0),
   mTimeout(100),
   mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
   mLockCount(0),
   mBlendMethod(1), /* imgIContainer::kBlendOver */
   mSinglePixel(false),
-  mFormatChanged(false),
   mCompositingFailed(false),
   mNonPremult(false),
   mDiscardable(false),
-  mInformedDiscardTracker(false),
-  mDirty(false)
+  mInformedDiscardTracker(false)
 {
   static bool hasCheckedOptimize = false;
   if (!hasCheckedOptimize) {
     if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
       gDisableOptimize = true;
     }
     hasCheckedOptimize = true;
   }
@@ -170,17 +129,17 @@ imgFrame::~imgFrame()
   mPalettedImageData = nullptr;
 
   if (mInformedDiscardTracker) {
     DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width);
   }
 }
 
 nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
-                        gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
+                        SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
 {
   // assert for properties that should be verified by decoders, warn for properties related to bad content
   if (!AllowedImageSize(aWidth, aHeight)) {
     NS_WARNING("Should have legal image size");
     return NS_ERROR_FAILURE;
   }
 
   mOffset.MoveTo(aX, aY);
@@ -203,65 +162,32 @@ nsresult imgFrame::Init(int32_t aX, int3
       NS_WARNING("moz_malloc for paletted image data should succeed");
     NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
   } else {
     // Inform the discard tracker that we are going to allocate some memory.
     if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) {
       NS_WARNING("Exceed the hard limit of decode image size");
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    // For Windows, we must create the device surface first (if we're
-    // going to) so that the image surface can wrap it.  Can't be done
-    // the other way around.
-#ifdef USE_WIN_SURFACE
-    if (!ShouldUseImageSurfaces()) {
-      mWinSurface = new gfxWindowsSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
-      if (mWinSurface && mWinSurface->CairoStatus() == 0) {
-        // no error
-        mImageSurface = mWinSurface->GetAsImageSurface();
-      } else {
-        mWinSurface = nullptr;
-      }
-    }
-#endif
-
-    // For other platforms, space for the image surface is first allocated in
-    // a volatile buffer and then wrapped by a LockedImageSurface.
-    // This branch is also used on Windows if we're not using device surfaces
-    // or if we couldn't create one.
     if (!mImageSurface) {
-      mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
+      mVBuf = AllocateBufferForImage(mSize, mFormat);
       if (!mVBuf) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
-      mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
+      mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
     }
 
-    if (!mImageSurface || mImageSurface->CairoStatus()) {
-      mImageSurface = nullptr;
-      // guess
-      if (!mImageSurface) {
-        NS_WARNING("Allocation of gfxImageSurface should succeed");
-      } else if (!mImageSurface->CairoStatus()) {
-        NS_WARNING("gfxImageSurface should have good CairoStatus");
-      }
-
+    if (!mImageSurface) {
+      NS_WARNING("Failed to create VolatileDataSourceSurface");
       // Image surface allocation is failed, need to return
       // the booked buffer size.
       DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
       return NS_ERROR_OUT_OF_MEMORY;
     }
-
     mInformedDiscardTracker = true;
-
-#ifdef XP_MACOSX
-    if (!ShouldUseImageSurfaces()) {
-      mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
-    }
-#endif
   }
 
   return NS_OK;
 }
 
 nsresult imgFrame::Optimize()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -276,193 +202,157 @@ nsresult imgFrame::Optimize()
   // Cairo doesn't support non-premult single-colors.
   if (mNonPremult)
     return NS_OK;
 
   /* Figure out if the entire image is a constant color */
 
   // this should always be true
   if (mImageSurface->Stride() == mSize.width * 4) {
-    uint32_t *imgData = (uint32_t*) mImageSurface->Data();
+    uint32_t *imgData = (uint32_t*) ((uint8_t *)mVBufPtr);
     uint32_t firstPixel = * (uint32_t*) imgData;
     uint32_t pixelCount = mSize.width * mSize.height + 1;
 
     while (--pixelCount && *imgData++ == firstPixel)
       ;
 
     if (pixelCount == 0) {
       // all pixels were the same
-      if (mFormat == gfxImageFormat::ARGB32 ||
-          mFormat == gfxImageFormat::RGB24)
-      {
-        // Should already be premult if desired.
-        gfxRGBA::PackedColorType inputType = gfxRGBA::PACKED_XRGB;
-        if (mFormat == gfxImageFormat::ARGB32)
-          inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED;
-
-        mSinglePixelColor = gfxRGBA(firstPixel, inputType);
-
+      if (mFormat == SurfaceFormat::B8G8R8A8 ||
+          mFormat == SurfaceFormat::B8G8R8X8) {
         mSinglePixel = true;
+        mSinglePixelColor.a = ((firstPixel >> 24) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.r = ((firstPixel >> 16) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.g = ((firstPixel >>  8) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.b = ((firstPixel >>  0) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.r /= mSinglePixelColor.a;
+        mSinglePixelColor.g /= mSinglePixelColor.a;
+        mSinglePixelColor.b /= mSinglePixelColor.a;
 
         // blow away the older surfaces (if they exist), to release their memory
         mVBuf = nullptr;
+        mVBufPtr = nullptr;
         mImageSurface = nullptr;
         mOptSurface = nullptr;
-#ifdef USE_WIN_SURFACE
-        mWinSurface = nullptr;
-#endif
-#ifdef XP_MACOSX
-        mQuartzSurface = nullptr;
-#endif
-        mDrawSurface = nullptr;
 
         // We just dumped most of our allocated memory, so tell the discard
         // tracker that we're not using any at all.
         if (mInformedDiscardTracker) {
           DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
           mInformedDiscardTracker = false;
         }
 
         return NS_OK;
       }
     }
 
     // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
   }
 
-  // if we're being forced to use image surfaces due to
-  // resource constraints, don't try to optimize beyond same-pixel.
-  if (ShouldUseImageSurfaces())
-    return NS_OK;
-
-  mOptSurface = nullptr;
+#ifdef ANDROID
+  SurfaceFormat optFormat =
+    gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR);
 
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface) {
-    if (!mFormatChanged) {
-      // just use the DIB if the format has not changed
-      mOptSurface = mWinSurface;
-    }
-  }
-#endif
-
-#ifdef XP_MACOSX
-  if (mQuartzSurface) {
-    mQuartzSurface->Flush();
-  }
-#endif
-
-#ifdef ANDROID
-  gfxImageFormat optFormat =
-    gfxPlatform::GetPlatform()->
-      OptimalFormatForContent(gfxASurface::ContentFromFormat(mFormat));
-
-  if (optFormat == gfxImageFormat::RGB16_565) {
+  if (!GetHasAlpha() && optFormat == SurfaceFormat::R5G6B5) {
     RefPtr<VolatileBuffer> buf =
-      LockedImageSurface::AllocateBuffer(mSize, optFormat);
+      AllocateBufferForImage(mSize, optFormat);
     if (!buf)
       return NS_OK;
 
-    nsRefPtr<gfxImageSurface> surf =
-      LockedImageSurface::CreateSurface(buf, mSize, optFormat);
+    RefPtr<DataSourceSurface> surf =
+      CreateLockedSurface(buf, mSize, optFormat);
+    if (!surf)
+      return NS_ERROR_OUT_OF_MEMORY;
 
-    gfxContext ctx(surf);
-    ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
-    ctx.SetSource(mImageSurface);
-    ctx.Paint();
+    DataSourceSurface::MappedSurface mapping;
+    DebugOnly<bool> success =
+      surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+    NS_ASSERTION(success, "Failed to map surface");
+    RefPtr<DrawTarget> target =
+      Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                       mapping.mData,
+                                       mSize,
+                                       mapping.mStride,
+                                       optFormat);
+
+    Rect rect(0, 0, mSize.width, mSize.height);
+    target->DrawSurface(mImageSurface, rect, rect);
+    target->Flush();
+    surf->Unmap();
 
     mImageSurface = surf;
     mVBuf = buf;
     mFormat = optFormat;
-    mDrawSurface = nullptr;
   }
 #else
-  if (mOptSurface == nullptr)
-    mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
+  mOptSurface = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mImageSurface);
+  if (mOptSurface == mImageSurface)
+    mOptSurface = nullptr;
 #endif
 
   if (mOptSurface) {
     mVBuf = nullptr;
+    mVBufPtr = nullptr;
     mImageSurface = nullptr;
-#ifdef USE_WIN_SURFACE
-    mWinSurface = nullptr;
-#endif
-#ifdef XP_MACOSX
-    mQuartzSurface = nullptr;
-#endif
-    mDrawSurface = nullptr;
   }
 
   return NS_OK;
 }
 
-static void
-DoSingleColorFastPath(gfxContext*    aContext,
-                      const gfxRGBA& aSinglePixelColor,
-                      const gfxRect& aFill)
-{
-  // if a == 0, it's a noop
-  if (aSinglePixelColor.a == 0.0)
-    return;
-
-  gfxContext::GraphicsOperator op = aContext->CurrentOperator();
-  if (op == gfxContext::OPERATOR_OVER && aSinglePixelColor.a == 1.0) {
-    aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
-  }
-
-  aContext->SetDeviceColor(aSinglePixelColor);
-  aContext->NewPath();
-  aContext->Rectangle(aFill);
-  aContext->Fill();
-  aContext->SetOperator(op);
-  aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
-}
-
 imgFrame::SurfaceWithFormat
 imgFrame::SurfaceForDrawing(bool               aDoPadding,
                             bool               aDoPartialDecode,
                             bool               aDoTile,
                             const nsIntMargin& aPadding,
                             gfxMatrix&         aUserSpaceToImageSpace,
                             gfxRect&           aFill,
                             gfxRect&           aSubimage,
                             gfxRect&           aSourceRect,
                             gfxRect&           aImageRect,
-                            gfxASurface*       aSurface)
+                            SourceSurface*     aSurface)
 {
   IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
   if (!aDoPadding && !aDoPartialDecode) {
     NS_ASSERTION(!mSinglePixel, "This should already have been handled");
     return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat);
   }
 
   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
 
   if (aDoTile || mSinglePixel) {
     // Create a temporary surface.
     // Give this surface an alpha channel because there are
     // transparent pixels in the padding or undecoded area
-    gfxImageFormat format = gfxImageFormat::ARGB32;
-    nsRefPtr<gfxASurface> surface =
-      gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxImageSurface::ContentFromFormat(format));
-    if (!surface || surface->CairoStatus())
+    RefPtr<DrawTarget> target =
+      gfxPlatform::GetPlatform()->
+        CreateOffscreenContentDrawTarget(size, SurfaceFormat::B8G8R8A8);
+    if (!target)
       return SurfaceWithFormat();
 
+    Rect fillRect(aFill.x, aFill.y, aFill.width, aFill.height);
     // Fill 'available' with whatever we've got
-    gfxContext tmpCtx(surface);
-    tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
     if (mSinglePixel) {
-      tmpCtx.SetDeviceColor(mSinglePixelColor);
+      target->FillRect(fillRect, ColorPattern(mSinglePixelColor),
+                       DrawOptions(1.0f, CompositionOp::OP_SOURCE));
     } else {
-      tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top));
+      gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
+      imageSpaceToUserSpace.Invert();
+      SurfacePattern pattern(aSurface,
+                             ExtendMode::REPEAT,
+                             Matrix(imageSpaceToUserSpace.xx,
+                                    imageSpaceToUserSpace.xy,
+                                    imageSpaceToUserSpace.yx,
+                                    imageSpaceToUserSpace.yy,
+                                    imageSpaceToUserSpace.x0,
+                                    imageSpaceToUserSpace.y0));
+      target->FillRect(fillRect, pattern);
     }
-    tmpCtx.Rectangle(available);
-    tmpCtx.Fill();
 
-    return SurfaceWithFormat(new gfxSurfaceDrawable(surface, ThebesIntSize(size)), format);
+    RefPtr<SourceSurface> newsurf = target->Snapshot();
+    return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, ThebesIntSize(size)), target->GetFormat());
   }
 
   // Not tiling, and we have a surface, so we can account for
   // padding and/or a partial decode just by twiddling parameters.
   // First, update our user-space fill rect.
   aSourceRect = aSourceRect.Intersect(available);
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
@@ -488,53 +378,42 @@ bool imgFrame::Draw(gfxContext *aContext
 
   NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
   NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
   NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
 
   bool doPadding = aPadding != nsIntMargin(0,0,0,0);
   bool doPartialDecode = !ImageComplete();
 
+  RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
+
   if (mSinglePixel && !doPadding && !doPartialDecode) {
-    DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
+    if (mSinglePixelColor.a == 0.0) {
+      return true;
+    }
+
+    Rect target(aFill.x, aFill.y, aFill.width, aFill.height);
+    dt->FillRect(target, ColorPattern(mSinglePixelColor),
+                 DrawOptions(1.0f, CompositionOpForOp(aContext->CurrentOperator())));
     return true;
   }
 
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
   gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
                     mSize.height + aPadding.TopBottom());
   gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
   gfxRect fill = aFill;
 
   NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
                "We must be allowed to sample *some* source pixels!");
 
-  nsRefPtr<gfxASurface> surf = CachedThebesSurface();
-  VolatileBufferPtr<unsigned char> ref(mVBuf);
-  if (!mSinglePixel && !surf) {
-    if (ref.WasBufferPurged()) {
-      return false;
-    }
-
-    surf = mDrawSurface;
-    if (!surf) {
-      long stride = gfxImageSurface::ComputeStride(mSize, mFormat);
-      nsRefPtr<gfxImageSurface> imgSurf =
-        new gfxImageSurface(ref, mSize, stride, mFormat);
-#if defined(XP_MACOSX)
-      surf = mDrawSurface = new gfxQuartzImageSurface(imgSurf);
-#else
-      surf = mDrawSurface = imgSurf;
-#endif
-    }
-    if (!surf || surf->CairoStatus()) {
-      mDrawSurface = nullptr;
-      return true;
-    }
+  RefPtr<SourceSurface> surf = GetSurface();
+  if (!surf) {
+    return false;
   }
 
   bool doTile = !imageRect.Contains(sourceRect) &&
                 !(aImageFlags & imgIContainer::FLAG_CLAMP);
   SurfaceWithFormat surfaceResult =
     SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                       userSpaceToImageSpace, fill, subimage, sourceRect,
                       imageRect, surf);
@@ -546,79 +425,66 @@ bool imgFrame::Draw(gfxContext *aContext
                                surfaceResult.mFormat, aFilter, aImageFlags);
   }
   return true;
 }
 
 // This can be called from any thread, but not simultaneously.
 nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
 {
-  MutexAutoLock lock(mDirtyMutex);
+  MutexAutoLock lock(mDecodedMutex);
 
   mDecoded.UnionRect(mDecoded, aUpdateRect);
 
   // clamp to bounds, in case someone sends a bogus updateRect (I'm looking at
   // you, gif decoder)
-  nsIntRect boundsRect(mOffset, mSize);
+  nsIntRect boundsRect(mOffset, nsIntSize(mSize.width, mSize.height));
   mDecoded.IntersectRect(mDecoded, boundsRect);
 
-  mDirty = true;
-
   return NS_OK;
 }
 
-bool imgFrame::GetIsDirty() const
+nsIntRect imgFrame::GetRect() const
 {
-  MutexAutoLock lock(mDirtyMutex);
-  return mDirty;
+  return nsIntRect(mOffset, nsIntSize(mSize.width, mSize.height));
 }
 
-nsIntRect imgFrame::GetRect() const
-{
-  return nsIntRect(mOffset, mSize);
-}
-
-gfxImageFormat imgFrame::GetFormat() const
+SurfaceFormat imgFrame::GetFormat() const
 {
   return mFormat;
 }
 
 bool imgFrame::GetNeedsBackground() const
 {
   // We need a background painted if we have alpha or we're incomplete.
-  return (mFormat == gfxImageFormat::ARGB32 || !ImageComplete());
+  return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
 }
 
 uint32_t imgFrame::GetImageBytesPerRow() const
 {
-  if (mImageSurface)
-    return mImageSurface->Stride();
-
   if (mVBuf)
-    return gfxImageSurface::ComputeStride(mSize, mFormat);
+    return mSize.width * BytesPerPixel(mFormat);
 
   if (mPaletteDepth)
     return mSize.width;
 
-  NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0");
-
   return 0;
 }
 
 uint32_t imgFrame::GetImageDataLength() const
 {
   return GetImageBytesPerRow() * mSize.height;
 }
 
 void imgFrame::GetImageData(uint8_t **aData, uint32_t *length) const
 {
   NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetImageData unless frame is locked");
 
   if (mImageSurface)
-    *aData = mImageSurface->Data();
+    *aData = mVBufPtr;
   else if (mPalettedImageData)
     *aData = mPalettedImageData + PaletteDataLength();
   else
     *aData = nullptr;
 
   *length = GetImageDataLength();
 }
 
@@ -632,17 +498,17 @@ uint8_t* imgFrame::GetImageData() const
 
 bool imgFrame::GetIsPaletted() const
 {
   return mPalettedImageData != nullptr;
 }
 
 bool imgFrame::GetHasAlpha() const
 {
-  return mFormat == gfxImageFormat::ARGB32;
+  return mFormat == SurfaceFormat::B8G8R8A8;
 }
 
 void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const
 {
   NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetPaletteData unless frame is locked");
 
   if (!mPalettedImageData) {
     *aPalette = nullptr;
@@ -682,76 +548,69 @@ nsresult imgFrame::LockImageData()
     return NS_OK;
 
   if (!mImageSurface) {
     if (mVBuf) {
       VolatileBufferPtr<uint8_t> ref(mVBuf);
       if (ref.WasBufferPurged())
         return NS_ERROR_FAILURE;
 
-      mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
-      if (!mImageSurface || mImageSurface->CairoStatus())
+      mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
+      if (!mImageSurface)
         return NS_ERROR_OUT_OF_MEMORY;
     }
-    if (mOptSurface || mSinglePixel || mFormat == gfxImageFormat::RGB16_565) {
-      gfxImageFormat format = mFormat;
-      if (mFormat == gfxImageFormat::RGB16_565)
-        format = gfxImageFormat::ARGB32;
+    if (mOptSurface || mSinglePixel || mFormat == SurfaceFormat::R5G6B5) {
+      SurfaceFormat format = mFormat;
+      if (mFormat == SurfaceFormat::R5G6B5)
+        format = SurfaceFormat::B8G8R8A8;
 
       // Recover the pixels
       RefPtr<VolatileBuffer> buf =
-        LockedImageSurface::AllocateBuffer(mSize, format);
+        AllocateBufferForImage(mSize, format);
       if (!buf) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
-      RefPtr<gfxImageSurface> surf =
-        LockedImageSurface::CreateSurface(buf, mSize, mFormat);
-      if (!surf || surf->CairoStatus())
+      RefPtr<DataSourceSurface> surf =
+        CreateLockedSurface(buf, mSize, format);
+      if (!surf)
         return NS_ERROR_OUT_OF_MEMORY;
 
-      gfxContext context(surf);
-      context.SetOperator(gfxContext::OPERATOR_SOURCE);
+      DataSourceSurface::MappedSurface mapping;
+      DebugOnly<bool> success =
+        surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+      NS_ASSERTION(success, "Failed to map surface");
+      RefPtr<DrawTarget> target =
+        Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                         mapping.mData,
+                                         mSize,
+                                         mapping.mStride,
+                                         format);
+
+      Rect rect(0, 0, mSize.width, mSize.height);
       if (mSinglePixel)
-        context.SetDeviceColor(mSinglePixelColor);
-      else if (mFormat == gfxImageFormat::RGB16_565)
-        context.SetSource(mImageSurface);
+        target->FillRect(rect, ColorPattern(mSinglePixelColor),
+                         DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+      else if (mFormat == SurfaceFormat::R5G6B5)
+        target->DrawSurface(mImageSurface, rect, rect);
       else
-        context.SetSource(mOptSurface);
-      context.Paint();
+        target->DrawSurface(mOptSurface, rect, rect,
+                            DrawSurfaceOptions(),
+                            DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+      target->Flush();
+      surf->Unmap();
 
       mFormat = format;
       mVBuf = buf;
       mImageSurface = surf;
       mOptSurface = nullptr;
-#ifdef USE_WIN_SURFACE
-      mWinSurface = nullptr;
-#endif
-#ifdef XP_MACOSX
-      mQuartzSurface = nullptr;
-#endif
     }
   }
 
-  // We might write to the bits in this image surface, so we need to make the
-  // surface ready for that.
-  if (mImageSurface)
-    mImageSurface->Flush();
-
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface)
-    mWinSurface->Flush();
-#endif
-
-#ifdef XP_MACOSX
-  if (!mQuartzSurface && !ShouldUseImageSurfaces()) {
-    mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
-  }
-#endif
-
+  mVBufPtr = mVBuf;
   return NS_OK;
 }
 
 nsresult imgFrame::UnlockImageData()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!");
@@ -770,98 +629,56 @@ nsresult imgFrame::UnlockImageData()
   if (mLockCount != 0) {
     return NS_OK;
   }
 
   // Paletted images don't have surfaces, so there's nothing to do.
   if (mPalettedImageData)
     return NS_OK;
 
-  // FIXME: Bug 795737
-  // If this image has been drawn since we were locked, it has had snapshots
-  // added, and we need to remove them before calling MarkDirty.
-  if (mImageSurface)
-    mImageSurface->Flush();
-
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface)
-    mWinSurface->Flush();
-#endif
-
-  // Assume we've been written to.
-  if (mImageSurface)
-    mImageSurface->MarkDirty();
-
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface)
-    mWinSurface->MarkDirty();
-#endif
-
-#ifdef XP_MACOSX
-  // The quartz image surface (ab)uses the flush method to get the
-  // cairo_image_surface data into a CGImage, so we have to call Flush() here.
-  if (mQuartzSurface)
-    mQuartzSurface->Flush();
-#endif
-
+  mVBufPtr = nullptr;
   if (mVBuf && mDiscardable) {
     mImageSurface = nullptr;
-#ifdef XP_MACOSX
-    mQuartzSurface = nullptr;
-#endif
   }
 
   return NS_OK;
 }
 
-void imgFrame::ApplyDirtToSurfaces()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MutexAutoLock lock(mDirtyMutex);
-  if (mDirty) {
-    // FIXME: Bug 795737
-    // If this image has been drawn since we were locked, it has had snapshots
-    // added, and we need to remove them before calling MarkDirty.
-    if (mImageSurface)
-      mImageSurface->Flush();
-
-#ifdef USE_WIN_SURFACE
-    if (mWinSurface)
-      mWinSurface->Flush();
-#endif
-
-    if (mImageSurface)
-      mImageSurface->MarkDirty();
-
-#ifdef USE_WIN_SURFACE
-    if (mWinSurface)
-      mWinSurface->MarkDirty();
-#endif
-
-#ifdef XP_MACOSX
-    // The quartz image surface (ab)uses the flush method to get the
-    // cairo_image_surface data into a CGImage, so we have to call Flush() here.
-    if (mQuartzSurface)
-      mQuartzSurface->Flush();
-#endif
-
-    mDirty = false;
-  }
-}
-
 void imgFrame::SetDiscardable()
 {
   MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
   // Disabled elsewhere due to the cost of calling GetSourceSurfaceForSurface.
 #ifdef MOZ_WIDGET_ANDROID
   mDiscardable = true;
 #endif
 }
 
+TemporaryRef<SourceSurface>
+imgFrame::GetSurface()
+{
+  if (mOptSurface) {
+    if (mOptSurface->IsValid())
+      return mOptSurface;
+    else
+      mOptSurface = nullptr;
+  }
+
+  if (mImageSurface)
+    return mImageSurface;
+
+  if (!mVBuf)
+    return nullptr;
+
+  VolatileBufferPtr<char> buf(mVBuf);
+  if (buf.WasBufferPurged())
+    return nullptr;
+
+  return CreateLockedSurface(mVBuf, mSize, mFormat);
+}
+
 int32_t imgFrame::GetRawTimeout() const
 {
   return mTimeout;
 }
 
 void imgFrame::SetRawTimeout(int32_t aTimeout)
 {
   mTimeout = aTimeout;
@@ -885,33 +702,32 @@ int32_t imgFrame::GetBlendMethod() const
 void imgFrame::SetBlendMethod(int32_t aBlendMethod)
 {
   mBlendMethod = (int8_t)aBlendMethod;
 }
 
 // This can be called from any thread.
 bool imgFrame::ImageComplete() const
 {
-  MutexAutoLock lock(mDirtyMutex);
+  MutexAutoLock lock(mDecodedMutex);
 
-  return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
+  return mDecoded.IsEqualInterior(nsIntRect(mOffset.x, mOffset.y,
+                                            mSize.width, mSize.height));
 }
 
 // A hint from the image decoders that this image has no alpha, even
 // though we created is ARGB32.  This changes our format to RGB24,
 // which in turn will cause us to Optimize() to RGB24.  Has no effect
 // after Optimize() is called, though in all cases it will be just a
 // performance win -- the pixels are still correct and have the A byte
 // set to 0xff.
 void imgFrame::SetHasNoAlpha()
 {
-  if (mFormat == gfxImageFormat::ARGB32) {
-      mFormat = gfxImageFormat::RGB24;
-      mFormatChanged = true;
-      ThebesSurface()->SetOpaqueRect(gfxRect(0, 0, mSize.width, mSize.height));
+  if (mFormat == SurfaceFormat::B8G8R8A8) {
+    mFormat = SurfaceFormat::B8G8R8X8;
   }
 }
 
 void imgFrame::SetAsNonPremult(bool aIsNonPremult)
 {
   mNonPremult = aIsNonPremult;
 }
 
@@ -943,53 +759,26 @@ imgFrame::SizeOfExcludingThisWithCompute
   if (mPalettedImageData && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
     size_t n2 = aMallocSizeOf(mPalettedImageData);
     if (n2 == 0) {
       n2 = GetImageDataLength() + PaletteDataLength();
     }
     n += n2;
   }
 
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
-    n += mWinSurface->KnownMemoryUsed();
-  } else
-#endif
-#ifdef XP_MACOSX
-  if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mQuartzSurface);
+  if (mImageSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
+    n += aMallocSizeOf(mImageSurface);
   }
-#endif
-  if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
-    size_t n2 = 0;
-    if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { // HEAP: measure
-      n2 = mImageSurface->SizeOfIncludingThis(aMallocSizeOf);
-    }
-    if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
-      n2 = mImageSurface->KnownMemoryUsed();
-    }
-    n += n2;
+  if (mOptSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
+    n += aMallocSizeOf(mOptSurface);
   }
 
   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
     n += aMallocSizeOf(mVBuf);
     n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
   }
 
   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) {
     n += mVBuf->NonHeapSizeOfExcludingThis();
   }
 
-  if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
-    size_t n2 = 0;
-    if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&
-        mOptSurface->SizeOfIsMeasured()) {
-      // HEAP: measure (but only if the sub-class is capable of measuring)
-      n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf);
-    }
-    if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
-      n2 = mOptSurface->KnownMemoryUsed();
-    }
-    n += n2;
-  }
-
   return n;
 }
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -5,65 +5,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef imgFrame_h
 #define imgFrame_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/VolatileBuffer.h"
-#include "nsRect.h"
-#include "nsPoint.h"
-#include "nsSize.h"
-#include "gfxPattern.h"
 #include "gfxDrawable.h"
-#include "gfxImageSurface.h"
-#if defined(XP_WIN)
-#include "gfxWindowsSurface.h"
-#elif defined(XP_MACOSX)
-#include "gfxQuartzImageSurface.h"
-#endif
-#include "nsAutoPtr.h"
 #include "imgIContainer.h"
-#include "gfxColor.h"
-
-/*
- * This creates a gfxImageSurface which will unlock the buffer on destruction
- */
-
-class LockedImageSurface
-{
-public:
-  static gfxImageSurface *
-  CreateSurface(mozilla::VolatileBuffer *vbuf,
-                const gfxIntSize& size,
-                gfxImageFormat format);
-  static mozilla::TemporaryRef<mozilla::VolatileBuffer>
-  AllocateBuffer(const gfxIntSize& size, gfxImageFormat format);
-};
 
 class imgFrame
 {
+  typedef mozilla::gfx::Color Color;
+  typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
+  typedef mozilla::gfx::IntSize IntSize;
+  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
 public:
   imgFrame();
   ~imgFrame();
 
-  nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, gfxImageFormat aFormat, uint8_t aPaletteDepth = 0);
+  nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, mozilla::gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
   nsresult Optimize();
 
   bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
             const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
             const nsIntMargin &aPadding, const nsIntRect &aSubimage,
             uint32_t aImageFlags = imgIContainer::FLAG_NONE);
 
   nsresult ImageUpdated(const nsIntRect &aUpdateRect);
-  bool GetIsDirty() const;
 
   nsIntRect GetRect() const;
-  gfxImageFormat GetFormat() const;
+  mozilla::gfx::SurfaceFormat GetFormat() const;
   bool GetNeedsBackground() const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;
   bool GetIsPaletted() const;
   bool GetHasAlpha() const;
   void GetImageData(uint8_t **aData, uint32_t *length) const;
   uint8_t* GetImageData() const;
   void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
@@ -81,88 +59,33 @@ public:
   void SetHasNoAlpha();
   void SetAsNonPremult(bool aIsNonPremult);
 
   bool GetCompositingFailed() const;
   void SetCompositingFailed(bool val);
 
   nsresult LockImageData();
   nsresult UnlockImageData();
-  void ApplyDirtToSurfaces();
 
   void SetDiscardable();
 
-  nsresult GetSurface(gfxASurface **aSurface)
-  {
-    *aSurface = ThebesSurface();
-    NS_IF_ADDREF(*aSurface);
-    return NS_OK;
-  }
+  mozilla::TemporaryRef<SourceSurface> GetSurface();
 
-  nsresult GetPattern(gfxPattern **aPattern)
+  Color
+  SinglePixelColor()
   {
-    if (mSinglePixel)
-      *aPattern = new gfxPattern(mSinglePixelColor);
-    else
-      *aPattern = new gfxPattern(ThebesSurface());
-    NS_ADDREF(*aPattern);
-    return NS_OK;
+    return mSinglePixelColor;
   }
 
   bool IsSinglePixel()
   {
     return mSinglePixel;
   }
 
-  gfxASurface* CachedThebesSurface()
-  {
-    if (mOptSurface)
-      return mOptSurface;
-#if defined(XP_WIN)
-    if (mWinSurface)
-      return mWinSurface;
-#elif defined(XP_MACOSX)
-    if (mQuartzSurface)
-      return mQuartzSurface;
-#endif
-    if (mImageSurface)
-      return mImageSurface;
-    return nullptr;
-  }
-
-  gfxASurface* ThebesSurface()
-  {
-    gfxASurface *sur = CachedThebesSurface();
-    if (sur)
-      return sur;
-    if (mVBuf) {
-      mozilla::VolatileBufferPtr<uint8_t> ref(mVBuf);
-      if (ref.WasBufferPurged())
-        return nullptr;
-
-      gfxImageSurface *imgSur =
-        LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
-#if defined(XP_MACOSX)
-      // Manually addref and release to make sure the cairo surface isn't lost
-      NS_ADDREF(imgSur);
-      gfxQuartzImageSurface *quartzSur = new gfxQuartzImageSurface(imgSur);
-      // quartzSur does not hold on to the gfxImageSurface
-      NS_RELEASE(imgSur);
-      return quartzSur;
-#else
-      return imgSur;
-#endif
-    }
-    // We can return null here if we're single pixel optimized
-    // or a paletted image. However, one has to check for paletted
-    // image data first before attempting to get a surface, so
-    // this is only valid for single pixel optimized images
-    MOZ_ASSERT(mSinglePixel, "No image surface and not a single pixel!");
-    return nullptr;
-  }
+  mozilla::TemporaryRef<SourceSurface> CachedSurface();
 
   size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
            gfxMemoryLocation aLocation,
            mozilla::MallocSizeOf aMallocSizeOf) const;
 
   uint8_t GetPaletteDepth() const { return mPaletteDepth; }
   uint32_t PaletteDataLength() const {
     if (!mPaletteDepth)
@@ -170,82 +93,73 @@ public:
 
     return ((1 << mPaletteDepth) * sizeof(uint32_t));
   }
 
 private: // methods
 
   struct SurfaceWithFormat {
     nsRefPtr<gfxDrawable> mDrawable;
-    gfxImageFormat mFormat;
+    SurfaceFormat mFormat;
     SurfaceWithFormat() {}
-    SurfaceWithFormat(gfxDrawable* aDrawable, gfxImageFormat aFormat)
+    SurfaceWithFormat(gfxDrawable* aDrawable, SurfaceFormat aFormat)
      : mDrawable(aDrawable), mFormat(aFormat) {}
     bool IsValid() { return !!mDrawable; }
   };
 
   SurfaceWithFormat SurfaceForDrawing(bool               aDoPadding,
                                       bool               aDoPartialDecode,
                                       bool               aDoTile,
                                       const nsIntMargin& aPadding,
                                       gfxMatrix&         aUserSpaceToImageSpace,
                                       gfxRect&           aFill,
                                       gfxRect&           aSubimage,
                                       gfxRect&           aSourceRect,
                                       gfxRect&           aImageRect,
-                                      gfxASurface*       aSurface);
+                                      SourceSurface*     aSurface);
 
 private: // data
-  nsRefPtr<gfxImageSurface> mImageSurface;
-  nsRefPtr<gfxASurface> mOptSurface;
-#if defined(XP_WIN)
-  nsRefPtr<gfxWindowsSurface> mWinSurface;
-#elif defined(XP_MACOSX)
-  nsRefPtr<gfxQuartzImageSurface> mQuartzSurface;
-#endif
+  mozilla::RefPtr<DataSourceSurface> mImageSurface;
+  mozilla::RefPtr<SourceSurface> mOptSurface;
 
-  nsRefPtr<gfxASurface> mDrawSurface;
-
-  nsIntSize    mSize;
+  IntSize      mSize;
   nsIntPoint   mOffset;
 
   nsIntRect    mDecoded;
 
-  mutable mozilla::Mutex mDirtyMutex;
+  mutable mozilla::Mutex mDecodedMutex;
 
   // The palette and image data for images that are paletted, since Cairo
   // doesn't support these images.
   // The paletted data comes first, then the image data itself.
   // Total length is PaletteDataLength() + GetImageDataLength().
   uint8_t*     mPalettedImageData;
 
-  // Note that the data stored in gfxRGBA is *non-alpha-premultiplied*.
-  gfxRGBA      mSinglePixelColor;
+  // Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
+  Color        mSinglePixelColor;
 
   int32_t      mTimeout; // -1 means display forever
   int32_t      mDisposalMethod;
 
   /** Indicates how many readers currently have locked this frame */
   int32_t mLockCount;
 
   mozilla::RefPtr<mozilla::VolatileBuffer> mVBuf;
+  mozilla::VolatileBufferPtr<uint8_t> mVBufPtr;
 
-  gfxImageFormat mFormat;
+  SurfaceFormat mFormat;
   uint8_t      mPaletteDepth;
   int8_t       mBlendMethod;
   bool mSinglePixel;
-  bool mFormatChanged;
   bool mCompositingFailed;
   bool mNonPremult;
   bool mDiscardable;
 
   /** Have we called DiscardTracker::InformAllocation()? */
   bool mInformedDiscardTracker;
-
-  bool mDirty;
 };
 
 namespace mozilla {
 namespace image {
   // An RAII class to ensure it's easy to balance locks and unlocks on
   // imgFrames.
   class AutoFrameLocker
   {
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -227,20 +227,17 @@ typedef bool
 (* StrictElementIdOp)(JSContext *cx, JS::HandleObject obj, uint32_t index,
                       JS::MutableHandleValue vp, bool strict);
 typedef bool
 (* GenericAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
 typedef bool
 (* PropertyAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
                          unsigned *attrsp);
 typedef bool
-(* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
-                     bool *succeeded);
-typedef bool
-(* DeleteElementOp)(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *succeeded);
+(* DeleteGenericOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
 
 typedef bool
 (* WatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
 
 typedef bool
 (* UnwatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 
 typedef bool
@@ -337,41 +334,39 @@ struct ObjectOps
     GenericIdOp         getGeneric;
     PropertyIdOp        getProperty;
     ElementIdOp         getElement;
     StrictGenericIdOp   setGeneric;
     StrictPropertyIdOp  setProperty;
     StrictElementIdOp   setElement;
     GenericAttributesOp getGenericAttributes;
     GenericAttributesOp setGenericAttributes;
-    DeletePropertyOp    deleteProperty;
-    DeleteElementOp     deleteElement;
+    DeleteGenericOp     deleteGeneric;
     WatchOp             watch;
     UnwatchOp           unwatch;
     SliceOp             slice; // Optimized slice, can be null.
-
     JSNewEnumerateOp    enumerate;
     ObjectOp            thisObject;
 };
 
 #define JS_NULL_OBJECT_OPS                                                    \
-    {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, \
-     nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, \
-     nullptr,nullptr}
+    {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
+     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
+     nullptr, nullptr, nullptr}
 
 } // namespace js
 
 // Classes, objects, and properties.
 
 typedef void (*JSClassInternal)();
 
 struct JSClass {
     JS_CLASS_MEMBERS(JSFinalizeOp);
 
-    void                *reserved[32];
+    void                *reserved[31];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_NEW_ENUMERATE           (1<<1)  // has JSNewEnumerateOp hook
 #define JSCLASS_NEW_RESOLVE             (1<<2)  // has JSNewResolveOp hook
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports *)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
 #define JSCLASS_IMPLEMENTS_BARRIERS     (1<<5)  // Correctly implements GC read
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -37,17 +37,17 @@ template <typename T> class AutoVectorRo
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
 template<typename T> class RootedGeneric;
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
-class JS_PUBLIC_API(AutoCheckCannotGC);
+class AutoCheckCannotGC;
 
 }
 
 // Do the importing.
 namespace js {
 
 using JS::Value;
 using JS::BooleanValue;
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -655,16 +655,92 @@ function ArrayValues() {
 function ArrayEntries() {
     return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE);
 }
 
 function ArrayKeys() {
     return CreateArrayIterator(this, ITEM_KIND_KEY);
 }
 
+/* ES6 rev 25 (2014 May 22) 22.1.2.1 */
+function ArrayFrom(arrayLike, mapfn=undefined, thisArg=undefined) {
+    // Step 1.
+    var C = this;
+
+    // Steps 2-3.
+    var items = ToObject(arrayLike);
+
+    // Steps 4-5.
+    var mapping = (mapfn !== undefined);
+    if (mapping && !IsCallable(mapfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn));
+
+    // All elements defined by this algorithm have the same attrs:
+    var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE;
+
+    // Steps 6-8.
+    var usingIterator = items["@@iterator"];
+    if (usingIterator !== undefined) {
+        // Steps 8.a-c.
+        var A = IsConstructor(C) ? new C() : [];
+
+        // Steps 8.d-e.
+        var iterator = callFunction(usingIterator, items);
+
+        // Step 8.f.
+        var k = 0;
+
+        // Steps 8.g.i-vi.
+        // These steps cannot be implemented using a for-of loop.
+        // See <https://bugs.ecmascript.org/show_bug.cgi?id=2883>.
+        var next;
+        while (true) {
+            // Steps 8.g.ii-vi.
+            next = iterator.next();
+            if (!IsObject(next))
+                ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE);
+            if (next.done)
+                break;  // Substeps of 8.g.iv are implemented below.
+            var nextValue = next.value;
+
+            // Steps 8.g.vii-viii.
+            var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
+
+            // Steps 8.g.ix-xi.
+            _DefineDataProperty(A, k++, mappedValue, attrs);
+        }
+    } else {
+        // Step 9 is an assertion: items is not an Iterator. Testing this is
+        // literally the very last thing we did, so we don't assert here.
+
+        // Steps 10-12.
+        // FIXME: Array operations should use ToLength (bug 924058).
+        var len = ToInteger(items.length);
+
+        // Steps 13-15.
+        var A = IsConstructor(C) ? new C(len) : NewDenseArray(len);
+
+        // Steps 16-17.
+        for (var k = 0; k < len; k++) {
+            // Steps 17.a-c.
+            var kValue = items[k];
+
+            // Steps 17.d-e.
+            var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
+
+            // Steps 17.f-g.
+            _DefineDataProperty(A, k, mappedValue, attrs);
+        }
+    }
+
+    // Steps 8.g.iv.1-3 and 18-20 are the same.
+    A.length = k;
+    return A;
+}
+
 #ifdef ENABLE_PARALLEL_JS
 
 /*
  * Strawman spec:
  *   http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism
  */
 
 /**
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -811,21 +811,21 @@ function SupportedLocales(availableLocal
 
     // Steps 2-3.
     var subset = (matcher === undefined || matcher === "best fit")
                  ? BestFitSupportedLocales(availableLocales, requestedLocales)
                  : LookupSupportedLocales(availableLocales, requestedLocales);
 
     // Step 4.
     for (var i = 0; i < subset.length; i++) {
-        _DefineValueProperty(subset, i, subset[i],
-                             ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
+        _DefineDataProperty(subset, i, subset[i],
+                            ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
     }
-    _DefineValueProperty(subset, "length", subset.length,
-                         ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
+    _DefineDataProperty(subset, "length", subset.length,
+                        ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
 
     // Step 5.
     return subset;
 }
 
 
 /**
  * Extracts a property value from the provided options object, converts it to
@@ -891,17 +891,17 @@ function GetNumberOption(options, proper
 /********** Property access for Intl objects **********/
 
 
 /**
  * Set a normal public property p of o to value v, but use Object.defineProperty
  * to avoid interference from setters on Object.prototype.
  */
 function defineProperty(o, p, v) {
-    _DefineValueProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
+    _DefineDataProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
 }
 
 
 /**
  * Weak map used to track the initialize-as-Intl status (and, if an object has
  * been so initialized, the Intl-specific internal properties) of all objects.
  * Presence of an object as a key within this map indicates that the object has
  * its [[initializedIntlObject]] internal property set to true.  The associated
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -366,17 +366,17 @@ enum DefineType { GetterAccessor, Setter
 template<DefineType Type>
 static bool
 DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!BoxNonStrictThis(cx, args))
         return false;
 
-    if (args.length() < 2 || !js_IsCallable(args[1])) {
+    if (args.length() < 2 || !IsCallable(args[1])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_BAD_GETTER_OR_SETTER,
                              Type == GetterAccessor ? js_getter_str : js_setter_str);
         return false;
     }
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[0], &id))
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2088,50 +2088,28 @@ TypedObject::obj_setGenericAttributes(JS
         *attrsp = 0;
         return true;
     }
 
     return JSObject::setGenericAttributes(cx, proto, id, attrsp);
 }
 
 bool
-TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
-                                HandlePropertyName name, bool *succeeded)
+TypedObject::obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
-    Rooted<jsid> id(cx, NameToId(name));
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
         return true;
     }
 
-    return JSObject::deleteProperty(cx, proto, name, succeeded);
-}
-
-bool
-TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                               bool *succeeded)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-
-    if (IsOwnId(cx, obj, id))
-        return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
-
-    RootedObject proto(cx, obj->getProto());
-    if (!proto) {
-        *succeeded = false;
-        return true;
-    }
-
-    return JSObject::deleteElement(cx, proto, index, succeeded);
+    return JSObject::deleteGeneric(cx, proto, id, succeeded);
 }
 
 bool
 TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                            MutableHandleValue statep, MutableHandleId idp)
 {
     int32_t index;
 
@@ -2282,18 +2260,17 @@ const Class TransparentTypedObject::clas
         TypedObject::obj_getGeneric,
         TypedObject::obj_getProperty,
         TypedObject::obj_getElement,
         TypedObject::obj_setGeneric,
         TypedObject::obj_setProperty,
         TypedObject::obj_setElement,
         TypedObject::obj_getGenericAttributes,
         TypedObject::obj_setGenericAttributes,
-        TypedObject::obj_deleteProperty,
-        TypedObject::obj_deleteElement,
+        TypedObject::obj_deleteGeneric,
         nullptr, nullptr, // watch/unwatch
         nullptr,   /* slice */
         TypedObject::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
 static int32_t
@@ -2628,18 +2605,17 @@ const Class OpaqueTypedObject::class_ = 
         TypedObject::obj_getGeneric,
         TypedObject::obj_getProperty,
         TypedObject::obj_getElement,
         TypedObject::obj_setGeneric,
         TypedObject::obj_setProperty,
         TypedObject::obj_setElement,
         TypedObject::obj_getGenericAttributes,
         TypedObject::obj_setGenericAttributes,
-        TypedObject::obj_deleteProperty,
-        TypedObject::obj_deleteElement,
+        TypedObject::obj_deleteGeneric,
         nullptr, nullptr, // watch/unwatch
         nullptr, // slice
         TypedObject::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
 const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -571,20 +571,17 @@ class TypedObject : public ArrayBufferVi
     static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
                                MutableHandleValue vp, bool strict);
 
     static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
                                          HandleId id, unsigned *attrsp);
     static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
                                          HandleId id, unsigned *attrsp);
 
-    static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                   bool *succeeded);
-    static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                  bool *succeeded);
+    static bool obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
 
     static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                               MutableHandleValue statep, MutableHandleId idp);
 
   public:
     static size_t offsetOfOwnerSlot();
 
     // Each typed object contains a void* pointer pointing at the
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -23,18 +23,23 @@
          JSMSG_EMPTY_ARRAY_REDUCE: false, JSMSG_CANT_CONVERT_TO: false,
 */
 
 #include "SelfHostingDefines.h"
 
 // Remove unsafe builtin functions.
 Object.defineProperty = null; // See bug 988416.
 
-// Cache builtin functions so using them doesn't require cloning the whole object they're 
+// Cache builtin functions so using them doesn't require cloning the whole object they're
 // installed on.
+//
+// WARNING: Do not make std_ references to builtin constructors (like Array and
+// Object) below. Setting `var std_Array = Array;`, for instance, would cause
+// the entire Array constructor, including its prototype and methods, to be
+// cloned into content compartments.
 var std_isFinite = isFinite;
 var std_isNaN = isNaN;
 var std_Array_indexOf = ArrayIndexOf;
 var std_Array_iterator = Array.prototype.iterator;
 var std_Array_join = Array.prototype.join;
 var std_Array_push = Array.prototype.push;
 var std_Array_pop = Array.prototype.pop;
 var std_Array_shift = Array.prototype.shift;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -260,16 +260,34 @@ Zone::hasMarkedCompartments()
 {
     for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
         if (comp->marked)
             return true;
     }
     return false;
 }
 
+bool
+Zone::canCollect()
+{
+    // Zones cannot be collected while in use by other threads.
+    if (usedByExclusiveThread)
+        return false;
+    JSRuntime *rt = runtimeFromAnyThread();
+    if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
+        return false;
+    return true;
+}
+
 JS::Zone *
 js::ZoneOfValue(const JS::Value &value)
 {
     JS_ASSERT(value.isMarkable());
     if (value.isObject())
         return value.toObject().zone();
     return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
 }
+
+bool
+js::ZonesIter::atAtomsZone(JSRuntime *rt)
+{
+    return rt->isAtomsZone(*it);
+}
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -132,25 +132,17 @@ struct Zone : public JS::shadow::Zone,
 
     void scheduleGC() { JS_ASSERT(!runtimeFromMainThread()->isHeapBusy()); gcScheduled_ = true; }
     void unscheduleGC() { gcScheduled_ = false; }
     bool isGCScheduled() { return gcScheduled_ && canCollect(); }
 
     void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
     bool isPreservingCode() const { return gcPreserveCode_; }
 
-    bool canCollect() {
-        // Zones cannot be collected while in use by other threads.
-        if (usedByExclusiveThread)
-            return false;
-        JSRuntime *rt = runtimeFromAnyThread();
-        if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
-            return false;
-        return true;
-    }
+    bool canCollect();
 
     enum GCState {
         NoGC,
         Mark,
         MarkGray,
         Sweep,
         Finished
     };
@@ -301,21 +293,23 @@ class ZonesIter
     JS::Zone **it, **end;
 
   public:
     ZonesIter(JSRuntime *rt, ZoneSelector selector) {
         it = rt->gc.zones.begin();
         end = rt->gc.zones.end();
 
         if (selector == SkipAtoms) {
-            JS_ASSERT(rt->isAtomsZone(*it));
+            MOZ_ASSERT(atAtomsZone(rt));
             it++;
         }
     }
 
+    bool atAtomsZone(JSRuntime *rt);
+
     bool done() const { return it == end; }
 
     void next() {
         JS_ASSERT(!done());
         do {
             it++;
         } while (!done() && (*it)->usedByExclusiveThread);
     }
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -31,107 +31,108 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::BinarySearch;
 using mozilla::IsNaN;
 using mozilla::PodZero;
 
-AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
+static uint8_t *
+ReturnAddressForExitCall(uint8_t **psp)
 {
-    if (!activation || activation->isInterruptedSP()) {
-        PodZero(this);
-        JS_ASSERT(done());
-        return;
-    }
-
-    module_ = &activation->module();
-    sp_ = activation->exitSP();
-
+    uint8_t *sp = *psp;
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     // For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
     // to C++. Since the call instruction pushes the return address, we know
     // that the return address is 1 word below exitSP.
-    returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
+    return *(uint8_t**)(sp - sizeof(void*));
 #elif defined(JS_CODEGEN_ARM)
     // For calls to Ion/C++ on ARM, the *caller* pushes the return address on
     // the stack. For Ion, this is just part of the ABI. For C++, the return
     // address is explicitly pushed before the call since we cannot expect the
     // callee to immediately push lr. This means that exitSP points to the
     // return address.
-    returnAddress_ = *(uint8_t**)sp_;
+    return *(uint8_t**)sp;
 #elif defined(JS_CODEGEN_MIPS)
-    // On MIPS we have two cases. Exit to C++ will store return addres at
-    // sp + 16, While on exits to Ion, the return address will be stored at
-    // sp + 0. We indicate exits to ion by setting the lowest bit of stored sp.
-
-    // Check if this is the exit to Ion.
-    if (uint32_t(sp_) & 0x1) {
-        // Clear the low bit.
-        sp_ -= 0x1;
-        returnAddress_ = *(uint8_t**)sp_;
-    } else {
-        // This is exit to C++
-        returnAddress_ = *(uint8_t**)(sp_ + ShadowStackSpace);
+    // On MIPS we have two cases: an exit to C++ will store the return address
+    // at ShadowStackSpace above sp; an exit to Ion will store the return
+    // address at sp. To distinguish the two cases, the low bit of sp (which is
+    // aligned and therefore zero) is set for Ion exits.
+    if (uintptr_t(sp) & 0x1) {
+        sp = *psp -= 0x1;  // Clear the low bit
+        return *(uint8_t**)sp;
     }
+    return *(uint8_t**)(sp + ShadowStackSpace);
 #else
 # error "Unknown architecture!"
 #endif
+}
 
-    settle();
+static uint8_t *
+ReturnAddressForJitCall(uint8_t *sp)
+{
+    // Once inside JIT code, sp always points to the word before the return
+    // address.
+    return *(uint8_t**)(sp - sizeof(void*));
+}
+
+AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
+  : module_(nullptr)
+{
+    if (!activation || activation->isInterruptedSP())
+        return;
+
+    module_ = &activation->module();
+    sp_ = activation->exitSP();
+
+    settle(ReturnAddressForExitCall(&sp_));
+}
+
+void
+AsmJSFrameIterator::operator++()
+{
+    settle(ReturnAddressForJitCall(sp_));
 }
 
 struct GetCallSite
 {
     const AsmJSModule &module;
     explicit GetCallSite(const AsmJSModule &module) : module(module) {}
     uint32_t operator[](size_t index) const {
         return module.callSite(index).returnAddressOffset();
     }
 };
 
 void
-AsmJSFrameIterator::popFrame()
+AsmJSFrameIterator::settle(uint8_t *returnAddress)
 {
-    // After adding stackDepth, sp points to the word before the return address,
-    // on both ARM and x86/x64.
-    sp_ += callsite_->stackDepth();
-    returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
-}
-
-void
-AsmJSFrameIterator::settle()
-{
-    while (true) {
-        uint32_t target = returnAddress_ - module_->codeBase();
-        size_t lowerBound = 0;
-        size_t upperBound = module_->numCallSites();
+    uint32_t target = returnAddress - module_->codeBase();
+    size_t lowerBound = 0;
+    size_t upperBound = module_->numCallSites();
 
-        size_t match;
-        if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
-            callsite_ = nullptr;
-            return;
-        }
-
-        callsite_ = &module_->callSite(match);
-
-        if (callsite_->isExit()) {
-            popFrame();
-            continue;
-        }
-
-        if (callsite_->isEntry()) {
-            callsite_ = nullptr;
-            return;
-        }
-
-        JS_ASSERT(callsite_->isNormal());
+    size_t match;
+    if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
+        module_ = nullptr;
         return;
     }
+
+    callsite_ = &module_->callSite(match);
+
+    if (callsite_->isEntry()) {
+        module_ = nullptr;
+        return;
+    }
+
+    sp_ += callsite_->stackDepth();
+
+    if (callsite_->isExit())
+        return settle(ReturnAddressForJitCall(sp_));
+
+    JS_ASSERT(callsite_->isNormal());
 }
 
 JSAtom *
 AsmJSFrameIterator::functionDisplayAtom() const
 {
     JS_ASSERT(!done());
     return module_->functionName(callsite_->functionNameIndex());
 }
--- a/js/src/jit/AsmJSLink.h
+++ b/js/src/jit/AsmJSLink.h
@@ -18,25 +18,23 @@ class AsmJSModule;
 namespace jit { class CallSite; }
 
 // Iterates over the frames of a single AsmJSActivation.
 class AsmJSFrameIterator
 {
     const AsmJSModule *module_;
     const jit::CallSite *callsite_;
     uint8_t *sp_;
-    uint8_t *returnAddress_;
 
-    void popFrame();
-    void settle();
+    void settle(uint8_t *returnAddress);
 
   public:
     explicit AsmJSFrameIterator(const AsmJSActivation *activation);
-    void operator++() { popFrame(); settle(); }
-    bool done() const { return !callsite_; }
+    void operator++();
+    bool done() const { return !module_; }
     JSAtom *functionDisplayAtom() const;
     unsigned computeLine(uint32_t *column) const;
 };
 
 #ifdef JS_ION
 
 // Create a new JSFunction to replace originalFun as the representation of the
 // function defining the succesfully-validated module 'moduleObj'.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8477,80 +8477,46 @@ CodeGenerator::visitSetDOMProperty(LSetD
 typedef bool(*SPSFn)(JSContext *, HandleScript);
 static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter);
 static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit);
 
 bool
 CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
 {
     Register temp = ToRegister(lir->temp()->output());
-    bool inlinedFunction = lir->inlineLevel() > 0;
 
     switch (lir->type()) {
-        case MProfilerStackOp::InlineEnter:
-            // Multiple scripts can be inlined at one depth, but there is only
-            // one InlineExit node to signify this. To deal with this, if we
-            // reach the entry of another inline script on the same level, then
-            // just reset the sps metadata about the frame. We must balance
-            // calls to leave()/reenter(), so perform the balance without
-            // emitting any instrumentation. Technically the previous inline
-            // call at this same depth has reentered, but the instrumentation
-            // will be emitted at the common join point for all inlines at the
-            // same depth.
-            if (sps_.inliningDepth() == lir->inlineLevel()) {
-                sps_.leaveInlineFrame();
-                sps_.skipNextReenter();
-                sps_.reenter(masm, temp);
-            }
-
-            sps_.leave(masm, temp, /* inlinedFunction = */ true);
-            if (!sps_.enterInlineFrame())
-                return false;
-            // fallthrough
-
         case MProfilerStackOp::Enter:
             if (gen->options.spsSlowAssertionsEnabled()) {
-                if (!inlinedFunction) {
-                    saveLive(lir);
-                    pushArg(ImmGCPtr(lir->script()));
-                    if (!callVM(SPSEnterInfo, lir))
-                        return false;
-                    restoreLive(lir);
-                }
-                sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
+                saveLive(lir);
+                pushArg(ImmGCPtr(lir->script()));
+                if (!callVM(SPSEnterInfo, lir))
+                    return false;
+                restoreLive(lir);
+                sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ false);
                 return true;
             }
 
-            return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
-
-        case MProfilerStackOp::InlineExit:
-            // all inline returns were covered with ::Exit, so we just need to
-            // maintain the state of inline frames currently active and then
-            // reenter the caller
-            sps_.leaveInlineFrame();
-            sps_.reenter(masm, temp, /* inlinedFunction = */ true);
-            return true;
+            return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ false);
 
         case MProfilerStackOp::Exit:
             if (gen->options.spsSlowAssertionsEnabled()) {
-                if (!inlinedFunction) {
-                    saveLive(lir);
-                    pushArg(ImmGCPtr(lir->script()));
-                    // Once we've exited, then we shouldn't emit instrumentation for
-                    // the corresponding reenter() because we no longer have a
-                    // frame.
-                    sps_.skipNextReenter();
-                    if (!callVM(SPSExitInfo, lir))
-                        return false;
-                    restoreLive(lir);
-                }
+                saveLive(lir);
+                pushArg(ImmGCPtr(lir->script()));
+                // Once we've exited, then we shouldn't emit instrumentation for
+                // the corresponding reenter() because we no longer have a
+                // frame.
+                sps_.skipNextReenter();
+                if (!callVM(SPSExitInfo, lir))
+                    return false;
+                restoreLive(lir);
                 return true;
             }
 
-            sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction);
+            sps_.pop(masm, temp, /* inlinedFunction = */ false);
             return true;
 
         default:
             MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type");
     }
 }
 
 bool
--- a/js/src/jit/InlineList.h
+++ b/js/src/jit/InlineList.h
@@ -167,20 +167,18 @@ private:
 public:
     InlineForwardListIterator<T> & operator ++() {
         JS_ASSERT(modifyCount_ == owner_->modifyCount_);
         prev = iter;
         iter = iter->next;
         return *this;
     }
     InlineForwardListIterator<T> operator ++(int) {
-        JS_ASSERT(modifyCount_ == owner_->modifyCount_);
         InlineForwardListIterator<T> old(*this);
-        prev = iter;
-        iter = iter->next;
+        operator++();
         return old;
     }
     T * operator *() const {
         JS_ASSERT(modifyCount_ == owner_->modifyCount_);
         return static_cast<T *>(iter);
     }
     T * operator ->() const {
         JS_ASSERT(modifyCount_ == owner_->modifyCount_);
@@ -334,22 +332,26 @@ class InlineListIterator
 
   public:
     InlineListIterator<T> & operator ++() {
         iter = static_cast<Node *>(iter->next);
         return *this;
     }
     InlineListIterator<T> operator ++(int) {
         InlineListIterator<T> old(*this);
-        iter = static_cast<Node *>(iter->next);
+        operator++();
         return old;
     }
+    InlineListIterator<T> & operator --() {
+        iter = iter->prev;
+        return *this;
+    }
     InlineListIterator<T> operator --(int) {
         InlineListIterator<T> old(*this);
-        iter = iter->prev;
+        operator--();
         return old;
     }
     T * operator *() const {
         return static_cast<T *>(iter);
     }
     T * operator ->() const {
         return static_cast<T *>(iter);
     }
@@ -378,17 +380,26 @@ class InlineListReverseIterator
 
   public:
     InlineListReverseIterator<T> & operator ++() {
         iter = iter->prev;
         return *this;
     }
     InlineListReverseIterator<T> operator ++(int) {
         InlineListReverseIterator<T> old(*this);
-        iter = iter->prev;
+        operator++();
+        return old;
+    }
+    InlineListReverseIterator<T> & operator --() {
+        iter = static_cast<Node *>(iter->next);
+        return *this;
+    }
+    InlineListReverseIterator<T> operator --(int) {
+        InlineListReverseIterator<T> old(*this);
+        operator--();
         return old;
     }
     T * operator *() {
         return static_cast<T *>(iter);
     }
     T * operator ->() {
         return static_cast<T *>(iter);
     }
@@ -459,22 +470,22 @@ class InlineConcatListIterator
     typedef InlineConcatList<T> Node;
 
     explicit InlineConcatListIterator(const Node *iter)
       : iter(const_cast<Node *>(iter))
     { }
 
   public:
     InlineConcatListIterator<T> & operator ++() {
-        iter = iter->next;
-        return *iter;
+        iter = static_cast<Node *>(iter->next);
+        return *this;
     }
     InlineConcatListIterator<T> operator ++(int) {
         InlineConcatListIterator<T> old(*this);
-        iter = static_cast<Node *>(iter->next);
+        operator++();
         return old;
     }
     T * operator *() const {
         return static_cast<T *>(iter);
     }
     T * operator ->() const {
         return static_cast<T *>(iter);
     }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1408,18 +1408,17 @@ OptimizeMIR(MIRGenerator *mir)
 
     if (mir->optimizationInfo().licmEnabled()) {
         AutoTraceLog log(logger, TraceLogger::LICM);
         // LICM can hoist instructions from conditional branches and trigger
         // repeated bailouts. Disable it if this script is known to bailout
         // frequently.
         JSScript *script = mir->info().script();
         if (!script || !script->hadFrequentBailouts()) {
-            LICM licm(mir, graph);
-            if (!licm.analyze())
+            if (!LICM(mir, graph))
                 return false;
             IonSpewPass("LICM");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
         }
     }
@@ -1503,16 +1502,31 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
         IonSpewPass("DCE");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("DCE"))
             return false;
     }
 
+    // Make loops contiguious. We do this after GVN/UCE and range analysis,
+    // which can remove CFG edges, exposing more blocks that can be moved.
+    // We also disable this when profiling, since reordering blocks appears
+    // to make the profiler unhappy.
+    {
+        AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
+        if (!MakeLoopsContiguous(graph))
+            return false;
+        IonSpewPass("Make loops contiguous");
+        AssertExtendedGraphCoherency(graph);
+
+        if (mir->shouldCancel("Make loops contiguous"))
+            return false;
+    }
+
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
     if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) {
         AutoTraceLog log(logger, TraceLogger::EdgeCaseAnalysis);
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
             return false;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1402,25 +1402,28 @@ jit::AssertBasicGraphCoherency(MIRGraph 
     }
 
     JS_ASSERT(graph.numBlocks() == count);
 #endif
 }
 
 #ifdef DEBUG
 static void
-AssertReversePostOrder(MIRGraph &graph)
+AssertReversePostorder(MIRGraph &graph)
 {
     // Check that every block is visited after all its predecessors (except backedges).
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         JS_ASSERT(!block->isMarked());
 
         for (size_t i = 0; i < block->numPredecessors(); i++) {
             MBasicBlock *pred = block->getPredecessor(i);
-            JS_ASSERT_IF(!pred->isLoopBackedge(), pred->isMarked());
+            if (!pred->isMarked()) {
+                JS_ASSERT(pred->isLoopBackedge());
+                JS_ASSERT(block->backedge() == pred);
+            }
         }
 
         block->mark();
     }
 
     graph.unmarkBlocks();
 }
 #endif
@@ -1473,17 +1476,17 @@ AssertDominatorTree(MIRGraph &graph)
 
 void
 jit::AssertGraphCoherency(MIRGraph &graph)
 {
 #ifdef DEBUG
     if (!js_JitOptions.checkGraphConsistency)
         return;
     AssertBasicGraphCoherency(graph);
-    AssertReversePostOrder(graph);
+    AssertReversePostorder(graph);
 #endif
 }
 
 void
 jit::AssertExtendedGraphCoherency(MIRGraph &graph)
 {
     // Checks the basic GraphCoherency but also other conditions that
     // do not hold immediately (such as the fact that critical edges
@@ -2477,8 +2480,174 @@ jit::AnalyzeArgumentsUsage(JSContext *cx
     // arguments. The compiler can then assume that accesses through
     // arguments[i] will be on unaliased variables.
     if (script->funHasAnyAliasedFormal() && argumentsContentsObserved)
         return true;
 
     script->setNeedsArgsObj(false);
     return true;
 }
+
+// Mark all the blocks that are in the loop with the given header.
+// Returns the number of blocks marked. Set *canOsr to true if the loop is
+// reachable from both the normal entry and the OSR entry.
+size_t
+jit::MarkLoopBlocks(MIRGraph &graph, MBasicBlock *header, bool *canOsr)
+{
+#ifdef DEBUG
+    for (ReversePostorderIterator i = graph.rpoBegin(), e = graph.rpoEnd(); i != e; ++i)
+        MOZ_ASSERT(!i->isMarked(), "Some blocks already marked");
+#endif
+
+    MBasicBlock *osrBlock = graph.osrBlock();
+    *canOsr = false;
+
+    // The blocks are in RPO; start at the loop backedge, which is marks the
+    // bottom of the loop, and walk up until we get to the header. Loops may be
+    // discontiguous, so we trace predecessors to determine which blocks are
+    // actually part of the loop. The backedge is always part of the loop, and
+    // so are its predecessors, transitively, up to the loop header or an OSR
+    // entry.
+    MBasicBlock *backedge = header->backedge();
+    backedge->mark();
+    size_t numMarked = 1;
+    for (PostorderIterator i = graph.poBegin(backedge); ; ++i) {
+        MOZ_ASSERT(i != graph.poEnd(),
+                   "Reached the end of the graph while searching for the loop header");
+        MBasicBlock *block = *i;
+        // A block not marked by the time we reach it is not in the loop.
+        if (!block->isMarked())
+            continue;
+        // If we've reached the loop header, we're done.
+        if (block == header)
+            break;
+        // This block is in the loop; trace to its predecessors.
+        for (size_t p = 0, e = block->numPredecessors(); p != e; ++p) {
+            MBasicBlock *pred = block->getPredecessor(p);
+            if (pred->isMarked())
+                continue;
+
+            // Blocks dominated by the OSR entry are not part of the loop
+            // (unless they aren't reachable from the normal entry).
+            if (osrBlock && pred != header && osrBlock->dominates(pred)) {
+                *canOsr = true;
+                continue;
+            }
+
+            MOZ_ASSERT(pred->id() >= header->id() && pred->id() <= backedge->id(),
+                       "Loop block not between loop header and loop backedge");
+
+            pred->mark();
+            ++numMarked;
+
+            // A nested loop may not exit back to the enclosing loop at its
+            // bottom. If we just marked its header, then the whole nested loop
+            // is part of the enclosing loop.
+            if (pred->isLoopHeader()) {
+                MBasicBlock *innerBackedge = pred->backedge();
+                if (!innerBackedge->isMarked()) {
+                    // Mark its backedge so that we add all of its blocks to the
+                    // outer loop as we walk upwards.
+                    innerBackedge->mark();
+                    ++numMarked;
+
+                    // If the nested loop is not contiguous, we may have already
+                    // passed its backedge. If this happens, back up.
+                    if (backedge->id() > block->id()) {
+                        i = graph.poBegin(innerBackedge);
+                        --i;
+                    }
+                }
+            }
+        }
+    }
+    MOZ_ASSERT(header->isMarked(), "Loop header should be part of the loop");
+    return numMarked;
+}
+
+// Unmark all the blocks that are in the loop with the given header.
+void
+jit::UnmarkLoopBlocks(MIRGraph &graph, MBasicBlock *header)
+{
+    MBasicBlock *backedge = header->backedge();
+    for (ReversePostorderIterator i = graph.rpoBegin(header); ; ++i) {
+        MOZ_ASSERT(i != graph.rpoEnd(),
+                   "Reached the end of the graph while searching for the backedge");
+        MBasicBlock *block = *i;
+        if (block->isMarked()) {
+            block->unmark();
+            if (block == backedge)
+                break;
+        }
+    }
+
+#ifdef DEBUG
+    for (ReversePostorderIterator i = graph.rpoBegin(), e = graph.rpoEnd(); i != e; ++i)
+        MOZ_ASSERT(!i->isMarked(), "Not all blocks got unmarked");
+#endif
+}
+
+// Reorder the blocks in the loop starting at the given header to be contiguous.
+static void
+MakeLoopContiguous(MIRGraph &graph, MBasicBlock *header, size_t numMarked)
+{
+    MBasicBlock *backedge = header->backedge();
+
+    MOZ_ASSERT(header->isMarked(), "Loop header is not part of loop");
+    MOZ_ASSERT(backedge->isMarked(), "Loop backedge is not part of loop");
+
+    // If there are any blocks between the loop header and the loop backedge
+    // that are not part of the loop, prepare to move them to the end. We keep
+    // them in order, which preserves RPO.
+    ReversePostorderIterator insertIter = graph.rpoBegin(backedge);
+    insertIter++;
+    MBasicBlock *insertPt = *insertIter;
+
+    // Visit all the blocks from the loop header to the loop backedge.
+    size_t headerId = header->id();
+    size_t inLoopId = headerId;
+    size_t notInLoopId = inLoopId + numMarked;
+    ReversePostorderIterator i = graph.rpoBegin(header);
+    for (;;) {
+        MBasicBlock *block = *i++;
+        MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(),
+                   "Loop backedge should be last block in loop");
+
+        if (block->isMarked()) {
+            // This block is in the loop.
+            block->unmark();
+            block->setId(inLoopId++);
+            // If we've reached the loop backedge, we're done!
+            if (block == backedge)
+                break;
+        } else {
+            // This block is not in the loop. Move it to the end.
+            graph.moveBlockBefore(insertPt, block);
+            block->setId(notInLoopId++);
+        }
+    }
+    MOZ_ASSERT(header->id() == headerId, "Loop header id changed");
+    MOZ_ASSERT(inLoopId == headerId + numMarked, "Wrong number of blocks kept in loop");
+    MOZ_ASSERT(notInLoopId == (insertIter != graph.rpoEnd() ? insertPt->id() : graph.numBlocks()),
+               "Wrong number of blocks moved out of loop");
+}
+
+// Reorder the blocks in the graph so that loops are contiguous.
+bool
+jit::MakeLoopsContiguous(MIRGraph &graph)
+{
+    // Visit all loop headers (in any order).
+    for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) {
+        MBasicBlock *header = *i;
+        if (!header->isLoopHeader())
+            continue;
+
+        // Mark all blocks that are actually part of the loop.
+        bool canOsr;
+        size_t numMarked = MarkLoopBlocks(graph, header, &canOsr);
+
+        // Move all blocks between header and backedge that aren't marked to
+        // the end of the loop, making the loop itself contiguous.
+        MakeLoopContiguous(graph, header, numMarked);
+    }
+
+    return true;
+}
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -26,16 +26,25 @@ SplitCriticalEdges(MIRGraph &graph);
 enum Observability {
     ConservativeObservability,
     AggressiveObservability
 };
 
 bool
 EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe);
 
+size_t
+MarkLoopBlocks(MIRGraph &graph, MBasicBlock *header, bool *canOsr);
+
+void
+UnmarkLoopBlocks(MIRGraph &graph, MBasicBlock *header);
+
+bool
+MakeLoopsContiguous(MIRGraph &graph);
+
 bool
 EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph);
 
 bool
 EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph);
 
 bool
 ApplyTypeInformation(MIRGenerator *mir, MIRGraph &graph);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -826,26 +826,16 @@ IonBuilder::buildInline(IonBuilder *call
         return false;
 
     current->setCallerResumePoint(callerResumePoint);
 
     // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock *predecessor = callerBuilder->current;
     JS_ASSERT(predecessor == callerResumePoint->block());
 
-    // All further instructions generated in from this scope should be
-    // considered as part of the function that we're inlining. We also need to
-    // keep track of the inlining depth because all scripts inlined on the same
-    // level contiguously have only one InlineExit node.
-    if (instrumentedProfiling()) {
-        predecessor->add(MProfilerStackOp::New(alloc(), script(),
-                                               MProfilerStackOp::InlineEnter,
-                                               inliningDepth_));
-    }
-
     predecessor->end(MGoto::New(alloc(), current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Initialize scope chain slot to Undefined.  It's set later by |initScopeChain|.
     MInstruction *scope = MConstant::New(alloc(), UndefinedValue());
     current->add(scope);
     current->initSlot(info().scopeChainSlot(), scope);
@@ -3696,19 +3686,18 @@ IonBuilder::processReturn(JSOp op)
         def = current->getSlot(info().returnValueSlot());
         break;
 
       default:
         def = nullptr;
         MOZ_ASSUME_UNREACHABLE("unknown return op");
     }
 
-    if (instrumentedProfiling()) {
-        current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit,
-                                           inliningDepth_));
+    if (instrumentedProfiling() && inliningDepth_ == 0) {
+        current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit));
     }
     MReturn *ret = MReturn::New(alloc(), def);
     current->end(ret);
 
     if (!graph().addReturn(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
@@ -4043,20 +4032,16 @@ IonBuilder::inlineScriptedCall(CallInfo 
 
     // Create return block.
     jsbytecode *postCall = GetNextPc(pc);
     MBasicBlock *returnBlock = newBlock(nullptr, postCall);
     if (!returnBlock)
         return false;
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
-    // When profiling add InlineExit instruction to indicate end of inlined function.
-    if (instrumentedProfiling())
-        returnBlock->add(MProfilerStackOp::New(alloc(), nullptr, MProfilerStackOp::InlineExit));
-
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     if (returns.empty()) {
         // Inlining of functions that have no exit is not supported.
         calleeScript->setUninlineable();
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1956,19 +1956,20 @@ MacroAssembler::spsMarkJit(SPSProfiler *
     push(temp); // +4: Did we push an sps frame.
     branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled);
 
     Label stackFull;
     // We always need the "safe" versions, because these are used in trampolines
     // and won't be regenerated when SPS state changes.
     spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
+    // Push a C++ frame with non-copy label
     storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfLabel()));
     storePtr(framePtr,              Address(temp, ProfileEntry::offsetOfSpOrScript()));
-    store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
+    store32(Imm32(0),               Address(temp, ProfileEntry::offsetOfLineOrPc()));
     store32(Imm32(ProfileEntry::IS_CPP_ENTRY), Address(temp, ProfileEntry::offsetOfFlags()));
 
     /* Always increment the stack size, whether or not we actually pushed. */
     bind(&stackFull);
     loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp);
     add32(Imm32(1), Address(temp, 0));
 
     bind(&spsNotEnabled);
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -1081,45 +1081,47 @@ class MacroAssembler : public MacroAssem
         bind(&stackFull);
     }
 
     // spsPushFrame variant for Ion-optimized scripts.
     void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, 0, temp, &stackFull);
 
+        // Push a JS frame with a copy label
         storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfLabel()));
         storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfSpOrScript()));
         store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
-        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
+        store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->sizePointer()), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
     // spsPushFrame variant for Baseline-optimized scripts.
     void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script,
                       Register temp, Register temp2)
     {
         Label stackFull;
         spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
+        // Push a JS frame with a copy label
         loadPtr(str, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfLabel()));
 
         loadPtr(script, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfSpOrScript()));
 
         // Store 0 for PCIdx because that's what interpreter does.
         // (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
         //  with 0 pcIdx).
         store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc()));
-        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
+        store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->addressOfSizePointer()), temp);
         loadPtr(Address(temp, 0), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
--- a/js/src/jit/LICM.cpp
+++ b/js/src/jit/LICM.cpp
@@ -1,382 +1,257 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/LICM.h"
 
-#include <stdio.h>
-
+#include "jit/IonAnalysis.h"
 #include "jit/IonSpewer.h"
-#include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 
 using namespace js;
 using namespace js::jit;
 
-namespace {
+// Test whether any instruction in the loop possiblyCalls().
+static bool
+LoopContainsPossibleCall(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge)
+{
+    for (auto i(graph.rpoBegin(header)); ; ++i) {
+        MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
+        MBasicBlock *block = *i;
+        if (!block->isMarked())
+            continue;
 
-typedef Vector<MInstruction*, 1, IonAllocPolicy> InstructionQueue;
+        for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) {
+            MInstruction *ins = *insIter;
+            if (ins->possiblyCalls()) {
+                IonSpew(IonSpew_LICM, "    Possile call found at %s%u", ins->opName(), ins->id());
+                return true;
+            }
+        }
 
-class Loop
+        if (block == backedge)
+            break;
+    }
+    return false;
+}
+
+// When a nested loop has no exits back into what would be its parent loop,
+// MarkLoopBlocks on the parent loop doesn't mark the blocks of the nested
+// loop, since they technically aren't part of the loop. However, AliasAnalysis
+// currently does consider such nested loops to be part of their parent
+// loops. Consequently, we can't use IsInLoop on dependency() values; we must
+// test whether a dependency() is *before* the loop, even if it is not
+// technically in the loop.
+static bool
+IsBeforeLoop(MDefinition *ins, MBasicBlock *header)
 {
-    MIRGenerator *mir;
+    return ins->block()->id() < header->id();
+}
+
+// Test whether the given instruction is inside the loop (and thus not
+// loop-invariant).
+static bool
+IsInLoop(MDefinition *ins)
+{
+    return ins->block()->isMarked();
+}
 
-  public:
-    // Loop code may return three values:
-    enum LoopReturn {
-        LoopReturn_Success,
-        LoopReturn_Error, // Compilation failure.
-        LoopReturn_Skip   // The loop is not suitable for LICM, but there is no error.
-    };
+// Test whether the given instruction is cheap and not worth hoisting unless
+// one of its users will be hoisted as well.
+static bool
+RequiresHoistedUse(const MDefinition *ins, bool hasCalls)
+{
+    if (ins->isConstantElements())
+        return true;
+
+    if (ins->isBox()) {
+        MOZ_ASSERT(!ins->toBox()->input()->isBox(),
+                "Box of a box could lead to unbounded recursion");
+        return true;
+    }
+
+    // Integer constants are usually cheap and aren't worth hoisting on their
+    // own, in general. Floating-point constants typically are worth hoisting,
+    // unless they'll end up being spilled (eg. due to a call).
+    if (ins->isConstant() && (!IsFloatingPointType(ins->type()) || hasCalls))
+        return true;
+
+    return false;
+}
+
+// Test whether the given instruction has any operands defined within the loop.
+static bool
+HasOperandInLoop(MInstruction *ins, bool hasCalls)
+{
+    // An instruction is only loop invariant if it and all of its operands can
+    // be safely hoisted into the loop preheader.
+    for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
+        MDefinition *op = ins->getOperand(i);
+
+        if (!IsInLoop(op))
+            continue;
+
+        if (RequiresHoistedUse(op, hasCalls)) {
+            // Recursively test for loop invariance. Note that the recursion is
+            // bounded because we require RequiresHoistedUse to be set at each
+            // level.
+            if (!HasOperandInLoop(op->toInstruction(), hasCalls))
+                continue;
+        }
+
+        return true;
+    }
+    return false;
+}
 
-  public:
-    // A loop is constructed on a backedge found in the control flow graph.
-    Loop(MIRGenerator *mir, MBasicBlock *footer);
+// Test whether the given instruction is hoistable, ignoring memory
+// dependencies.
+static bool
+IsHoistableIgnoringDependency(MInstruction *ins, bool hasCalls)
+{
+    return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist() &&
+           !HasOperandInLoop(ins, hasCalls);
+}
+
+// Test whether the given instruction has a memory dependency inside the loop.
+static bool
+HasDependencyInLoop(MInstruction *ins, MBasicBlock *header)
+{
+    // Don't hoist if this instruction depends on a store inside the loop.
+    if (MDefinition *dep = ins->dependency())
+        return !IsBeforeLoop(dep, header);
+    return false;
+}
 
-    // Initializes the loop, finds all blocks and instructions contained in the loop.
-    LoopReturn init();
+// Test whether the given instruction is hoistable.
+static bool
+IsHoistable(MInstruction *ins, MBasicBlock *header, bool hasCalls)
+{
+    return IsHoistableIgnoringDependency(ins, hasCalls) && !HasDependencyInLoop(ins, header);
+}
+
+// In preparation for hoisting an instruction, hoist any of its operands which
+// were too cheap to hoist on their own.
+static void
+MoveDeferredOperands(MInstruction *ins, MInstruction *hoistPoint, bool hasCalls)
+{
+    // If any of our operands were waiting for a user to be hoisted, make a note
+    // to hoist them.
+    for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
+        MDefinition *op = ins->getOperand(i);
+        if (!IsInLoop(op))
+            continue;
+        MOZ_ASSERT(RequiresHoistedUse(op, hasCalls),
+                   "Deferred loop-invariant operand is not cheap");
+        MInstruction *opIns = op->toInstruction();
+
+        // Recursively move the operands. Note that the recursion is bounded
+        // because we require RequiresHoistedUse to be set at each level.
+        MoveDeferredOperands(opIns, hoistPoint, hasCalls);
+
+        IonSpew(IonSpew_LICM, "    Hoisting %s%u (now that a user will be hoisted)",
+                opIns->opName(), opIns->id());
 
-    // Identifies hoistable loop invariant instructions and moves them out of the loop.
-    bool optimize();
+        opIns->block()->moveBefore(hoistPoint, opIns);
+    }
+}
+
+static void
+VisitLoopBlock(MBasicBlock *block, MBasicBlock *header, MInstruction *hoistPoint, bool hasCalls)
+{
+    for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ) {
+        MInstruction *ins = *insIter++;
+
+        if (!IsHoistable(ins, header, hasCalls)) {
+#ifdef DEBUG
+            if (IsHoistableIgnoringDependency(ins, hasCalls)) {
+                IonSpew(IonSpew_LICM, "    %s%u isn't hoistable due to dependency on %s%u",
+                        ins->opName(), ins->id(),
+                        ins->dependency()->opName(), ins->dependency()->id());
+            }
+#endif
+            continue;
+        }
 
-  private:
-    // These blocks define the loop.  header_ points to the loop header
-    MBasicBlock *header_;
+        // Don't hoist a cheap constant if it doesn't enable us to hoist one of
+        // its uses. We want those instructions as close as possible to their
+        // use, to minimize register pressure.
+        if (RequiresHoistedUse(ins, hasCalls)) {
+            IonSpew(IonSpew_LICM, "    %s%u will be hoisted only if its users are",
+                    ins->opName(), ins->id());
+            continue;
+        }
+
+        // Hoist operands which were too cheap to hoist on their own.
+        MoveDeferredOperands(ins, hoistPoint, hasCalls);
+
+        IonSpew(IonSpew_LICM, "    Hoisting %s%u", ins->opName(), ins->id());
 
-    // The pre-loop block is the first predecessor of the loop header.  It is where
-    // the loop is first entered and where hoisted instructions will be placed.
-    MBasicBlock* preLoop_;
+        // Move the instruction to the hoistPoint.
+        block->moveBefore(hoistPoint, ins);
+    }
+}
+
+static void
+VisitLoop(MIRGraph &graph, MBasicBlock *header)
+{
+    MInstruction *hoistPoint = header->loopPredecessor()->lastIns();
+
+    IonSpew(IonSpew_LICM, "  Visiting loop with header block%u, hoisting to %s%u",
+            header->id(), hoistPoint->opName(), hoistPoint->id());
+
+    MBasicBlock *backedge = header->backedge();
 
     // This indicates whether the loop contains calls or other things which
     // clobber most or all floating-point registers. In such loops,
     // floating-point constants should not be hoisted unless it enables further
     // hoisting.
-    bool containsPossibleCall_;
-
-    TempAllocator &alloc() const {
-        return mir->alloc();
-    }
-
-    bool hoistInstructions(InstructionQueue &toHoist);
-
-    // Utility methods for invariance testing and instruction hoisting.
-    bool isInLoop(MDefinition *ins);
-    bool isBeforeLoop(MDefinition *ins);
-    bool isLoopInvariant(MInstruction *ins);
-
-    // This method determines if this block hot within a loop.  That is, if it's
-    // always or usually run when the loop executes
-    bool checkHotness(MBasicBlock *block);
+    bool hasCalls = LoopContainsPossibleCall(graph, header, backedge);
 
-    // Worklist and worklist usage methods
-    InstructionQueue worklist_;
-    bool insertInWorklist(MInstruction *ins);
-    MInstruction* popFromWorklist();
-
-    inline bool isHoistable(const MDefinition *ins) const {
-        return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist();
-    }
+    for (auto i(graph.rpoBegin(header)); ; ++i) {
+        MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
+        MBasicBlock *block = *i;
+        if (!block->isMarked())
+            continue;
 
-    bool requiresHoistedUse(const MDefinition *ins) const;
-};
+        VisitLoopBlock(block, header, hoistPoint, hasCalls);
 
-} /* namespace anonymous */
-
-LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
-  : mir(mir), graph(graph)
-{
+        if (block == backedge)
+            break;
+    }
 }
 
 bool
-LICM::analyze()
+jit::LICM(MIRGenerator *mir, MIRGraph &graph)
 {
-    IonSpew(IonSpew_LICM, "Beginning LICM pass.");
+    IonSpew(IonSpew_LICM, "Beginning LICM pass");
 
-    // Iterate in RPO to visit outer loops before inner loops.
-    for (ReversePostorderIterator i(graph.rpoBegin()); i != graph.rpoEnd(); i++) {
+    // Iterate in RPO to visit outer loops before inner loops. We'd hoist the
+    // same things either way, but outer first means we do a little less work.
+    for (auto i(graph.rpoBegin()), e(graph.rpoEnd()); i != e; ++i) {
         MBasicBlock *header = *i;
-
-        // Skip non-headers and self-loops.
-        if (!header->isLoopHeader() || header->numPredecessors() < 2)
+        if (!header->isLoopHeader())
             continue;
 
-        // Attempt to optimize loop.
-        Loop loop(mir, header);
+        bool canOsr;
+        MarkLoopBlocks(graph, header, &canOsr);
 
-        Loop::LoopReturn lr = loop.init();
-        if (lr == Loop::LoopReturn_Error)
+        // Hoisting out of a loop that has an entry from the OSR block in
+        // addition to its normal entry is tricky. In theory we could clone
+        // the instruction and insert phis.
+        if (!canOsr)
+            VisitLoop(graph, header);
+        else
+            IonSpew(IonSpew_LICM, "  Skipping loop with header block%u due to OSR", header->id());
+
+        UnmarkLoopBlocks(graph, header);
+
+        if (mir->shouldCancel("LICM (main loop)"))
             return false;
-        if (lr == Loop::LoopReturn_Skip) {
-            graph.unmarkBlocks();
-            continue;
-        }
-
-        if (!loop.optimize())
-            return false;
-
-        graph.unmarkBlocks();
     }
 
     return true;
 }
-
-Loop::Loop(MIRGenerator *mir, MBasicBlock *header)
-  : mir(mir),
-    header_(header),
-    containsPossibleCall_(false),
-    worklist_(mir->alloc())
-{
-    preLoop_ = header_->getPredecessor(0);
-}
-
-Loop::LoopReturn
-Loop::init()
-{
-    IonSpew(IonSpew_LICM, "Loop identified, headed by block %d", header_->id());
-    IonSpew(IonSpew_LICM, "footer is block %d", header_->backedge()->id());
-
-    // The first predecessor of the loop header must dominate the header.
-    JS_ASSERT(header_->id() > header_->getPredecessor(0)->id());
-
-    // Loops from backedge to header and marks all visited blocks
-    // as part of the loop. At the same time add all hoistable instructions
-    // (in RPO order) to the instruction worklist.
-    Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist(alloc());
-    if (!inlooplist.append(header_->backedge()))
-        return LoopReturn_Error;
-    header_->backedge()->mark();
-
-    while (!inlooplist.empty()) {
-        MBasicBlock *block = inlooplist.back();
-
-        // Hoisting requires more finesse if the loop contains a block that
-        // self-dominates: there exists control flow that may enter the loop
-        // without passing through the loop preheader.
-        //
-        // Rather than perform a complicated analysis of the dominance graph,
-        // just return a soft error to ignore this loop.
-        if (block->immediateDominator() == block) {
-            while (!worklist_.empty())
-                popFromWorklist();
-            return LoopReturn_Skip;
-        }
-
-        // Add not yet visited predecessors to the inlooplist.
-        if (block != header_) {
-            for (size_t i = 0; i < block->numPredecessors(); i++) {
-                MBasicBlock *pred = block->getPredecessor(i);
-                if (pred->isMarked())
-                    continue;
-
-                if (!inlooplist.append(pred))
-                    return LoopReturn_Error;
-                pred->mark();
-            }
-        }
-
-        // If any block was added, process them first.
-        if (block != inlooplist.back())
-            continue;
-
-        // Add all instructions in this block (but the control instruction) to the worklist
-        for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
-            MInstruction *ins = *i;
-
-            // Remember whether this loop contains anything which clobbers most
-            // or all floating-point registers. This is just a rough heuristic.
-            if (ins->possiblyCalls())
-                containsPossibleCall_ = true;
-
-            if (isHoistable(ins)) {
-                if (!insertInWorklist(ins))
-                    return LoopReturn_Error;
-            }
-        }
-
-        // All successors of this block are visited.
-        inlooplist.popBack();
-    }
-
-    return LoopReturn_Success;
-}
-
-bool
-Loop::optimize()
-{
-    InstructionQueue invariantInstructions(alloc());
-
-    IonSpew(IonSpew_LICM, "These instructions are in the loop: ");
-
-    while (!worklist_.empty()) {
-        if (mir->shouldCancel("LICM (worklist)"))
-            return false;
-
-        MInstruction *ins = popFromWorklist();
-
-        IonSpewHeader(IonSpew_LICM);
-
-        if (IonSpewEnabled(IonSpew_LICM)) {
-            ins->printName(IonSpewFile);
-            fprintf(IonSpewFile, " <- ");
-            ins->printOpcode(IonSpewFile);
-            fprintf(IonSpewFile, ":  ");
-        }
-
-        if (isLoopInvariant(ins)) {
-            // Flag this instruction as loop invariant.
-            ins->setLoopInvariant();
-            if (!invariantInstructions.append(ins))
-                return false;
-
-            if (IonSpewEnabled(IonSpew_LICM))
-                fprintf(IonSpewFile, " Loop Invariant!\n");
-        }
-    }
-
-    if (!hoistInstructions(invariantInstructions))
-        return false;
-    return true;
-}
-
-bool
-Loop::requiresHoistedUse(const MDefinition *ins) const
-{
-    if (ins->isConstantElements() || ins->isBox())
-        return true;
-
-    // Integer constants can often be folded as immediates and aren't worth
-    // hoisting on their own, in general. Floating-point constants typically
-    // are worth hoisting, unless they'll end up being spilled (eg. due to a
-    // call).
-    if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
-        return true;
-
-    return false;
-}
-
-bool
-Loop::hoistInstructions(InstructionQueue &toHoist)
-{
-    // Iterate in post-order (uses before definitions)
-    for (int32_t i = toHoist.length() - 1; i >= 0; i--) {
-        MInstruction *ins = toHoist[i];
-
-        // Don't hoist a cheap constant if it doesn't enable us to hoist one of
-        // its uses. We want those instructions as close as possible to their
-        // use, to facilitate folding and minimize register pressure.
-        if (requiresHoistedUse(ins)) {
-            bool loopInvariantUse = false;
-            for (MUseDefIterator use(ins); use; use++) {
-                if (use.def()->isLoopInvariant()) {
-                    loopInvariantUse = true;
-                    break;
-                }
-            }
-
-            if (!loopInvariantUse)
-                ins->setNotLoopInvariant();
-        }
-    }
-
-    // Move all instructions to the preLoop_ block just before the control instruction.
-    for (size_t i = 0; i < toHoist.length(); i++) {
-        MInstruction *ins = toHoist[i];
-
-        // Loads may have an implicit dependency on either stores (effectful instructions) or
-        // control instructions so we should never move these.
-        JS_ASSERT(!ins->isControlInstruction());
-        JS_ASSERT(!ins->isEffectful());
-        JS_ASSERT(ins->isMovable());
-
-        if (!ins->isLoopInvariant())
-            continue;
-
-        if (checkHotness(ins->block())) {
-            ins->block()->moveBefore(preLoop_->lastIns(), ins);
-            ins->setNotLoopInvariant();
-        }
-    }
-
-    return true;
-}
-
-bool
-Loop::isInLoop(MDefinition *ins)
-{
-    return ins->block()->isMarked();
-}
-
-bool
-Loop::isBeforeLoop(MDefinition *ins)
-{
-    return ins->block()->id() < header_->id();
-}
-
-bool
-Loop::isLoopInvariant(MInstruction *ins)
-{
-    if (!isHoistable(ins)) {
-        if (IonSpewEnabled(IonSpew_LICM))
-            fprintf(IonSpewFile, "not hoistable\n");
-        return false;
-    }
-
-    // Don't hoist if this instruction depends on a store inside or after the loop.
-    // Note: "after the loop" can sound strange, but Alias Analysis doesn't look
-    // at the control flow. Therefore it doesn't match the definition here, that a block
-    // is in the loop when there is a (directed) path from the block to the loop header.
-    if (ins->dependency() && !isBeforeLoop(ins->dependency())) {
-        if (IonSpewEnabled(IonSpew_LICM)) {
-            fprintf(IonSpewFile, "depends on store inside or after loop: ");
-            ins->dependency()->printName(IonSpewFile);
-            fprintf(IonSpewFile, "\n");
-        }
-        return false;
-    }
-
-    // An instruction is only loop invariant if it and all of its operands can
-    // be safely hoisted into the loop preheader.
-    for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
-        if (isInLoop(ins->getOperand(i)) &&
-            !ins->getOperand(i)->isLoopInvariant()) {
-
-            if (IonSpewEnabled(IonSpew_LICM)) {
-                ins->getOperand(i)->printName(IonSpewFile);
-                fprintf(IonSpewFile, " is in the loop.\n");
-            }
-
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool
-Loop::checkHotness(MBasicBlock *block)
-{
-    // TODO: determine if instructions within this block are worth hoisting.
-    // They might not be if the block is cold enough within the loop.
-    // BUG 669517
-    return true;
-}
-
-bool
-Loop::insertInWorklist(MInstruction *ins)
-{
-    if (!worklist_.insert(worklist_.begin(), ins))
-        return false;
-    ins->setInWorklist();
-    return true;
-}
-
-MInstruction*
-Loop::popFromWorklist()
-{
-    MInstruction* toReturn = worklist_.popCopy();
-    toReturn->setNotInWorklist();
-    return toReturn;
-}
--- a/js/src/jit/LICM.h
+++ b/js/src/jit/LICM.h
@@ -10,22 +10,14 @@
 // This file represents the Loop Invariant Code Motion optimization pass
 
 namespace js {
 namespace jit {
 
 class MIRGenerator;
 class MIRGraph;
 
-class LICM
-{
-    MIRGenerator *mir;
-    MIRGraph &graph;
-
-  public:
-    LICM(MIRGenerator *mir, MIRGraph &graph);
-    bool analyze();
-};
+bool LICM(MIRGenerator *mir, MIRGraph &graph);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_LICM_h */
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5731,20 +5731,16 @@ class LProfilerStackOp : public LInstruc
 
     JSScript *script() {
         return mir_->toProfilerStackOp()->script();
     }
 
     MProfilerStackOp::Type type() {
         return mir_->toProfilerStackOp()->type();
     }
-
-    unsigned inlineLevel() {
-        return mir_->toProfilerStackOp()->inlineLevel();
-    }
 };
 
 class LIsCallable : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(IsCallable);
     explicit LIsCallable(const LAllocation &object) {
         setOperand(0, object);
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -693,31 +693,36 @@ class LInstruction
 };
 
 class LInstructionVisitor
 {
     LInstruction *ins_;
 
   protected:
     jsbytecode *lastPC_;
+    jsbytecode *lastNotInlinedPC_;
 
     LInstruction *instruction() {
         return ins_;
     }
 
   public:
     void setInstruction(LInstruction *ins) {
         ins_ = ins;
-        if (ins->mirRaw())
+        if (ins->mirRaw()) {
             lastPC_ = ins->mirRaw()->trackedPc();
+            if (ins->mirRaw()->trackedTree())
+                lastNotInlinedPC_ = ins->mirRaw()->profilerLeavePc();
+        }
     }
 
     LInstructionVisitor()
       : ins_(nullptr),
-        lastPC_(nullptr)
+        lastPC_(nullptr),
+        lastNotInlinedPC_(nullptr)
     {}
 
   public:
 #define VISIT_INS(op) virtual bool visit##op(L##op *) { MOZ_ASSUME_UNREACHABLE("NYI: " #op); }
     LIR_OPCODE_LIST(VISIT_INS)
 #undef VISIT_INS
 };
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -55,17 +55,16 @@ MIRType MIRTypeFromValue(const js::Value
         }
     }
     return MIRTypeFromValueType(vp.extractNonDoubleType());
 }
 
 #define MIR_FLAG_LIST(_)                                                        \
     _(InWorklist)                                                               \
     _(EmittedAtUses)                                                            \
-    _(LoopInvariant)                                                            \
     _(Commutative)                                                              \
     _(Movable)       /* Allow LICM and GVN to move this instruction */          \
     _(Lowered)       /* (Debug only) has a virtual register */                  \
     _(Guard)         /* Not removable if uses == 0 */                           \
                                                                                 \
     /* Keep the flagged instruction in resume points and do not substitute this
      * instruction by an UndefinedValue. This might be used by call inlining
      * when a function argument is not used by the inlined instructions.
@@ -729,21 +728,25 @@ class MUseDefIterator
     explicit MUseDefIterator(MDefinition *def)
       : def_(def),
         current_(search(def->usesBegin()))
     { }
 
     operator bool() const {
         return current_ != def_->usesEnd();
     }
+    MUseDefIterator operator ++() {
+        JS_ASSERT(current_ != def_->usesEnd());
+        ++current_;
+        current_ = search(current_);
+        return *this;
+    }
     MUseDefIterator operator ++(int) {
         MUseDefIterator old(*this);
-        if (current_ != def_->usesEnd())
-            current_++;
-        current_ = search(current_);
+        operator++();
         return old;
     }
     MUse *use() const {
         return *current_;
     }
     MDefinition *def() const {
         return current_->consumer()->toDefinition();
     }
@@ -9719,57 +9722,45 @@ class MNewStringObject :
 // Node that represents that a script has begun executing. This comes at the
 // start of the function and is called once per function (including inline
 // ones)
 class MProfilerStackOp : public MNullaryInstruction
 {
   public:
     enum Type {
         Enter,        // a function has begun executing and it is not inline
-        Exit,         // any function has exited (inlined or normal)
-        InlineEnter,  // an inline function has begun executing
-
-        InlineExit    // all instructions of an inline function are done, a
-                      // return from the inline function could have occurred
-                      // before this boundary
+        Exit          // any function has exited and is not inline
     };
 
   private:
     JSScript *script_;
     Type type_;
-    unsigned inlineLevel_;
-
-    MProfilerStackOp(JSScript *script, Type type, unsigned inlineLevel)
-      : script_(script), type_(type), inlineLevel_(inlineLevel)
-    {
-        JS_ASSERT_IF(type != InlineExit, script != nullptr);
-        JS_ASSERT_IF(type == InlineEnter, inlineLevel != 0);
+
+    MProfilerStackOp(JSScript *script, Type type)
+      : script_(script), type_(type)
+    {
+        JS_ASSERT(script);
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(ProfilerStackOp)
 
-    static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type,
-                                  unsigned inlineLevel = 0) {
-        return new(alloc) MProfilerStackOp(script, type, inlineLevel);
+    static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type) {
+        return new(alloc) MProfilerStackOp(script, type);
     }
 
     JSScript *script() {
         return script_;
     }
 
     Type type() {
         return type_;
     }
 
-    unsigned inlineLevel() {
-        return inlineLevel_;
-    }
-
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 // This is an alias for MLoadFixedSlot.
 class MEnclosingScope : public MLoadFixedSlot
 {
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -686,17 +686,19 @@ void
 MBasicBlock::moveBefore(MInstruction *at, MInstruction *ins)
 {
     // Remove |ins| from the current block.
     JS_ASSERT(ins->block() == this);
     instructions_.remove(ins);
 
     // Insert into new block, which may be distinct.
     // Uses and operands are untouched.
-    at->block()->insertBefore(at, ins);
+    ins->setBlock(at->block());
+    at->block()->instructions_.insertBefore(at, ins);
+    ins->setTrackedSite(at->trackedSite());
 }
 
 static inline void
 AssertSafelyDiscardable(MDefinition *def)
 {
 #ifdef DEBUG
     // Instructions captured by resume points cannot be safely discarded, since
     // they are necessary for interpreter frame reconstruction in case of bailout.
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -356,19 +356,24 @@ class MBasicBlock : public TempObject, p
     }
     void setStackDepth(uint32_t depth) {
         stackPosition_ = depth;
     }
     bool isMarked() const {
         return mark_;
     }
     void mark() {
+        MOZ_ASSERT(!mark_, "Marking already-marked block");
+        markUnchecked();
+    }
+    void markUnchecked() {
         mark_ = true;
     }
     void unmark() {
+        MOZ_ASSERT(mark_, "Unarking unmarked block");
         mark_ = false;
     }
     void makeStart(MStart *start) {
         add(start);
         start_ = start;
     }
     MStart *start() const {
         return start_;
@@ -598,16 +603,19 @@ class MIRGraph
         return blocks_.begin(at);
     }
     MBasicBlockIterator end() {
         return blocks_.end();
     }
     PostorderIterator poBegin() {
         return blocks_.rbegin();
     }
+    PostorderIterator poBegin(MBasicBlock *at) {
+        return blocks_.rbegin(at);
+    }
     PostorderIterator poEnd() {
         return blocks_.rend();
     }
     ReversePostorderIterator rpoBegin() {
         return blocks_.begin();
     }
     ReversePostorderIterator rpoBegin(MBasicBlock *at) {
         return blocks_.begin(at);
@@ -617,16 +625,21 @@ class MIRGraph
     }
     void removeBlocksAfter(MBasicBlock *block);
     void removeBlock(MBasicBlock *block);
     void moveBlockToEnd(MBasicBlock *block) {
         JS_ASSERT(block->id());
         blocks_.remove(block);
         blocks_.pushBack(block);
     }
+    void moveBlockBefore(MBasicBlock *at, MBasicBlock *block) {
+        JS_ASSERT(block->id());
+        blocks_.remove(block);
+        blocks_.insertBefore(at, block);
+    }
     size_t numBlocks() const {
         return numBlocks_;
     }
     uint32_t numBlockIds() const {
         return blockIdGen_;
     }
     void allocDefinitionId(MDefinition *ins) {
         ins->setId(idGen_++);
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -370,17 +370,17 @@ ParallelSafetyAnalysis::analyze()
                     SpewMIR(instr, "Unaccepted");
                     return false;
                 }
             }
 
             if (!visitor.unsafe()) {
                 // Block consists of only safe instructions.  Visit its successors.
                 for (uint32_t i = 0; i < block->numSuccessors(); i++)
-                    block->getSuccessor(i)->mark();
+                    block->getSuccessor(i)->markUnchecked();
             } else {
                 // Block contains an unsafe instruction.  That means that once
                 // we enter this block, we are guaranteed to bailout.
 
                 // If this is the entry block, then there is no point
                 // in even trying to execute this function as it will
                 // always bailout.
                 if (*block == graph_.entryBlock()) {
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1532,62 +1532,31 @@ MRandom::computeRange(TempAllocator &all
     setRange(Range::NewDoubleRange(alloc, 0.0, 1.0));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Range Analysis
 ///////////////////////////////////////////////////////////////////////////////
 
 bool
-RangeAnalysis::markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *backedge)
-{
-    Vector<MBasicBlock *, 16, IonAllocPolicy> worklist(alloc());
-
-    // Mark the header as being in the loop. This terminates the walk.
-    header->mark();
-
-    backedge->mark();
-    if (!worklist.append(backedge))
-        return false;
-
-    // If we haven't reached the loop header yet, walk up the predecessors
-    // we haven't seen already.
-    while (!worklist.empty()) {
-        MBasicBlock *current = worklist.popCopy();
-        for (size_t i = 0; i < current->numPredecessors(); i++) {
-            MBasicBlock *pred = current->getPredecessor(i);
-
-            if (pred->isMarked())
-                continue;
-
-            pred->mark();
-            if (!worklist.append(pred))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-bool
 RangeAnalysis::analyzeLoop(MBasicBlock *header)
 {
     JS_ASSERT(header->hasUniqueBackedge());
 
     // Try to compute an upper bound on the number of times the loop backedge
     // will be taken. Look for tests that dominate the backedge and which have
     // an edge leaving the loop body.
     MBasicBlock *backedge = header->backedge();
 
     // Ignore trivial infinite loops.
     if (backedge == header)
         return true;
 
-    if (!markBlocksInLoopBody(header, backedge))
-        return false;
+    bool canOsr;
+    MarkLoopBlocks(graph_, header, &canOsr);
 
     LoopIterationBound *iterationBound = nullptr;
 
     MBasicBlock *block = backedge;
     do {
         BranchDirection direction;
         MTest *branch = block->immediateDominatorBranch(&direction);
 
@@ -1604,17 +1573,17 @@ RangeAnalysis::analyzeLoop(MBasicBlock *
                     analyzeLoopIterationCount(header, branch, direction);
                 if (iterationBound)
                     break;
             }
         }
     } while (block != header);
 
     if (!iterationBound) {
-        graph_.unmarkBlocks();
+        UnmarkLoopBlocks(graph_, header);
         return true;
     }
 
 #ifdef DEBUG
     if (IonSpewEnabled(IonSpew_Range)) {
         Sprinter sp(GetIonContext()->cx);
         sp.init();
         iterationBound->sum.print(sp);
@@ -1657,17 +1626,17 @@ RangeAnalysis::analyzeLoop(MBasicBlock *
         // one of the bounds checks we just added.
         for (size_t i = 0; i < hoistedChecks.length(); i++) {
             MBoundsCheck *ins = hoistedChecks[i];
             ins->replaceAllUsesWith(ins->index());
             ins->block()->discard(ins);
         }
     }
 
-    graph_.unmarkBlocks();
+    UnmarkLoopBlocks(graph_, header);
     return true;
 }
 
 LoopIterationBound *
 RangeAnalysis::analyzeLoopIterationCount(MBasicBlock *header,
                                          MTest *test, BranchDirection direction)
 {
     SimpleLinearSum lhs(nullptr, 0);
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -100,17 +100,16 @@ class RangeAnalysis
     bool truncate();
 
   private:
     bool analyzeLoop(MBasicBlock *header);
     LoopIterationBound *analyzeLoopIterationCount(MBasicBlock *header,
                                                   MTest *test, BranchDirection direction);
     void analyzeLoopPhi(MBasicBlock *header, LoopIterationBound *loopBound, MPhi *phi);
     bool tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins);
-    bool markBlocksInLoopBody(MBasicBlock *header, MBasicBlock *current);
 };
 
 class Range : public TempObject {
   public:
     // Int32 are signed. INT32_MAX is pow(2,31)-1 and INT32_MIN is -pow(2,31),
     // so the greatest exponent we need is 31.
     static const uint16_t MaxInt32Exponent = 31;
 
--- a/js/src/jit/RegisterAllocator.h
+++ b/js/src/jit/RegisterAllocator.h
@@ -133,17 +133,17 @@ struct AllocationIntegrityState
 // this instruction, it can be clobbered in the output". Just having ranges
 // of instruction IDs is insufficiently expressive to denote all possibilities.
 // This class solves this issue by associating an extra bit with the instruction
 // ID which indicates whether the position is the input half or output half of
 // an instruction.
 class CodePosition
 {
   private:
-    MOZ_CONSTEXPR explicit CodePosition(const uint32_t &bits)
+    MOZ_CONSTEXPR explicit CodePosition(uint32_t bits)
       : bits_(bits)
     { }
 
     static const unsigned int INSTRUCTION_SHIFT = 1;
     static const unsigned int SUBPOSITION_MASK = 1;
     uint32_t bits_;
 
   public:
@@ -173,37 +173,37 @@ class CodePosition
     uint32_t pos() const {
         return bits_;
     }
 
     SubPosition subpos() const {
         return (SubPosition)(bits_ & SUBPOSITION_MASK);
     }
 
-    bool operator <(const CodePosition &other) const {
+    bool operator <(CodePosition other) const {
         return bits_ < other.bits_;
     }
 
-    bool operator <=(const CodePosition &other) const {
+    bool operator <=(CodePosition other) const {
         return bits_ <= other.bits_;
     }
 
-    bool operator !=(const CodePosition &other) const {
+    bool operator !=(CodePosition other) const {
         return bits_ != other.bits_;
     }
 
-    bool operator ==(const CodePosition &other) const {
+    bool operator ==(CodePosition other) const {
         return bits_ == other.bits_;
     }
 
-    bool operator >(const CodePosition &other) const {
+    bool operator >(CodePosition other) const {
         return bits_ > other.bits_;
     }
 
-    bool operator >=(const CodePosition &other) const {
+    bool operator >=(CodePosition other) const {
         return bits_ >= other.bits_;
     }
 
     CodePosition previous() const {
         JS_ASSERT(*this != MIN);
         return CodePosition(bits_ - 1);
     }
     CodePosition next() const {
@@ -263,17 +263,17 @@ class InstructionDataMap
         insData_ = gen->allocate<InstructionData>(numInstructions);
         numIns_ = numInstructions;
         if (!insData_)
             return false;
         memset(insData_, 0, sizeof(InstructionData) * numInstructions);
         return true;
     }
 
-    InstructionData &operator[](const CodePosition &pos) {
+    InstructionData &operator[](CodePosition pos) {
         JS_ASSERT(pos.ins() < numIns_);
         return insData_[pos.ins()];
     }
     InstructionData &operator[](LInstruction *ins) {
         JS_ASSERT(ins->id() < numIns_);
         return insData_[ins->id()];
     }
     InstructionData &operator[](uint32_t ins) {
--- a/js/src/jit/UnreachableCodeElimination.cpp
+++ b/js/src/jit/UnreachableCodeElimination.cpp
@@ -169,17 +169,17 @@ UnreachableCodeElimination::prunePointle
         MBasicBlock *osrBlock = graph_.osrBlock();
         JS_ASSERT(!osrBlock->isMarked());
         if (!enqueue(osrBlock, worklist))
             return false;
         for (size_t i = 0; i < osrBlock->numSuccessors(); i++) {
             if (!osrBlock->getSuccessor(i)->isMarked()) {
                 // OSR block has an otherwise unreachable successor, abort.
                 for (MBasicBlockIterator iter(graph_.begin()); iter != graph_.end(); iter++)
-                    iter->mark();
+                    iter->markUnchecked();
                 marked_ = graph_.numBlocks();
                 return true;
             }
         }
     }
 
     // Now that we know we will not abort due to OSR, go back and
     // transform any tests on constant operands into gotos.
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -44,17 +44,17 @@ CodeGeneratorShared::CodeGeneratorShared
     current(nullptr),
     snapshots_(),
     recovers_(),
     deoptTable_(nullptr),
 #ifdef DEBUG
     pushedArgs_(0),
 #endif
     lastOsiPointOffset_(0),
-    sps_(&GetIonContext()->runtime->spsProfiler(), &lastPC_),
+    sps_(&GetIonContext()->runtime->spsProfiler(), &lastNotInlinedPC_),
     osrEntryOffset_(0),
     skipArgCheckEntryOffset_(0),
     frameDepth_(graph->paddedLocalSlotsSize() + graph->argumentsSize()),
     frameInitialAdjustment_(0)
 {
     if (!gen->compilingAsmJS())
         masm.setInstrumentation(&sps_);
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -352,17 +352,17 @@ MSG_DEF(JSMSG_FUNCTION_ARGUMENTS_AND_RES
 MSG_DEF(JSMSG_REST_WITH_DEFAULT,      299, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
 MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
 MSG_DEF(JSMSG_YIELD_IN_DEFAULT,       301, 0, JSEXN_SYNTAXERR, "yield in default expression")
 MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED,  302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}")
 MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA, 303, 2, JSEXN_ERR,      "{0} is being assigned a {1}, but already has one")
 MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG,      304, 0, JSEXN_RANGEERR, "invalid parallel method argument")
 MSG_DEF(JSMSG_REGEXP_RUNTIME_ERROR,   305, 0, JSEXN_INTERNALERR, "an error occurred while executing regular expression")
 MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT,    306, 0, JSEXN_ERR, "variable has been optimized out")
-MSG_DEF(JSMSG_UNUSED307,              307, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE,307, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds")
 MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE,   310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
 MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE,    311, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
 MSG_DEF(JSMSG_CANT_REPORT_NEW,        312, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
 MSG_DEF(JSMSG_CANT_REPORT_INVALID,    313, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
 MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC,   314, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
 MSG_DEF(JSMSG_CANT_DEFINE_NEW,        315, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3626,17 +3626,17 @@ JS_SetUCProperty(JSContext *cx, HandleOb
 
 JS_PUBLIC_API(bool)
 JS_DeletePropertyById2(JSContext *cx, HandleObject obj, HandleId id, bool *result)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
 
-    return JSObject::deleteByValue(cx, obj, IdToValue(id), result);
+    return JSObject::deleteGeneric(cx, obj, id, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeleteElement2(JSContext *cx, HandleObject obj, uint32_t index, bool *result)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
@@ -3648,30 +3648,32 @@ JS_PUBLIC_API(bool)
 JS_DeleteProperty2(JSContext *cx, HandleObject obj, const char *name, bool *result)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
-    return JSObject::deleteByValue(cx, obj, StringValue(atom), result);
+    RootedId id(cx, AtomToId(atom));
+    return JSObject::deleteGeneric(cx, obj, id, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeleteUCProperty2(JSContext *cx, HandleObject obj, const jschar *name, size_t namelen,
                      bool *result)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
-    return JSObject::deleteByValue(cx, obj, StringValue(atom), result);
+    RootedId id(cx, AtomToId(atom));
+    return JSObject::deleteGeneric(cx, obj, id, result);
 }
 
 JS_PUBLIC_API(bool)
 JS_DeletePropertyById(JSContext *cx, HandleObject obj, HandleId id)
 {
     bool junk;
     return JS_DeletePropertyById2(cx, obj, id, &junk);
 }
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -332,20 +332,20 @@ DeleteArrayElement(JSContext *cx, Handle
                     return false;
             }
         }
 
         *succeeded = true;
         return true;
     }
 
-    if (index <= UINT32_MAX)
-        return JSObject::deleteElement(cx, obj, uint32_t(index), succeeded);
-
-    return JSObject::deleteByValue(cx, obj, DoubleValue(index), succeeded);
+    RootedId id(cx);
+    if (!ToId(cx, index, &id))
+        return false;
+    return JSObject::deleteGeneric(cx, obj, id, succeeded);
 }
 
 /* ES6 20130308 draft 9.3.5 */
 static bool
 DeletePropertyOrThrow(JSContext *cx, HandleObject obj, double index)
 {
     bool succeeded;
     if (!DeleteArrayElement(cx, obj, index, &succeeded))
@@ -1135,17 +1135,17 @@ array_toString(JSContext *cx, unsigned a
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     RootedValue join(cx, args.calleev());
     if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join))
         return false;
 
-    if (!js_IsCallable(join)) {
+    if (!IsCallable(join)) {
         JSString *str = JS_BasicObjectToString(cx, obj);
         if (!str)
             return false;
         args.rval().setString(str);
         return true;
     }
 
     InvokeArgs args2(cx);
@@ -2987,16 +2987,17 @@ static const JSFunctionSpec array_static
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
     JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
     JS_SELF_HOSTED_FN("map",         "ArrayStaticMap",   2,0),
     JS_SELF_HOSTED_FN("every",       "ArrayStaticEvery", 2,0),
     JS_SELF_HOSTED_FN("some",        "ArrayStaticSome",  2,0),
     JS_SELF_HOSTED_FN("reduce",      "ArrayStaticReduce", 2,0),
     JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
+    JS_SELF_HOSTED_FN("from",        "ArrayFrom", 3,0),
     JS_FN("of",                 array_of,           0,0),
 
 #ifdef ENABLE_PARALLEL_JS
     JS_SELF_HOSTED_FN("build",       "ArrayStaticBuild", 2,0),
     /* Parallelizable and pure static methods. */
     JS_SELF_HOSTED_FN("buildPar",    "ArrayStaticBuildPar", 3,0),
 #endif
 
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -360,23 +360,16 @@ AtomizeAndCopyChars(ExclusiveContext *cx
          return s;
 
     AtomHasher::Lookup lookup(tbchars, length);
 
     AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
     if (pp)
         return pp->asPtr();
 
-    /*
-     * If a GC occurs at js_NewStringCopy then |p| will still have the correct
-     * hash, allowing us to avoid rehashing it. Even though the hash is
-     * unchanged, we need to re-lookup the table position because a last-ditch
-     * GC will potentially free some table entries.
-     */
-
     AutoLockForExclusiveAccess lock(cx);
 
     AtomSet& atoms = cx->atoms();
     AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
     if (p) {
         JSAtom *atom = p->asPtr();
         p->setTagged(bool(ib));
         return atom;
@@ -387,17 +380,20 @@ AtomizeAndCopyChars(ExclusiveContext *cx
     JSFlatString *flat = js_NewStringCopyN<NoGC>(cx, tbchars, length);
     if (!flat) {
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 
     JSAtom *atom = flat->morphAtomizedStringIntoAtom();
 
-    if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
+    // We have held the lock since looking up p, and the operations we've done
+    // since then can't GC; therefore the atoms table has not been modified and
+    // p is still valid.
+    if (!atoms.add(p, AtomStateEntry(atom, bool(ib)))) {
         js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
         return nullptr;
     }
 
     return atom;
 }
 
 JSAtom *
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1011,17 +1011,17 @@ class ContextAllocPolicy
 bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ToInteger(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ToString(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp);
 
 bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp);
-bool intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp);
+bool intrinsic_DefineDataProperty(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_UnsafeGetReservedSlot(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_HaveSameClass(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsPackedArray(JSContext *cx, unsigned argc, Value *vp);
 
 bool intrinsic_ShouldForceSequential(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_NewParallelArray(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_ForkJoinGetSlice(JSContext *cx, unsigned argc, Value *vp);
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2474,17 +2474,17 @@ date_toJSON(JSContext *cx, unsigned argc
     }
 
     /* Step 4. */
     RootedValue toISO(cx);
     if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
         return false;
 
     /* Step 5. */
-    if (!js_IsCallable(toISO)) {
+    if (!IsCallable(toISO)) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
                                      JSMSG_BAD_TOISOSTRING_PROP);
         return false;
     }
 
     /* Step 6. */
     InvokeArgs args2(cx);
     if (!args2.init(0))
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -278,18 +278,17 @@ namespace js {
             js::proxy_GetGeneric,                                                       \
             js::proxy_GetProperty,                                                      \
             js::proxy_GetElement,                                                       \
             js::proxy_SetGeneric,                                                       \
             js::proxy_SetProperty,                                                      \
             js::proxy_SetElement,                                                       \
             js::proxy_GetGenericAttributes,                                             \
             js::proxy_SetGenericAttributes,                                             \
-            js::proxy_DeleteProperty,                                                   \
-            js::proxy_DeleteElement,                                                    \
+            js::proxy_DeleteGeneric,                                                    \
             js::proxy_Watch, js::proxy_Unwatch,                                         \
             js::proxy_Slice,                                                            \
             nullptr,             /* enumerate       */                                  \
             nullptr,             /* thisObject      */                                  \
         }                                                                               \
     }
 
 #define PROXY_CLASS_DEF(name, extraSlots, flags, callOp, constructOp)   \
@@ -344,20 +343,17 @@ proxy_SetProperty(JSContext *cx, JS::Han
 extern JS_FRIEND_API(bool)
 proxy_SetElement(JSContext *cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp,
                  bool strict);
 extern JS_FRIEND_API(bool)
 proxy_GetGenericAttributes(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
 extern JS_FRIEND_API(bool)
 proxy_SetGenericAttributes(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
 extern JS_FRIEND_API(bool)
-proxy_DeleteProperty(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
-                     bool *succeeded);
-extern JS_FRIEND_API(bool)
-proxy_DeleteElement(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *succeeded);
+proxy_DeleteGeneric(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
 
 extern JS_FRIEND_API(void)
 proxy_Trace(JSTracer *trc, JSObject *obj);
 extern JS_FRIEND_API(JSObject *)
 proxy_WeakmapKeyDelegate(JSObject *obj);
 extern JS_FRIEND_API(bool)
 proxy_Convert(JSContext *cx, JS::HandleObject proxy, JSType hint, JS::MutableHandleValue vp);
 extern JS_FRIEND_API(void)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1045,17 +1045,17 @@ fun_toSource(JSContext *cx, unsigned arg
 #endif
 
 bool
 js_fun_call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     HandleValue fval = args.thisv();
-    if (!js_IsCallable(fval)) {
+    if (!IsCallable(fval)) {
         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
         return false;
     }
 
     args.setCallee(fval);
     args.setThis(args.get(0));
 
     if (args.length() > 0) {
@@ -1070,17 +1070,17 @@ js_fun_call(JSContext *cx, unsigned argc
 // ES5 15.3.4.3
 bool
 js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     HandleValue fval = args.thisv();
-    if (!js_IsCallable(fval)) {
+    if (!IsCallable(fval)) {
         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
         return false;
     }
 
     // Step 2.
     if (args.length() < 2 || args[1].isNullOrUndefined())
         return js_fun_call(cx, (args.length() > 0) ? 1 : 0, vp);
 
@@ -1424,17 +1424,17 @@ static bool
 fun_bind(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     Value thisv = args.thisv();
 
     /* Step 2. */
-    if (!js_IsCallable(thisv)) {
+    if (!IsCallable(thisv)) {
         ReportIncompatibleMethod(cx, args, &JSFunction::class_);
         return false;
     }
 
     /* Step 3. */
     Value *boundArgs = nullptr;
     unsigned argslen = 0;
     if (args.length() > 1) {
@@ -1938,33 +1938,16 @@ js::DefineFunction(JSContext *cx, Handle
 
     RootedValue funVal(cx, ObjectValue(*fun));
     if (!JSObject::defineGeneric(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK))
         return nullptr;
 
     return fun;
 }
 
-bool
-js::IsConstructor(const Value &v)
-{
-    // Step 2.
-    if (!v.isObject())
-        return false;
-
-    // Step 3-4, a bit complex for us, since we have several flavors of
-    // [[Construct]] internal method.
-    JSObject &obj = v.toObject();
-    if (obj.is<JSFunction>()) {
-        JSFunction &fun = obj.as<JSFunction>();
-        return fun.isNativeConstructor() || fun.isInterpretedConstructor();
-    }
-    return obj.getClass()->construct != nullptr;
-}
-
 void
 js::ReportIncompatibleMethod(JSContext *cx, CallReceiver call, const Class *clasp)
 {
     RootedValue thisv(cx, call.thisv());
 
 #ifdef DEBUG
     if (thisv.isObject()) {
         JS_ASSERT(thisv.toObject().getClass() != clasp ||
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -527,19 +527,16 @@ DefineFunction(JSContext *cx, HandleObje
                NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name);
 
 extern bool
 fun_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp);
 
-// ES6 9.2.5 IsConstructor
-bool IsConstructor(const Value &v);
-
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough do that efficient
  * storage is required (no malloc'ed reserved slots).
  */
 class FunctionExtended : public JSFunction
 {
   public:
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1062,18 +1062,20 @@ js::DefineProperty(JSContext *cx, Handle
 {
     if (obj->is<ArrayObject>()) {
         Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
         return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval);
     }
 
     if (obj->getOps()->lookupGeneric) {
         if (obj->is<ProxyObject>()) {
-            RootedValue pd(cx, desc.descriptorValue());
-            return Proxy::defineProperty(cx, obj, id, pd);
+            Rooted<PropertyDescriptor> pd(cx);
+            desc.populatePropertyDescriptor(obj, &pd);
+            pd.object().set(obj);
+            return Proxy::defineProperty(cx, obj, id, &pd);
         }
         return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
     }
 
     return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
 }
 
 bool
@@ -1144,19 +1146,20 @@ js::DefineProperties(JSContext *cx, Hand
             if (!DefinePropertyOnArray(cx, arr, ids[i], descs[i], true, &dummy))
                 return false;
         }
         return true;
     }
 
     if (obj->getOps()->lookupGeneric) {
         if (obj->is<ProxyObject>()) {
+            Rooted<PropertyDescriptor> pd(cx);
             for (size_t i = 0, len = ids.length(); i < len; i++) {
-                RootedValue pd(cx, descs[i].descriptorValue());
-                if (!Proxy::defineProperty(cx, obj, ids[i], pd))
+                descs[i].populatePropertyDescriptor(obj, &pd);
+                if (!Proxy::defineProperty(cx, obj, ids[i], &pd))
                     return false;
             }
             return true;
         }
         bool dummy;
         return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy);
     }
 
@@ -1814,36 +1817,16 @@ JSObject::nonNativeSetElement(JSContext 
 
         WatchpointMap *wpmap = cx->compartment()->watchpointMap;
         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
             return false;
     }
     return obj->getOps()->setElement(cx, obj, index, vp, strict);
 }
 
-/* static */ bool
-JSObject::deleteByValue(JSContext *cx, HandleObject obj, const Value &property, bool *succeeded)
-{
-    uint32_t index;
-    if (IsDefinitelyIndex(property, &index))
-        return deleteElement(cx, obj, index, succeeded);
-
-    RootedValue propval(cx, property);
-
-    JSAtom *name = ToAtom<CanGC>(cx, propval);
-    if (!name)
-        return false;
-
-    if (name->isIndex(&index))
-        return deleteElement(cx, obj, index, succeeded);
-
-    Rooted<PropertyName*> propname(cx, name->asPropertyName());
-    return deleteProperty(cx, obj, propname, succeeded);
-}
-
 JS_FRIEND_API(bool)
 JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target,
                     HandleObject obj)
 {
     // |obj| and |cx| are generally not same-compartment with |target| here.
     assertSameCompartment(cx, obj, id);
     Rooted<JSPropertyDescriptor> desc(cx);
 
@@ -2711,17 +2694,18 @@ DefineConstructorAndPrototype(JSContext 
 
     if (ctorp)
         *ctorp = ctor;
     return proto;
 
 bad:
     if (named) {
         bool succeeded;
-        JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded);
+        RootedId id(cx, AtomToId(atom));
+        JSObject::deleteGeneric(cx, obj, id, &succeeded);
     }
     if (cached)
         ClearClassObject(obj, key);
     return nullptr;
 }
 
 JSObject *
 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
@@ -3442,16 +3426,26 @@ js::FindClassObject(ExclusiveContext *cx
         if (shape->hasSlot())
             v = pobj->nativeGetSlot(shape->slot());
     }
     if (v.isObject())
         protop.set(&v.toObject());
     return true;
 }
 
+bool
+JSObject::isConstructor() const
+{
+    if (is<JSFunction>()) {
+        const JSFunction &fun = as<JSFunction>();
+        return fun.isNativeConstructor() || fun.isInterpretedConstructor();
+    }
+    return getClass()->construct != nullptr;
+}
+
 /* static */ bool
 JSObject::allocSlot(ThreadSafeContext *cx, HandleObject obj, uint32_t *slotp)
 {
     JS_ASSERT(cx->isThreadLocal(obj));
 
     uint32_t slot = obj->slotSpan();
     JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
 
@@ -5308,33 +5302,16 @@ baseops::DeleteGeneric(JSContext *cx, Ha
         return false;
     if (!succeeded)
         return true;
 
     return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
 }
 
 bool
-baseops::DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                        bool *succeeded)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return baseops::DeleteGeneric(cx, obj, id, succeeded);
-}
-
-bool
-baseops::DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return baseops::DeleteGeneric(cx, obj, id, succeeded);
-}
-
-bool
 js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
 {
     RootedObject obj(cx, GetInnerObject(origObj));
     if (obj->isNative()) {
         // Use sparse indexes for watched objects, as dense elements can be
         // written to without checking the watchpoint map.
         if (!JSObject::sparsifyDenseElements(cx, obj))
             return false;
@@ -5410,17 +5387,17 @@ js::HasDataProperty(JSContext *cx, JSObj
  * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
  * or steps 3-4.
  */
 static bool
 MaybeCallMethod(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
     if (!JSObject::getGeneric(cx, obj, obj, id, vp))
         return false;
-    if (!js_IsCallable(vp)) {
+    if (!IsCallable(vp)) {
         vp.setObject(*obj);
         return true;
     }
     return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
 }
 
 JS_FRIEND_API(bool)
 js::DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
@@ -6006,16 +5983,17 @@ js_DumpBacktrace(JSContext *cx)
         unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
         JSScript *script = i.script();
         sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
                         depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
                         script, script->pcToOffset(i.pc()));
     }
     fprintf(stdout, "%s", sprinter.string());
 }
+
 void
 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     if (hasDynamicSlots())
         sizes->mallocHeapSlots += mallocSizeOf(slots);
 
     if (hasDynamicElements()) {
         js::ObjectElements *elements = getElementsHeader();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -144,22 +144,16 @@ SetElementHelper(JSContext *cx, HandleOb
 
 extern bool
 GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp);
 
 extern bool
 SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp);
 
 extern bool
-DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, bool *succeeded);
-
-extern bool
-DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded);
-
-extern bool
 DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
 
 extern bool
 Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
 
 extern bool
 Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 
@@ -808,16 +802,17 @@ class JSObject : public js::ObjectImpl
     static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
 
     /*
      * Back to generic stuff.
      */
     bool isCallable() {
         return getClass()->isCallable();
     }
+    bool isConstructor() const;
 
     inline void finish(js::FreeOp *fop);
     MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 
     static inline bool hasProperty(JSContext *cx, js::HandleObject obj,
                                    js::HandleId id, bool *foundp);
 
     /*
@@ -1049,23 +1044,20 @@ class JSObject : public js::ObjectImpl
     {
         js::GenericAttributesOp op = obj->getOps()->getGenericAttributes;
         return (op ? op : js::baseops::GetAttributes)(cx, obj, id, attrsp);
     }
 
     static inline bool setGenericAttributes(JSContext *cx, js::HandleObject obj,
                                             js::HandleId id, unsigned *attrsp);
 
-    static inline bool deleteProperty(JSContext *cx, js::HandleObject obj,
-                                      js::HandlePropertyName name,
-                                      bool *succeeded);
-    static inline bool deleteElement(JSContext *cx, js::HandleObject obj,
-                                     uint32_t index, bool *succeeded);
-    static bool deleteByValue(JSContext *cx, js::HandleObject obj,
-                              const js::Value &property, bool *succeeded);
+    static inline bool deleteGeneric(JSContext *cx, js::HandleObject obj, js::HandleId id,
+                                     bool *succeeded);
+    static inline bool deleteElement(JSContext *cx, js::HandleObject obj, uint32_t index,
+                                     bool *succeeded);
 
     static inline bool watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                              JS::HandleObject callable);
     static inline bool unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 
     static bool enumerate(JSContext *cx, JS::HandleObject obj, JSIterateOp iterop,
                           JS::MutableHandleValue statep, JS::MutableHandleId idp)
     {
@@ -1188,22 +1180,31 @@ operator!=(const JSObject &lhs, const JS
 }
 
 struct JSObject_Slots2 : JSObject { js::Value fslots[2]; };
 struct JSObject_Slots4 : JSObject { js::Value fslots[4]; };
 struct JSObject_Slots8 : JSObject { js::Value fslots[8]; };
 struct JSObject_Slots12 : JSObject { js::Value fslots[12]; };
 struct JSObject_Slots16 : JSObject { js::Value fslots[16]; };
 
-static inline bool
-js_IsCallable(const js::Value &v)
+namespace js {
+
+inline bool
+IsCallable(const Value &v)
 {
     return v.isObject() && v.toObject().isCallable();
 }
 
+// ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
+inline bool
+IsConstructor(const Value &v)
+{
+    return v.isObject() && v.toObject().isConstructor();
+}
+
 inline JSObject *
 GetInnerObject(JSObject *obj)
 {
     if (js::InnerObjectOp op = obj->getClass()->ext.innerObject) {
         JS::AutoSuppressGCAnalysis nogc;
         return op(obj);
     }
     return obj;
@@ -1212,16 +1213,18 @@ GetInnerObject(JSObject *obj)
 inline JSObject *
 GetOuterObject(JSContext *cx, js::HandleObject obj)
 {
     if (js::ObjectOp op = obj->getClass()->ext.outerObject)
         return op(cx, obj);
     return obj;
 }
 
+} /* namespace js */
+
 class JSValueArray {
   public:
     const jsval *array;
     size_t length;
 
     JSValueArray(const jsval *v, size_t c) : array(v), length(c) {}
 };
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -36,34 +36,31 @@ JSObject::setGenericAttributes(JSContext
 JSObject::changePropertyAttributes(JSContext *cx, js::HandleObject obj,
                                    js::HandleShape shape, unsigned attrs)
 {
     return !!changeProperty<js::SequentialExecution>(cx, obj, shape, attrs, 0,
                                                      shape->getter(), shape->setter());
 }
 
 /* static */ inline bool
-JSObject::deleteProperty(JSContext *cx, js::HandleObject obj, js::HandlePropertyName name,
+JSObject::deleteGeneric(JSContext *cx, js::HandleObject obj, js::HandleId id,
                          bool *succeeded)
 {
-    JS::RootedId id(cx, js::NameToId(name));
     js::types::MarkTypePropertyNonData(cx, obj, id);
-    js::DeletePropertyOp op = obj->getOps()->deleteProperty;
-    return (op ? op : js::baseops::DeleteProperty)(cx, obj, name, succeeded);
+    js::DeleteGenericOp op = obj->getOps()->deleteGeneric;
+    return (op ? op : js::baseops::DeleteGeneric)(cx, obj, id, succeeded);
 }
 
 /* static */ inline bool
 JSObject::deleteElement(JSContext *cx, js::HandleObject obj, uint32_t index, bool *succeeded)
 {
     JS::RootedId id(cx);
     if (!js::IndexToId(cx, index, &id))
         return false;
-    js::types::MarkTypePropertyNonData(cx, obj, id);
-    js::DeleteElementOp op = obj->getOps()->deleteElement;
-    return (op ? op : js::baseops::DeleteElement)(cx, obj, index, succeeded);
+    return deleteGeneric(cx, obj, id, succeeded);
 }
 
 /* static */ inline bool
 JSObject::watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
                 JS::HandleObject callable)
 {
     js::WatchOp op = obj->getOps()->watch;
     return (op ? op : js::baseops::Watch)(cx, obj, id, callable);
@@ -649,18 +646,18 @@ PropDesc::PropDesc(const Value &getter, 
     get_(getter), set_(setter),
     attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED |
           (enumerable ? JSPROP_ENUMERATE : 0) |
           (configurable ? 0 : JSPROP_PERMANENT)),
     hasGet_(true), hasSet_(true),
     hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true),
     isUndefined_(false)
 {
-    MOZ_ASSERT(getter.isUndefined() || js_IsCallable(getter));
-    MOZ_ASSERT(setter.isUndefined() || js_IsCallable(setter));
+    MOZ_ASSERT(getter.isUndefined() || IsCallable(getter));
+    MOZ_ASSERT(setter.isUndefined() || IsCallable(setter));
 }
 
 static MOZ_ALWAYS_INLINE bool
 IsFunctionObject(const js::Value &v)
 {
     return v.isObject() && v.toObject().is<JSFunction>();
 }
 
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -190,17 +190,17 @@ PreprocessValue(JSContext *cx, HandleObj
 
     /* Step 2. */
     if (vp.isObject()) {
         RootedValue toJSON(cx);
         RootedObject obj(cx, &vp.toObject());
         if (!JSObject::getProperty(cx, obj, obj, cx->names().toJSON, &toJSON))
             return false;
 
-        if (js_IsCallable(toJSON)) {
+        if (IsCallable(toJSON)) {
             keyStr = KeyStringifier<KeyType>::toString(cx, key);
             if (!keyStr)
                 return false;
 
             InvokeArgs args(cx);
             if (!args.init(1))
                 return false;
 
@@ -262,17 +262,17 @@ PreprocessValue(JSContext *cx, HandleObj
  * gauntlet will result in Str returning |undefined|.  This function is used to
  * properly omit properties resulting in such values when stringifying objects,
  * while properly stringifying such properties as null when they're encountered
  * in arrays.
  */
 static inline bool
 IsFilteredValue(const Value &v)
 {
-    return v.isUndefined() || js_IsCallable(v);
+    return v.isUndefined() || IsCallable(v);
 }
 
 /* ES5 15.12.3 JO. */
 static bool
 JO(JSContext *cx, HandleObject obj, StringifyContext *scx)
 {
     /*
      * This method implements the JO algorithm in ES5 15.12.3, but:
@@ -693,17 +693,17 @@ Walk(JSContext *cx, HandleObject holder,
 
                 /* Step 2a(iii)(1). */
                 if (!Walk(cx, obj, id, reviver, &newElement))
                     return false;
 
                 if (newElement.isUndefined()) {
                     /* Step 2a(iii)(2). */
                     bool succeeded;
-                    if (!JSObject::deleteByValue(cx, obj, IdToValue(id), &succeeded))
+                    if (!JSObject::deleteGeneric(cx, obj, id, &succeeded))
                         return false;
                 } else {
                     /* Step 2a(iii)(3). */
                     // XXX This definition should ignore success/failure, when
                     //     our property-definition APIs indicate that.
                     if (!JSObject::defineGeneric(cx, obj, id, newElement))
                         return false;
                 }
@@ -721,17 +721,17 @@ Walk(JSContext *cx, HandleObject holder,
                 /* Step 2b(ii)(1). */
                 id = keys[i];
                 if (!Walk(cx, obj, id, reviver, &newElement))
                     return false;
 
                 if (newElement.isUndefined()) {
                     /* Step 2b(ii)(2). */
                     bool succeeded;
-                    if (!JSObject::deleteByValue(cx, obj, IdToValue(id), &succeeded))
+                    if (!JSObject::deleteGeneric(cx, obj, id, &succeeded))
                         return false;
                 } else {
                     /* Step 2b(ii)(3). */
                     // XXX This definition should ignore success/failure, when
                     //     our property-definition APIs indicate that.
                     if (!JSObject::defineGeneric(cx, obj, id, newElement))
                         return false;
                 }
@@ -778,17 +778,17 @@ js::ParseJSONWithReviver(JSContext *cx, 
                          HandleValue reviver, MutableHandleValue vp)
 {
     /* 15.12.2 steps 2-3. */
     JSONParser parser(cx, chars, length);
     if (!parser.parse(vp))
         return false;
 
     /* 15.12.2 steps 4-5. */
-    if (js_IsCallable(reviver))
+    if (IsCallable(reviver))
         return Revive(cx, reviver, vp);
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static bool
 json_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -422,17 +422,17 @@ DirectProxyHandler::getOwnPropertyNames(
     return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props);
 }
 
 bool
 DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     assertEnteredPolicy(cx, proxy, id, SET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return JS_DeletePropertyById2(cx, target, id, bp);
+    return JSObject::deleteGeneric(cx, target, id, bp);
 }
 
 bool
 DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy,
                               AutoIdVector &props)
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
     JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype.
@@ -910,30 +910,30 @@ ScriptedIndirectProxyHandler::enumerate(
 
 bool
 ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::has(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, &value) &&
            ValueToBool(value, bp);
 }
 
 bool
 ScriptedIndirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, &value) &&
            ValueToBool(value, bp);
 }
 
 bool
 ScriptedIndirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                                   HandleId id, MutableHandleValue vp)
@@ -945,17 +945,17 @@ ScriptedIndirectProxyHandler::get(JSCont
         return false;
     RootedValue value(cx, StringValue(str));
     JS::AutoValueArray<2> argv(cx);
     argv[0].setObjectOrNull(receiver);
     argv[1].set(value);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
     return Trap(cx, handler, fval, 2, argv.begin(), vp);
 }
 
 bool
 ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
                                   HandleId id, bool strict, MutableHandleValue vp)
 {
@@ -967,43 +967,43 @@ ScriptedIndirectProxyHandler::set(JSCont
     RootedValue value(cx, StringValue(str));
     JS::AutoValueArray<3> argv(cx);
     argv[0].setObjectOrNull(receiver);
     argv[1].set(value);
     argv[2].set(vp);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
         return false;
-    if (!js_IsCallable(fval))
+    if (!IsCallable(fval))
         return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
     return Trap(cx, handler, fval, 3, argv.begin(), &value);
 }
 
 bool
 ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
         return false;
-    if (!js_IsCallable(value))
+    if (!IsCallable(value))
         return BaseProxyHandler::keys(cx, proxy, props);
     return Trap(cx, handler, value, 0, nullptr, &value) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
 ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                                       MutableHandleValue vp)
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
         return false;
-    if (!js_IsCallable(value))
+    if (!IsCallable(value))
         return BaseProxyHandler::iterate(cx, proxy, flags, vp);
     return Trap(cx, handler, value, 0, nullptr, vp) &&
            ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp);
 }
 
 bool
 ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
 {
@@ -2379,25 +2379,16 @@ Proxy::defineProperty(JSContext *cx, Han
     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc);
 }
 
 bool
-Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v)
-{
-    JS_CHECK_RECURSION(cx, return false);
-    Rooted<PropertyDescriptor> desc(cx);
-    return ParsePropertyDescriptorObject(cx, proxy, v, &desc) &&
-           Proxy::defineProperty(cx, proxy, id, &desc);
-}
-
-bool
 Proxy::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     JS_CHECK_RECURSION(cx, return false);
     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     if (!policy.allowed())
         return policy.returnValue();
     return proxy->as<ProxyObject>().handler()->getOwnPropertyNames(cx, proxy, props);
@@ -2915,42 +2906,26 @@ js::proxy_SetGenericAttributes(JSContext
     /* Lookup the current property descriptor so we have setter/getter/value. */
     Rooted<PropertyDescriptor> desc(cx);
     if (!Proxy::getOwnPropertyDescriptor(cx, obj, id, &desc))
         return false;
     desc.setAttributes(*attrsp);
     return Proxy::defineProperty(cx, obj, id, &desc);
 }
 
-static bool
-proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
+bool
+js::proxy_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
     bool deleted;
     if (!Proxy::delete_(cx, obj, id, &deleted))
         return false;
     *succeeded = deleted;
     return js_SuppressDeletedProperty(cx, obj, id);
 }
 
-bool
-js::proxy_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, bool *succeeded)
-{
-    RootedId id(cx, NameToId(name));
-    return proxy_DeleteGeneric(cx, obj, id, succeeded);
-}
-
-bool
-js::proxy_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return proxy_DeleteGeneric(cx, obj, id, succeeded);
-}
-
 void
 js::proxy_Trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->is<ProxyObject>());
     ProxyObject::trace(trc, obj);
 }
 
 /* static */ void
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -309,17 +309,16 @@ class Proxy
     static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                       MutableHandleValue vp);
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandle<JSPropertyDescriptor> desc);
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandleValue vp);
     static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc);
-    static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v);
     static bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props);
     static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props);
 
     /* ES5 Harmony derived proxy traps. */
     static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3355,17 +3355,17 @@ js::str_replace(JSContext *cx, unsigned 
     rdata.str = ThisToStringForStringProto(cx, args);
     if (!rdata.str)
         return false;
 
     if (!rdata.g.init(cx, args))
         return false;
 
     /* Extract replacement string/function. */
-    if (args.length() >= ReplaceOptArg && js_IsCallable(args[1])) {
+    if (args.length() >= ReplaceOptArg && IsCallable(args[1])) {
         rdata.setReplacementFunction(&args[1].toObject());
 
         if (!LambdaIsGetElem(cx, *rdata.lambda, &rdata.elembase))
             return false;
     } else {
         JSLinearString *string = ArgToRootedString(cx, args, 1);
         if (!string)
             return false;
@@ -4343,17 +4343,17 @@ js::ValueToSource(JSContext *cx, HandleV
         }
         return ToString<CanGC>(cx, v);
     }
 
     RootedValue fval(cx);
     RootedObject obj(cx, &v.toObject());
     if (!JSObject::getProperty(cx, obj, obj, cx->names().toSource, &fval))
         return nullptr;
-    if (js_IsCallable(fval)) {
+    if (IsCallable(fval)) {
         RootedValue rval(cx);
         if (!Invoke(cx, ObjectValue(*obj), fval, 0, nullptr, &rval))
             return nullptr;
         return ToString<CanGC>(cx, rval);
     }
 
     return ObjectToSource(cx, obj);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/strict/8.12.7-2.js
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+// delete o[p] only performs ToString(p) once, even if there's a strict error.
+var hits = 0;
+var p = {
+    toString: function () {
+        hits++;
+        return "noconfig";
+    }
+};
+assertEq(testLenientAndStrict('var o = Object.freeze({noconfig: "ow"}); delete o[p]',
+                              returns(false), raisesException(TypeError)),
+         true);
+assertEq(hits, 2);
+
+reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_basics.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Array.from copies arrays.
+var src = [1, 2, 3], copy = Array.from(src);
+assertEq(copy === src, false);
+assertEq(Array.isArray(copy), true);
+assertDeepEq(copy, src);
+
+// Non-element properties are not copied.
+var a = [0, 1];
+a.name = "lisa";
+assertDeepEq(Array.from(a), [0, 1]);
+
+// It's a shallow copy.
+src = [[0], [1]];
+copy = Array.from(src);
+assertEq(copy[0], src[0]);
+assertEq(copy[1], src[1]);
+
+// Array.from can copy non-iterable objects, if they're array-like.
+src = {0: "zero", 1: "one", length: 2};
+copy = Array.from(src);
+assertEq(Array.isArray(copy), true);
+assertDeepEq(copy, ["zero", "one"]);
+
+// Properties past the .length are not copied.
+src = {0: "zero", 1: "one", 2: "two", 9: "nine", name: "lisa", length: 2};
+assertDeepEq(Array.from(src), ["zero", "one"]);
+
+// If an object has neither an @@iterator method nor .length,
+// then it's treated as zero-length.
+assertDeepEq(Array.from({}), []);
+
+// Source object property order doesn't matter.
+src = {length: 2, 1: "last", 0: "first"};
+assertDeepEq(Array.from(src), ["first", "last"]);
+
+// Array.from does not preserve holes.
+assertDeepEq(Array.from(Array(3)), [undefined, undefined, undefined]);
+assertDeepEq(Array.from([, , 2, 3]), [undefined, undefined, 2, 3]);
+assertDeepEq(Array.from([0, , , ,]), [0, undefined, undefined, undefined]);
+
+// Even on non-iterable objects.
+assertDeepEq(Array.from({length: 4}), [undefined, undefined, undefined, undefined]);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_constructor.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Array.from can be applied to any constructor.
+// For example, the Date builtin constructor.
+var d = Array.from.call(Date, ["A", "B"]);
+assertEq(Array.isArray(d), false);
+assertEq(Object.prototype.toString.call(d), "[object Date]");
+assertEq(Object.getPrototypeOf(d), Date.prototype);
+assertEq(d.length, 2);
+assertEq(d[0], "A");
+assertEq(d[1], "B");
+
+// Or Object.
+var obj = Array.from.call(Object, []);
+assertEq(Array.isArray(obj), false);
+assertEq(Object.getPrototypeOf(obj), Object.prototype);
+assertEq(Object.getOwnPropertyNames(obj).join(","), "length");
+assertEq(obj.length, 0);
+
+// Or any JS function.
+function C(arg) {
+    this.args = arguments;
+}
+var c = Array.from.call(C, {length: 1, 0: "zero"});
+assertEq(c instanceof C, true);
+assertEq(c.args.length, 1);
+assertEq(c.args[0], 1);
+assertEq(c.length, 1);
+assertEq(c[0], "zero");
+
+// If the 'this' value passed to Array.from is not a constructor,
+// a plain Array is created.
+var arr = [3, 4, 5];
+var nonconstructors = [
+    {}, Math, Object.getPrototypeOf, undefined, 17,
+    () => ({})  // arrow functions are not constructors
+];
+for (var v of nonconstructors) {
+    obj = Array.from.call(v, arr);
+    assertEq(Array.isArray(obj), true);
+    assertDeepEq(obj, arr);
+}
+
+// Array.from does not get confused if global.Array is replaced with another
+// constructor.
+function NotArray() {
+}
+var RealArray = Array;
+NotArray.from = Array.from;
+Array = NotArray;
+assertEq(RealArray.from([1]) instanceof RealArray, true);
+assertEq(NotArray.from([1]) instanceof NotArray, true);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_errors.js
@@ -0,0 +1,145 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Array.from throws if the argument is undefined or null.
+assertThrowsInstanceOf(() => Array.from(), TypeError);
+assertThrowsInstanceOf(() => Array.from(undefined), TypeError);
+assertThrowsInstanceOf(() => Array.from(null), TypeError);
+
+// Array.from throws if an element can't be defined on the new object.
+function ObjectWithReadOnlyElement() {
+    Object.defineProperty(this, "0", {value: null});
+    this.length = 0;
+}
+ObjectWithReadOnlyElement.from = Array.from;
+assertDeepEq(ObjectWithReadOnlyElement.from([]), new ObjectWithReadOnlyElement);
+assertThrowsInstanceOf(() => ObjectWithReadOnlyElement.from([1]), TypeError);
+
+// The same, but via preventExtensions.
+function InextensibleObject() {
+    Object.preventExtensions(this);
+}
+InextensibleObject.from = Array.from;
+assertThrowsInstanceOf(() => InextensibleObject.from([1]), TypeError);
+
+// We will now test this property, that Array.from throws if the .length can't
+// be assigned, using several different kinds of object.
+var obj;
+function init(self) {
+    obj = self;
+    self[0] = self[1] = self[2] = self[3] = 0;
+}
+
+function testUnsettableLength(C, Exc) {
+    if (Exc === undefined)
+        Exc = TypeError;  // the usual expected exception type
+    C.from = Array.from;
+
+    obj = null;
+    assertThrowsInstanceOf(() => C.from([]), Exc);
+    assertEq(obj instanceof C, true);
+    for (var i = 0; i < 4; i++)
+        assertEq(obj[0], 0);
+
+    obj = null;
+    assertThrowsInstanceOf(() => C.from([0, 10, 20, 30]), Exc);
+    assertEq(obj instanceof C, true);
+    for (var i = 0; i < 4; i++)
+        assertEq(obj[i], i * 10);
+}
+
+// Array.from throws if the new object's .length can't be assigned because
+// there is no .length and the object is inextensible.
+function InextensibleObject4() {
+    init(this);
+    Object.preventExtensions(this);
+}
+testUnsettableLength(InextensibleObject4);
+
+// Array.from throws if the new object's .length can't be assigned because it's
+// read-only.
+function ObjectWithReadOnlyLength() {
+    init(this);
+    Object.defineProperty(this, "length", {configurable: true, writable: false, value: 4});
+}
+testUnsettableLength(ObjectWithReadOnlyLength);
+
+// The same, but using a builtin type.
+Uint8Array.from = Array.from;
+assertThrowsInstanceOf(() => Uint8Array.from([]), TypeError);
+
+// Array.from throws if the new object's .length can't be assigned because it
+// inherits a readonly .length along the prototype chain.
+function ObjectWithInheritedReadOnlyLength() {
+    init(this);
+}
+Object.defineProperty(ObjectWithInheritedReadOnlyLength.prototype,
+                      "length",
+                      {configurable: true, writable: false, value: 4});
+testUnsettableLength(ObjectWithInheritedReadOnlyLength);
+
+// The same, but using an object with a .length getter but no setter.
+function ObjectWithGetterOnlyLength() {
+    init(this);
+    Object.defineProperty(this, "length", {configurable: true, get: () => 4});
+}
+testUnsettableLength(ObjectWithGetterOnlyLength);
+
+// The same, but with a setter that throws.
+function ObjectWithThrowingLengthSetter() {
+    init(this);
+    Object.defineProperty(this, "length", {
+        configurable: true,
+        get: () => 4,
+        set: () => { throw new RangeError("surprise!"); }
+    });
+}
+testUnsettableLength(ObjectWithThrowingLengthSetter, RangeError);
+
+// Array.from throws if mapfn is neither callable nor undefined.
+assertThrowsInstanceOf(() => Array.from([3, 4, 5], {}), TypeError);
+assertThrowsInstanceOf(() => Array.from([3, 4, 5], "also not a function"), TypeError);
+assertThrowsInstanceOf(() => Array.from([3, 4, 5], null), TypeError);
+
+// Even if the function would not have been called.
+assertThrowsInstanceOf(() => Array.from([], JSON), TypeError);
+
+// If mapfn is not undefined and not callable, the error happens before anything else.
+// Before calling the constructor, before touching the arrayLike.
+var log = "";
+function C() {
+    log += "C";
+    obj = this;
+}
+var p = new Proxy({}, {
+    has: function () { log += "1"; },
+    get: function () { log += "2"; },
+    getOwnPropertyDescriptor: function () { log += "3"; }
+});
+assertThrowsInstanceOf(() => Array.from.call(C, p, {}), TypeError);
+assertEq(log, "");
+
+// If mapfn throws, the new object has already been created.
+var arrayish = {
+    get length() { log += "l"; return 1; },
+    get 0() { log += "0"; return "q"; }
+};
+log = "";
+var exc = {surprise: "ponies"};
+assertThrowsValue(() => Array.from.call(C, arrayish, () => { throw exc; }), exc);
+assertEq(log, "lC0");
+assertEq(obj instanceof C, true);
+
+// It's a TypeError if the iterator's .next() method returns a primitive.
+for (var primitive of [undefined, null, 17]) {
+    assertThrowsInstanceOf(
+        () => Array.from({
+            "@@iterator": () => {
+                next: () => primitive
+            }
+        }),
+        TypeError);
+}
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_iterable.js
@@ -0,0 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Array.from works on arguments objects.
+(function () {
+    assertDeepEq(Array.from(arguments), ["arg0", "arg1", undefined]);
+})("arg0", "arg1", undefined);
+
+// If an object has both .length and [@@iterator] properties, [@@iterator] is used.
+var a = ['a', 'e', 'i', 'o', 'u'];
+a["@@iterator"] = function* () {
+    for (var i = 5; i--; )
+        yield this[i];
+};
+
+var log = '';
+function f(x) {
+    log += x;
+    return x + x;
+}
+
+var b = Array.from(a, f);
+assertDeepEq(b, ['uu', 'oo', 'ii', 'ee', 'aa']);
+assertEq(log, 'uoiea');
+
+// In fact, if [@@iterator] is present, .length isn't queried at all.
+var pa = new Proxy(a, {
+    has: function (target, id) {
+        if (id === "length")
+            throw new Error(".length should not be queried (has)");
+        return id in target;
+    },
+    get: function (target, id) {
+        if (id === "length")
+            throw new Error(".length should not be queried (get)");
+        return target[id];
+    },
+    getOwnPropertyDescriptor: function (target, id) {
+        if (id === "length")
+            throw new Error(".length should not be queried (getOwnPropertyDescriptor)");
+        return Object.getOwnPropertyDescriptor(target, id)
+    }
+});
+log = "";
+b = Array.from(pa, f);
+assertDeepEq(b, ['uu', 'oo', 'ii', 'ee', 'aa']);
+assertEq(log, 'uoiea');
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_length_setter.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Array.from calls a length setter if present.
+var hits = 0;
+function C() {}
+C.prototype = {set length(v) { hits++; }};
+C.from = Array.from;
+var copy = C.from(["A", "B"]);
+assertEq(hits, 1);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_mapping.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// If the mapfn argument to Array.from is undefined, don't map.
+assertDeepEq(Array.from([3, 4, 5], undefined), [3, 4, 5]);
+assertDeepEq(Array.from([4, 5, 6], undefined, Math), [4, 5, 6]);
+
+// mapfn is called with two arguments: value and index.
+var log = [];
+function f() {
+    log.push(Array.from(arguments));
+    return log.length;
+}
+assertDeepEq(Array.from(['a', 'e', 'i', 'o', 'u'], f), [1, 2, 3, 4, 5]);
+assertDeepEq(log, [['a', 0], ['e', 1], ['i', 2], ['o', 3], ['u', 4]]);
+
+// If the object to be copied is non-iterable, mapfn is still called with two
+// arguments.
+log = [];
+assertDeepEq(Array.from({0: "zero", 1: "one", length: 2}, f), [1, 2]);
+assertDeepEq(log, [["zero", 0], ["one", 1]]);
+
+// If the object to be copied is iterable and the constructor is not Array,
+// mapfn is still called with two arguments.
+log = [];
+function C() {}
+C.from = Array.from;
+var c = new C;
+c[0] = 1;
+c[1] = 2;
+c.length = 2;
+assertDeepEq(C.from(["zero", "one"], f), c);
+assertDeepEq(log, [["zero", 0], ["one", 1]]);
+
+// The mapfn is called even if the value to be mapped is undefined.
+assertDeepEq(Array.from([0, 1, , 3], String), ["0", "1", "undefined", "3"]);
+var arraylike = {length: 4, "0": 0, "1": 1, "3": 3};
+assertDeepEq(Array.from(arraylike, String), ["0", "1", "undefined", "3"]);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_proxy.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Two tests involving Array.from and a Proxy.
+var log = [];
+function LoggingProxy(target) {
+    var h = {
+        defineProperty: function (t, id) {
+            log.push("define " + id);
+            return undefined;
+        },
+        has: function (t, id) {
+            log.push("has " + id);
+            return id in t;
+        },
+        get: function (t, id) {
+            log.push("get " + id);
+            return t[id];
+        },
+        set: function (t, id, v) {
+            log.push("set " + id);
+            t[id] = v;
+        }
+    };
+    return new Proxy(target || [], h);
+}
+
+// When the new object created by Array.from is a Proxy,
+// Array.from calls handler.defineProperty to create new elements
+// but handler.set to set the length.
+LoggingProxy.from = Array.from;
+LoggingProxy.from([3, 4, 5]);
+assertDeepEq(log, ["define 0", "define 1", "define 2", "set length"]);
+
+// When the argument passed to Array.from is a Proxy, Array.from
+// calls handler.get on it.
+log = [];
+assertDeepEq(Array.from(new LoggingProxy([3, 4, 5])), [3, 4, 5]);
+assertDeepEq(log, ["get @@iterator",
+                   "get length", "get 0", "get length", "get 1", "get length", "get 2",
+                   "get length"]);
+
+// Array-like iteration only gets the length once.
+log = [];
+var arr = [5, 6, 7];
+arr["@@iterator"] = undefined;
+assertDeepEq(Array.from(new LoggingProxy(arr)), [5, 6, 7]);
+assertDeepEq(log, ["get @@iterator",
+                   "get length", "get 0", "get 1", "get 2"]);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_realms.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+if (typeof newGlobal === 'function') {
+    // G.Array.from, where G is any global, produces an array whose prototype
+    // is G.Array.prototype.
+    var g = newGlobal();
+    var ga = g.Array.from([1, 2, 3]);
+    assertEq(ga instanceof g.Array, true);
+
+    // Even if G.Array is not passed in as the 'this' value to the call.
+    var from = g.Array.from
+    var ga2 = from([1, 2, 3]);
+    assertEq(ga2 instanceof g.Array, true);
+
+    // Array.from can be applied to a constructor from another realm.
+    var p = Array.from.call(g.Array, [1, 2, 3]);
+    assertEq(p instanceof g.Array, true);
+    var q = g.Array.from.call(Array, [3, 4, 5]);
+    assertEq(q instanceof Array, true);
+
+    // The default 'this' value received by a non-strict mapping function is
+    // that function's global, not Array.from's global or the caller's global.
+    var h = newGlobal(), result = undefined;
+    h.mainGlobal = this;
+    h.eval("function f() { mainGlobal.result = this; }");
+    g.Array.from.call(Array, [5, 6, 7], h.f);
+    // (Give each global in the test a name, for better error messages.)
+    this.name = "main";
+    g.name = "g";
+    h.name = "h";
+    assertEq(result.name, "h");
+}
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_string.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Array.from on a string iterates over the string.
+assertDeepEq(Array.from("test string"),
+             ['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g']);
+
+// Array.from on a string handles surrogate pairs correctly.
+var gclef = "\uD834\uDD1E"; // U+1D11E MUSICAL SYMBOL G CLEF
+assertDeepEq(Array.from(gclef), [gclef]);
+assertDeepEq(Array.from(gclef + " G"), [gclef, " ", "G"]);
+
+// Array.from on a string calls the @@iterator method.
+String.prototype["@@iterator"] = function* () { yield 1; yield 2; };
+assertDeepEq(Array.from("anything"), [1, 2]);
+
+// If the iterator method is deleted, Strings are still arraylike.
+delete String.prototype["@@iterator"];
+assertDeepEq(Array.from("works"), ['w', 'o', 'r', 'k', 's']);
+assertDeepEq(Array.from(gclef), ['\uD834', '\uDD1E']);
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_surfaces.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Check superficial features of Array.from.
+var desc = Object.getOwnPropertyDescriptor(Array, "from");
+assertEq(desc.configurable, true);
+assertEq(desc.enumerable, false);
+assertEq(desc.writable, true);
+assertEq(Array.from.length, 1);
+assertThrowsInstanceOf(() => new Array.from(), TypeError);  // not a constructor
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/from_this.js
@@ -0,0 +1,48 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// The third argument to Array.from is passed as the 'this' value to the
+// mapping function.
+var hits = 0, obj = {};
+function f(x) {
+    assertEq(this, obj);
+    hits++;
+}
+Array.from(["a", "b", "c"], f, obj);
+assertEq(hits, 3);
+
+// Without an argument, undefined is passed...
+hits = 0;
+function gs(x) {
+    "use strict";
+    assertEq(this, undefined);
+    hits++;
+}
+Array.from("def", gs);
+assertEq(hits, 3);
+
+// ...and if the mapping function is non-strict, that means the global is
+// passed.
+var global = this;
+hits = 0;
+function g(x) {
+    assertEq(this, global);
+    hits++;
+}
+Array.from("ghi", g);
+assertEq(hits, 3);
+
+// A primitive value can be passed.
+for (var v of [0, "str", undefined]) {
+    hits = 0;
+    var mapfn = function h(x) {
+        "use strict";
+        assertEq(this, v);
+        hits++;
+    };
+    Array.from("pq", mapfn, v);
+    assertEq(hits, 2);
+}
+
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0);
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/defineProperty-proxy.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/ */
+
+// Test details of the implementation of ToPropertyDescriptor exposed to scripts
+// thanks to scriptable proxies.
+
+// A LoggingProxy object logs certain operations performed on it.
+var log = [];
+function LoggingProxy(target) {
+    return new Proxy(target, {
+        has: function (t, id) {
+            log.push("has " + id);
+            return id in t;
+        },
+        get: function (t, id) {
+            log.push("get " + id);
+            return t[id];
+        }
+    });
+}
+
+// Tragically, we use separate code to implement Object.defineProperty on
+// arrays and on proxies. So run the test three times.
+var testSubjects = [
+    {},
+    [],
+    new Proxy({}, {})
+];
+
+for (var obj of testSubjects) {
+    log = [];
+
+    // Object.defineProperty is one public method that performs a
+    // ToPropertyDescriptor call.
+    Object.defineProperty(obj, "x", new LoggingProxy({
+        enumerable: true,
+        configurable: true,
+        value: 3,
+        writable: true
+    }));
+
+    // It should have performed exactly these operations on the proxy, in this
+    // order. See ES6 rev 24 (2014 April 27) 6.2.4.5 ToPropertyDescriptor.
+    assertDeepEq(log, [
+        "has enumerable", "get enumerable",
+        "has configurable", "get configurable",
+        "has value", "get value",
+        "has writable", "get writable",
+        "has get",
+        "has set"
+    ]);
+}
+
+reportCompare(0, 0);
new file mode 100644
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -224,17 +224,17 @@ def load_tests(options, requested_paths,
             xul_info = manifest.XULInfo.create(options.js_shell)
         else:
             xul_abi, xul_os, xul_debug = options.xul_info_src.split(r':')
             xul_debug = xul_debug.lower() is 'true'
             xul_info = manifest.XULInfo(xul_abi, xul_os, xul_debug)
         xul_tester = manifest.XULInfoTester(xul_info, options.js_shell)
 
     test_dir = dirname(abspath(__file__))
-    test_list = manifest.load(test_dir, xul_tester)
+    test_list = manifest.load(test_dir, requested_paths, excluded_paths, xul_tester)
     skip_list = []
 
     if options.make_manifests:
         manifest.make_manifests(options.make_manifests, test_list)
         sys.exit()
 
     # Create a new test list. Apply each TBPL configuration to every test.
     if options.tbpl:
@@ -252,27 +252,16 @@ def load_tests(options, requested_paths,
         print("Warning: the --jitflags option is obsolete and does nothing now.")
 
     if options.test_file:
         paths = set()
         for test_file in options.test_file:
             paths |= set([ line.strip() for line in open(test_file).readlines()])
         test_list = [ _ for _ in test_list if _.path in paths ]
 
-    if requested_paths:
-        def p(path):
-            for arg in requested_paths:
-                if path.find(arg) != -1:
-                    return True
-            return False
-        test_list = [ _ for _ in test_list if p(_.path) ]
-
-    if options.exclude_file:
-        test_list = [_ for _ in test_list if _.path not in excluded_paths]
-
     if options.no_extensions:
         pattern = os.sep + 'extensions' + os.sep
         test_list = [_ for _ in test_list if pattern not in _.path]
 
     if not options.random:
         test_list = [ _ for _ in test_list if not _.random ]
 
     if options.run_only_skipped:
--- a/js/src/tests/lib/manifest.py
+++ b/js/src/tests/lib/manifest.py
@@ -327,17 +327,17 @@ def _apply_external_manifests(filename, 
             # conditions.
             # At this point, we use external manifests only for test cases
             # that can't have their own failure type comments, so we simply
             # use the terms for the most specific path.
             testcase.terms = entry["terms"]
             testcase.comment = entry["comment"]
             _parse_one(testcase, xul_tester)
 
-def load(location, xul_tester, reldir = ''):
+def load(location, requested_paths, excluded_paths, xul_tester, reldir = ''):
     """
     Locates all tests by walking the filesystem starting at |location|.
     Uses xul_tester to evaluate any test conditions in the test header.
     Failure type and comment for a test case can come from
     - an external manifest entry for the test case,
     - an external manifest entry for a containing directory,
     - most commonly: the header of the test case itself.
     """
@@ -363,16 +363,24 @@ def load(location, xul_tester, reldir = 
         # Skip files that we know are not tests.
         if basename in EXCLUDED:
             continue
 
         # Get the full path and relative location of the file.
         filename = os.path.join(root, basename)
         fullpath = os.path.join(location, filename)
 
+        # If any tests are requested by name, skip tests that do not match.
+        if requested_paths and not any(req in filename for req in requested_paths):
+            continue
+
+        # Skip excluded tests.
+        if filename in excluded_paths:
+            continue
+
         # Skip empty files.
         statbuf = os.stat(fullpath)
         if statbuf.st_size == 0:
             continue
 
         testcase = TestCase(os.path.join(reldir, filename))
         _apply_external_manifests(filename, testcase, externalManifestEntries, xul_tester)
         _parse_test_header(fullpath, testcase, xul_tester)
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1042,17 +1042,17 @@ CallMethodIfPresent(JSContext *cx, Handl
     rval.setUndefined();
     JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     RootedId id(cx, AtomToId(atom));
     RootedValue fval(cx);
     return JSObject::getGeneric(cx, obj, obj, id, &fval) &&
-           (!js_IsCallable(fval) || Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
+           (!IsCallable(fval) || Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
 }
 
 JSTrapStatus
 Debugger::fireDebuggerStatement(JSContext *cx, MutableHandleValue vp)
 {
     RootedObject hook(cx, getHook(OnDebuggerStatement));
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
@@ -5412,26 +5412,28 @@ DebuggerObject_defineProperties(JSContex
 /*
  * This does a non-strict delete, as a matter of API design. The case where the
  * property is non-configurable isn't necessarily exceptional here.
  */
 static bool
 DebuggerObject_deleteProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "deleteProperty", args, dbg, obj);
-    RootedValue nameArg(cx, args.get(0));
+    RootedId id(cx);
+    if (!ValueToId<CanGC>(cx, args.get(0), &id))
+        return false;
 
     Maybe<AutoCompartment> ac;
     ac.construct(cx, obj);
-    if (!cx->compartment()->wrap(cx, &nameArg))
+    if (!cx->compartment()->wrapId(cx, id.address()))
         return false;
 
     bool succeeded;
     ErrorCopier ec(ac, dbg->toJSObject());
-    if (!JSObject::deleteByValue(cx, obj, nameArg, &succeeded))
+    if (!JSObject::deleteGeneric(cx, obj, id, &succeeded))
         return false;
     args.rval().setBoolean(succeeded);
     return true;
 }
 
 enum SealHelperOp { Seal, Freeze, PreventExtensions };
 
 static bool
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2307,27 +2307,27 @@ CASE(JSOP_DELNAME)
     MutableHandleValue res = REGS.stackHandleAt(-1);
     if (!DeleteNameOperation(cx, name, scopeObj, res))
         goto error;
 }
 END_CASE(JSOP_DELNAME)
 
 CASE(JSOP_DELPROP)
 {
-    RootedPropertyName &name = rootName0;
-    name = script->getName(REGS.pc);
+    RootedId &id = rootId0;
+    id = NameToId(script->getName(REGS.pc));
 
     RootedObject &obj = rootObject0;
     FETCH_OBJECT(cx, -1, obj);
 
     bool succeeded;
-    if (!JSObject::deleteProperty(cx, obj, name, &succeeded))
+    if (!JSObject::deleteGeneric(cx, obj, id, &succeeded))
         goto error;
     if (!succeeded && script->strict()) {
-        obj->reportNotConfigurable(cx, NameToId(name));
+        obj->reportNotConfigurable(cx, id);
         goto error;
     }
     MutableHandleValue res = REGS.stackHandleAt(-1);
     res.setBoolean(succeeded);
 }
 END_CASE(JSOP_DELPROP)
 
 CASE(JSOP_DELELEM)
@@ -2335,25 +2335,22 @@ CASE(JSOP_DELELEM)
     /* Fetch the left part and resolve it to a non-null object. */
     RootedObject &obj = rootObject0;
     FETCH_OBJECT(cx, -2, obj);
 
     RootedValue &propval = rootValue0;
     propval = REGS.sp[-1];
 
     bool succeeded;
-    if (!JSObject::deleteByValue(cx, obj, propval, &succeeded))
+    RootedId &id = rootId0;
+    if (!ValueToId<CanGC>(cx, propval, &id))
+        goto error;
+    if (!JSObject::deleteGeneric(cx, obj, id, &succeeded))
         goto error;
     if (!succeeded && script->strict()) {
-        // XXX This observably calls ToString(propval).  We should convert to
-        //     PropertyKey and use that to delete, and to report an error if
-        //     necessary!
-        RootedId id(cx);
-        if (!ValueToId<CanGC>(cx, propval, &id))
-            goto error;
         obj->reportNotConfigurable(cx, id);
         goto error;
     }
 
     MutableHandleValue res = REGS.stackHandleAt(-2);
     res.setBoolean(succeeded);
     REGS.sp--;
 }
@@ -3679,17 +3676,18 @@ template bool js::SetProperty<false>(JSC
 template <bool strict>
 bool
 js::DeleteProperty(JSContext *cx, HandleValue v, HandlePropertyName name, bool *bp)
 {
     RootedObject obj(cx, ToObjectFromStack(cx, v));
     if (!obj)
         return false;
 
-    if (!JSObject::deleteProperty(cx, obj, name, bp))
+    RootedId id(cx, NameToId(name));
+    if (!JSObject::deleteGeneric(cx, obj, id, bp))
         return false;
 
     if (strict && !*bp) {
         obj->reportNotConfigurable(cx, NameToId(name));
         return false;
     }
     return true;
 }
@@ -3700,26 +3698,23 @@ template bool js::DeleteProperty<false>(
 template <bool strict>
 bool
 js::DeleteElement(JSContext *cx, HandleValue val, HandleValue index, bool *bp)
 {
     RootedObject obj(cx, ToObjectFromStack(cx, val));
     if (!obj)
         return false;
 
-    if (!JSObject::deleteByValue(cx, obj, index, bp))
+    RootedId id(cx);
+    if (!ValueToId<CanGC>(cx, index, &id))
+        return false;
+    if (!JSObject::deleteGeneric(cx, obj, id, bp))
         return false;
 
     if (strict && !*bp) {
-        // XXX This observably calls ToString(propval).  We should convert to
-        //     PropertyKey and use that to delete, and to report an error if
-        //     necessary!
-        RootedId id(cx);
-        if (!ValueToId<CanGC>(cx, index, &id))
-            return false;
         obj->reportNotConfigurable(cx, id);
         return false;
     }
     return true;
 }
 
 template bool js::DeleteElement<true> (JSContext *, HandleValue, HandleValue, bool *succeeded);
 template bool js::DeleteElement<false>(JSContext *, HandleValue, HandleValue, bool *succeeded);
@@ -3810,17 +3805,18 @@ js::DeleteNameOperation(JSContext *cx, H
 
     if (!scope) {
         // Return true for non-existent names.
         res.setBoolean(true);
         return true;
     }
 
     bool succeeded;
-    if (!JSObject::deleteProperty(cx, scope, name, &succeeded))
+    RootedId id(cx, NameToId(name));
+    if (!JSObject::deleteGeneric(cx, scope, id, &succeeded))
         return false;
     res.setBoolean(succeeded);
     return true;
 }
 
 bool
 js::ImplicitThisOperation(JSContext *cx, HandleObject scopeObj, HandlePropertyName name,
                           MutableHandleValue res)
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -40,30 +40,30 @@ PropDesc::setUndefined()
     isUndefined_ = true;
 }
 
 
 bool
 PropDesc::checkGetter(JSContext *cx)
 {
     if (hasGet_) {
-        if (!js_IsCallable(get_) && !get_.isUndefined()) {
+        if (!IsCallable(get_) && !get_.isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_getter_str);
             return false;
         }
     }
     return true;
 }
 
 bool
 PropDesc::checkSetter(JSContext *cx)
 {
     if (hasSet_) {
-        if (!js_IsCallable(set_) && !set_.isUndefined()) {
+        if (!IsCallable(set_) && !set_.isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_setter_str);
             return false;
         }
     }
     return true;
 }
 
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -529,29 +529,20 @@ with_GetGenericAttributes(JSContext *cx,
 static bool
 with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return JSObject::setGenericAttributes(cx, actual, id, attrsp);
 }
 
 static bool
-with_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                    bool *succeeded)
+with_DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
-    return JSObject::deleteProperty(cx, actual, name, succeeded);
-}
-
-static bool
-with_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                   bool *succeeded)
-{
-    RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
-    return JSObject::deleteElement(cx, actual, index, succeeded);
+    return JSObject::deleteGeneric(cx, actual, id, succeeded);
 }
 
 static JSObject *
 with_ThisObject(JSContext *cx, HandleObject obj)
 {
     return &obj->as<DynamicWithObject>().withThis();
 }
 
@@ -597,18 +588,17 @@ const Class DynamicWithObject::class_ = 
         with_GetGeneric,
         with_GetProperty,
         with_GetElement,
         with_SetGeneric,
         with_SetProperty,
         with_SetElement,
         with_GetGenericAttributes,
         with_SetGenericAttributes,
-        with_DeleteProperty,
-        with_DeleteElement,
+        with_DeleteGeneric,
         nullptr, nullptr,    /* watch/unwatch */
         nullptr,             /* slice */
         nullptr,             /* enumerate (native enumeration of target doesn't work) */
         with_ThisObject,
     }
 };
 
 /*****************************************************************************/
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -83,19 +83,25 @@ js::intrinsic_ToString(JSContext *cx, un
     args.rval().setString(str);
     return true;
 }
 
 bool
 js::intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    Value val = args[0];
-    bool isCallable = val.isObject() && val.toObject().isCallable();
-    args.rval().setBoolean(isCallable);
+    args.rval().setBoolean(IsCallable(args[0]));
+    return true;
+}
+
+static bool
+intrinsic_IsConstructor(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    args.rval().setBoolean(IsConstructor(args[0]));
     return true;
 }
 
 bool
 js::intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() >= 1);
@@ -449,55 +455,53 @@ js::intrinsic_UnsafePutElements(JSContex
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 bool
-js::intrinsic_DefineValueProperty(JSContext *cx, unsigned argc, Value *vp)
+js::intrinsic_DefineDataProperty(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     MOZ_ASSERT(args.length() == 4);
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[3].isInt32());
 
     RootedObject obj(cx, &args[0].toObject());
-    if (obj->is<ProxyObject>()) {
-        JS_ReportError(cx, "_DefineValueProperty can't be used on proxies");
-        return false;
-    }
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[1], &id))
         return false;
     RootedValue value(cx, args[2]);
     unsigned attributes = args[3].toInt32();
 
-    unsigned resolvedAttributes = JSPROP_PERMANENT | JSPROP_READONLY;
+    Rooted<PropDesc> desc(cx);
 
     MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
-               "_DefineValueProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
-    if (attributes & ATTR_ENUMERABLE)
-        resolvedAttributes |= JSPROP_ENUMERATE;
+               "_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
+    PropDesc::Enumerability enumerable =
+        PropDesc::Enumerability(bool(attributes & ATTR_ENUMERABLE));
 
     MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
-               "_DefineValueProperty must receive either ATTR_CONFIGURABLE xor "
+               "_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
                "ATTR_NONCONFIGURABLE");
-    if (attributes & ATTR_CONFIGURABLE)
-        resolvedAttributes &= ~JSPROP_PERMANENT;
+    PropDesc::Configurability configurable =
+        PropDesc::Configurability(bool(attributes & ATTR_CONFIGURABLE));
 
     MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
-               "_DefineValueProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
-    if (attributes & ATTR_WRITABLE)
-        resolvedAttributes &= ~JSPROP_READONLY;
+               "_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
+    PropDesc::Writability writable =
+        PropDesc::Writability(bool(attributes & ATTR_WRITABLE));
 
-    return JSObject::defineGeneric(cx, obj, id, value, JS_PropertyStub, JS_StrictPropertyStub,
-                                   resolvedAttributes);
+    desc.set(PropDesc(value, writable, enumerable, configurable));
+
+    bool result;
+    return DefineProperty(cx, obj, id, desc, true, &result);
 }
 
 bool
 js::intrinsic_UnsafeSetReservedSlot(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() == 3);
     JS_ASSERT(args[0].isObject());
@@ -752,25 +756,26 @@ intrinsic_RuntimeDefaultLocale(JSContext
     return true;
 }
 
 static const JSFunctionSpec intrinsic_functions[] = {
     JS_FN("ToObject",                intrinsic_ToObject,                1,0),
     JS_FN("ToInteger",               intrinsic_ToInteger,               1,0),
     JS_FN("ToString",                intrinsic_ToString,                1,0),
     JS_FN("IsCallable",              intrinsic_IsCallable,              1,0),
+    JS_FN("IsConstructor",           intrinsic_IsConstructor,           1,0),
     JS_FN("ThrowError",              intrinsic_ThrowError,              4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("SetScriptHints",          intrinsic_SetScriptHints,          2,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
 
     JS_FN("UnsafePutElements",       intrinsic_UnsafePutElements,       3,0),
-    JS_FN("_DefineValueProperty",    intrinsic_DefineValueProperty,     4,0),
+    JS_FN("_DefineDataProperty",     intrinsic_DefineDataProperty,      4,0),
     JS_FN("UnsafeSetReservedSlot",   intrinsic_UnsafeSetReservedSlot,   3,0),
     JS_FN("UnsafeGetReservedSlot",   intrinsic_UnsafeGetReservedSlot,   2,0),
     JS_FN("HaveSameClass",           intrinsic_HaveSameClass,           2,0),
     JS_FN("IsPackedArray",           intrinsic_IsPackedArray,           1,0),
 
     JS_FN("GetIteratorPrototype",    intrinsic_GetIteratorPrototype,    0,0),
 
     JS_FN("NewArrayIterator",        intrinsic_NewArrayIterator,        0,0),
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -132,16 +132,17 @@ namespace jit {
     _(IrregexpExecute)                                \
     _(VM)                                             \
                                                       \
     /* Specific passes during ion compilation */      \
     _(SplitCriticalEdges)                             \
     _(RenumberBlocks)                                 \
     _(DominatorTree)                                  \
     _(PhiAnalysis)                                    \
+    _(MakeLoopsContiguous)                            \
     _(ApplyTypes)                                     \
     _(ParallelSafetyAnalysis)                         \
     _(AliasAnalysis)                                  \
     _(GVN)                                            \
     _(UCE)                                            \
     _(LICM)                                           \
     _(RangeAnalysis)                                  \
     _(EffectiveAddressAnalysis)                       \
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -692,18 +692,17 @@ const XPCWrappedNativeJSClass XPC_WN_NoH
         nullptr, // getGeneric
         nullptr, // getProperty
         nullptr, // getElement
         nullptr, // setGeneric
         nullptr, // setProperty
         nullptr, // setElement
         nullptr, // getGenericAttributes
         nullptr, // setGenericAttributes
-        nullptr, // deleteProperty
-        nullptr, // deleteElement
+        nullptr, // deleteGeneric
         nullptr, nullptr, // watch/unwatch
         nullptr, // slice
         XPC_WN_JSOp_Enumerate,
         XPC_WN_JSOp_ThisObject,
     }
   },
   0 // interfacesBitmap
 };
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -907,18 +907,17 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS
         nullptr, /* getGeneric    */                                          \
         nullptr, /* getProperty    */                                         \
         nullptr, /* getElement    */                                          \
         nullptr, /* setGeneric    */                                          \
         nullptr, /* setProperty    */                                         \
         nullptr, /* setElement    */                                          \
         nullptr, /* getGenericAttributes  */                                  \
         nullptr, /* setGenericAttributes  */                                  \
-        nullptr, /* deleteProperty */                                         \
-        nullptr, /* deleteElement */                                          \
+        nullptr, /* deleteGeneric */                                          \
         nullptr, nullptr, /* watch/unwatch */                                 \
         nullptr, /* slice */                                                  \
         XPC_WN_JSOp_Enumerate,                                                \
         XPC_WN_JSOp_ThisObject,                                               \
     }
 
 #define XPC_WN_NoCall_ObjectOps                                               \
     {                                                                         \
@@ -931,18 +930,17 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS
         nullptr, /* getGeneric    */                                          \
         nullptr, /* getProperty    */                                         \
         nullptr, /* getElement    */                                          \
         nullptr, /* setGeneric    */                                          \
         nullptr, /* setProperty    */                                         \
         nullptr, /* setElement    */                                          \
         nullptr, /* getGenericAttributes  */                                  \
         nullptr, /* setGenericAttributes  */                                  \
-        nullptr, /* deleteProperty */                                         \
-        nullptr, /* deleteElement */                                          \
+        nullptr, /* deleteGeneric */                                          \
         nullptr, nullptr, /* watch/unwatch */                                 \
         nullptr, /* slice */                                                  \
         XPC_WN_JSOp_Enumerate,                                                \
         XPC_WN_JSOp_ThisObject,                                               \
     }
 
 // Maybe this macro should check for class->enumerate ==
 // XPC_WN_Shared_Proto_Enumerate or something rather than checking for
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1176,26 +1176,40 @@ nsDisplayList::ComputeVisibilityForSubli
       // If we're in a displayport, we need to make sure that fixed position
       // items do not subtract from the visible region, as async scrolling
       // may expose these occluded areas.
       // If the item is fixed pos in the same document as the displayport
       // then don't let it occlude this list. The only other case possible
       // is that the fixed pos content is in a child document, in which it
       // would scroll with the rest of the content.
       bool occlude = true;
+      nsIPresShell* presShell = nullptr;
       if (aDisplayPortFrame && item->IsInFixedPos()) {
         if (item->Frame()->PresContext() == aDisplayPortFrame->PresContext()) {
           occlude = false;
+          presShell = aDisplayPortFrame->PresContext()->PresShell();
         }
       }
 
+      nsRegion opaque = TreatAsOpaque(item, aBuilder);
       if (occlude) {
-        nsRegion opaque = TreatAsOpaque(item, aBuilder);
         // Subtract opaque item from the visible region
         aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
+      } else if (presShell &&
+                 presShell->IsScrollPositionClampingScrollPortSizeSet() &&
+                 !opaque.IsEmpty()) {
+        // We make an exception if the fixed position item would fully occlude
+        // the scroll position clamping scroll-port. In that case, it's very
+        // unlikely that it will become visible via async scrolling, so we let
+        // it occlude.
+        nsRect scrollClampingScrollPort(nsPoint(0, 0),
+          presShell->GetScrollPositionClampingScrollPortSize());
+        if (opaque.Contains(scrollClampingScrollPort)) {
+          aVisibleRegion->SetEmpty();
+        }
       }
 
       if (aBuilder->NeedToForceTransparentSurfaceForItem(item) ||
           (list && list->NeedsTransparentSurface())) {
         forceTransparentSurface = true;
       }
     }
     AppendToBottom(item);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5048,17 +5048,17 @@ nsLayoutUtils::DrawPixelSnapped(nsRender
                    drawingParams.mSubimage.width, drawingParams.mSubimage.height);
 
   NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
                "We must be allowed to sample *some* source pixels!");
 
   gfxUtils::DrawPixelSnapped(ctx, aDrawable,
                              drawingParams.mUserSpaceToImageSpace, subimage,
                              sourceRect, imageRect, drawingParams.mFillRect,
-                             gfxImageFormat::ARGB32, aFilter);
+                             gfx::SurfaceFormat::B8G8R8A8, aFilter);
 }
 
 /* static */ nsresult
 nsLayoutUtils::DrawSingleUnscaledImage(nsRenderingContext* aRenderingContext,
                                        imgIContainer*       aImage,
                                        GraphicsFilter       aGraphicsFilter,
                                        const nsPoint&       aDest,
                                        const nsRect*        aDirty,
--- a/memory/mozalloc/VolatileBuffer.h
+++ b/memory/mozalloc/VolatileBuffer.h
@@ -74,48 +74,69 @@ private:
   bool mHeap;
   bool mFirstLock;
 #endif
 };
 
 class VolatileBufferPtr_base {
 public:
   explicit VolatileBufferPtr_base(VolatileBuffer* vbuf) : mVBuf(vbuf) {
-    if (vbuf) {
-      mPurged = !vbuf->Lock(&mMapping);
-    } else {
-      mMapping = nullptr;
-      mPurged = false;
-    }
+    Lock();
   }
 
   ~VolatileBufferPtr_base() {
-    if (mVBuf) {
-      mVBuf->Unlock();
-    }
+    Unlock();
   }
 
   bool WasBufferPurged() const {
     return mPurged;
   }
 
 protected:
   void* mMapping;
 
+  void Set(VolatileBuffer* vbuf) {
+    Unlock();
+    mVBuf = vbuf;
+    Lock();
+  }
+
 private:
   RefPtr<VolatileBuffer> mVBuf;
   bool mPurged;
+
+  void Lock() {
+    if (mVBuf) {
+      mPurged = !mVBuf->Lock(&mMapping);
+    } else {
+      mMapping = nullptr;
+      mPurged = false;
+    }
+  }
+
+  void Unlock() {
+    if (mVBuf) {
+      mVBuf->Unlock();
+    }
+  }
 };
 
 template <class T>
 class VolatileBufferPtr : public VolatileBufferPtr_base
 {
 public:
   explicit VolatileBufferPtr(VolatileBuffer* vbuf) : VolatileBufferPtr_base(vbuf) {}
+  VolatileBufferPtr() : VolatileBufferPtr_base(nullptr) {}
 
   operator T*() const {
     return (T*) mMapping;
   }
+
+  void operator =(VolatileBuffer* vbuf) {
+    Set(vbuf);
+  }
+private:
+  VolatileBufferPtr(VolatileBufferPtr const& vbufptr) MOZ_DELETE;
 };
 
 }; /* namespace mozilla */
 
 #endif /* mozalloc_VolatileBuffer_h */
--- a/mfbt/Atomics.h
+++ b/mfbt/Atomics.h
@@ -865,23 +865,23 @@ struct IntrinsicAddSub : public Intrinsi
 template<typename T>
 struct IntrinsicAddSub<T*> : public IntrinsicApplyHelper<T*>
 {
   typedef typename IntrinsicApplyHelper<T*>::ValueType ValueType;
 
   static ValueType add(ValueType& aPtr, ptrdiff_t aAmount)
   {
     return applyBinaryFunction(&Primitives::add, aPtr,
-                               (ValueType)(aAmount * sizeof(ValueType)));
+                               (ValueType)(aAmount * sizeof(T)));
   }
 
   static ValueType sub(ValueType& aPtr, ptrdiff_t aAmount)
   {
     return applyBinaryFunction(&Primitives::sub, aPtr,
-                               (ValueType)(aAmount * sizeof(ValueType)));
+                               (ValueType)(aAmount * sizeof(T)));
   }
 };
 
 template<typename T>
 struct IntrinsicIncDec : public IntrinsicAddSub<T>
 {
   typedef typename IntrinsicAddSub<T>::ValueType ValueType;
   static ValueType inc(ValueType& aPtr) { return add(aPtr, 1); }
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -930,18 +930,32 @@ Http2Session::CleanupStream(Http2Stream 
 
   CloseStream(aStream, aResult);
 
   // Remove the stream from the ID hash table and, if an even id, the pushed
   // table too.
   uint32_t id = aStream->StreamID();
   if (id > 0) {
     mStreamIDHash.Remove(id);
-    if (!(id & 1))
+    if (!(id & 1)) {
       mPushedStreams.RemoveElement(aStream);
+      Http2PushedStream *pushStream = static_cast<Http2PushedStream *>(aStream);
+      nsAutoCString hashKey;
+      pushStream->GetHashKey(hashKey);
+      nsILoadGroupConnectionInfo *loadGroupCI = aStream->LoadGroupConnectionInfo();
+      if (loadGroupCI) {
+        SpdyPushCache *cache = nullptr;
+        loadGroupCI->GetSpdyPushCache(&cache);
+        if (cache) {
+          Http2PushedStream *trash = cache->RemovePushedStreamHttp2(hashKey);
+          LOG3(("Http2Session::CleanupStream %p aStream=%p pushStream=%p trash=%p",
+                this, aStream, pushStream, trash));
+        }
+      }
+    }
   }
 
   RemoveStreamFromQueues(aStream);
 
   // removing from the stream transaction hash will
   // delete the Http2Stream and drop the reference to
   // its transaction
   mStreamTransactionHash.Remove(aStream->Transaction());
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -386,16 +386,23 @@ NSSCertDBTrustDomain::CheckRevocation(
     }
     if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure from cached "
               "response after OCSP request failure"));
       PR_SetError(cachedResponseErrorCode, 0);
       return SECFailure;
     }
+    if (stapledOCSPResponse) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: returning SECFailure from expired "
+              "stapled response after OCSP request failure"));
+      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+      return SECFailure;
+    }
 
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: returning SECSuccess after "
             "OCSP request failure"));
     return SECSuccess; // Soft fail -> success :(
   }
 
   SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time,
@@ -409,20 +416,28 @@ NSSCertDBTrustDomain::CheckRevocation(
   }
 
   PRErrorCode error = PR_GetError();
   if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
       error == SEC_ERROR_REVOKED_CERTIFICATE) {
     return rv;
   }
 
+  if (stapledOCSPResponse) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
+            "response after OCSP request verification failure"));
+    PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+    return SECFailure;
+  }
+
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
          ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
-  return SECSuccess;
+  return SECSuccess; // Soft fail -> success :(
 }
 
 SECStatus
 NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
   uint16_t maxLifetimeInDays, const SECItem* encodedResponse,
   EncodedResponseSource responseSource)
 {
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -43,19 +43,24 @@ let oldValidityPeriodOCSPResponseGood = 
 // Fresh signature, certificate is revoked.
 let ocspResponseRevoked = ocspResponses[3];
 // Fresh signature, certificate is unknown.
 let ocspResponseUnknown = ocspResponses[4];
 
 function run_test() {
   let ocspResponder = new HttpServer();
   ocspResponder.registerPrefixHandler("/", function(request, response) {
-    response.setStatusLine(request.httpVersion, 200, "OK");
-    response.setHeader("Content-Type", "application/ocsp-response");
-    response.write(gCurrentOCSPResponse);
+    if (gCurrentOCSPResponse) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      response.setHeader("Content-Type", "application/ocsp-response");
+      response.write(gCurrentOCSPResponse);
+    } else {
+      response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+      response.write("Internal Server Error");
+    }
     gOCSPRequestCount++;
   });
   ocspResponder.start(8080);
   add_tls_server_setup("OCSPStaplingServer");
   add_tests_in_mode(true);
   add_tests_in_mode(false);
   add_test(function () { ocspResponder.stop(run_next_test); });
   add_test(check_ocsp_stapling_telemetry);
@@ -78,33 +83,62 @@ function add_tests_in_mode(useMozillaPKI
   // staples an expired OCSP response. The certificate has not expired.
   // For ocsp-stapling-expired-fresh-ca.example.com, the OCSP stapling
   // server staples an OCSP response with a recent signature but with an
   // out-of-date validity period. The certificate has not expired.
   add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK,
                 ocspResponseGood);
   add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
                 ocspResponseGood);
-  add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK,
+  // With mozilla::pkix, if we can't fetch a more recent response when
+  // given an expired stapled response, we terminate the connection.
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
                 expiredOCSPResponseGood);
-  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
                 expiredOCSPResponseGood);
-  add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK,
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
+                oldValidityPeriodOCSPResponseGood);
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
                 oldValidityPeriodOCSPResponseGood);
-  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
-                oldValidityPeriodOCSPResponseGood);
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
+                null);
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
+                null);
+  // Of course, if the newer response indicates Revoked or Unknown,
+  // that status must be returned.
   add_ocsp_test("ocsp-stapling-expired.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                 ocspResponseRevoked);
   add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                 ocspResponseRevoked);
   add_ocsp_test("ocsp-stapling-expired.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT),
                 ocspResponseUnknown);
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
+                getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT),
+                ocspResponseUnknown);
 
   if (useMozillaPKIX) {
     // These tests are verifying that an valid but very old response
     // is rejected as a valid stapled response, requiring a fetch
     // from the ocsp responder.
     add_ocsp_test("ocsp-stapling-ancient-valid.example.com", Cr.NS_OK,
                   ocspResponseGood);
     add_ocsp_test("ocsp-stapling-ancient-valid.example.com",
@@ -119,16 +153,13 @@ function add_tests_in_mode(useMozillaPKI
 function check_ocsp_stapling_telemetry() {
   let histogram = Cc["@mozilla.org/base/telemetry;1"]
                     .getService(Ci.nsITelemetry)
                     .getHistogramById("SSL_OCSP_STAPLING")
                     .snapshot();
   do_check_eq(histogram.counts[0], 2 * 0); // histogram bucket 0 is unused
   do_check_eq(histogram.counts[1], 2 * 0); // 0 connections with a good response
   do_check_eq(histogram.counts[2], 2 * 0); // 0 connections with no stapled resp.
-  do_check_eq(histogram.counts[3], 2 * 9 + 3); // 9 connections with an expired response
-                                               // 3 connection with a response
-                                               // considered expired due to being
-                                               // old but having an overly-long
-                                               // validity period
+  do_check_eq(histogram.counts[3], 2 * 12 + 3); // 12 connections with an expired response
+                                                // +3 more mozilla::pkix-only expired responses
   do_check_eq(histogram.counts[4], 2 * 0); // 0 connections with bad responses
   run_next_test();
 }
--- a/toolkit/components/startup/nsAppStartup.cpp
+++ b/toolkit/components/startup/nsAppStartup.cpp
@@ -607,29 +607,30 @@ nsAppStartup::GetInterrupted(bool *aInte
 //
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent,
                                  uint32_t aChromeFlags,
                                  nsIWebBrowserChrome **_retval)
 {
   bool cancel;
-  return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, &cancel, _retval);
+  return CreateChromeWindow2(aParent, aChromeFlags, 0, 0, nullptr, &cancel, _retval);
 }
 
 
 //
 // nsAppStartup->nsIWindowCreator2
 //
 
 NS_IMETHODIMP
 nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent,
                                   uint32_t aChromeFlags,
                                   uint32_t aContextFlags,
                                   nsIURI *aURI,
+                                  nsITabParent *aOpeningTab,
                                   bool *aCancel,
                                   nsIWebBrowserChrome **_retval)
 {
   NS_ENSURE_ARG_POINTER(aCancel);
   NS_ENSURE_ARG_POINTER(_retval);
   *aCancel = false;
   *_retval = 0;
 
@@ -639,33 +640,34 @@ nsAppStartup::CreateChromeWindow2(nsIWeb
 
   nsCOMPtr<nsIXULWindow> newWindow;
 
   if (aParent) {
     nsCOMPtr<nsIXULWindow> xulParent(do_GetInterface(aParent));
     NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work.");
 
     if (xulParent)
-      xulParent->CreateNewWindow(aChromeFlags, getter_AddRefs(newWindow));
+      xulParent->CreateNewWindow(aChromeFlags, aOpeningTab, getter_AddRefs(newWindow));
     // And if it fails, don't try again without a parent. It could fail
     // intentionally (bug 115969).
   } else { // try using basic methods:
     /* You really shouldn't be making dependent windows without a parent.
       But unparented modal (and therefore dependent) windows happen
       in our codebase, so we allow it after some bellyaching: */
     if (aChromeFlags & nsIWebBrowserChrome::CHROME_DEPENDENT)
       NS_WARNING("dependent window created without a parent");
 
     nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
     if (!appShell)
       return NS_ERROR_FAILURE;
-    
+
     appShell->CreateTopLevelWindow(0, 0, aChromeFlags,
                                    nsIAppShellService::SIZE_TO_CONTENT,
                                    nsIAppShellService::SIZE_TO_CONTENT,
+                                   aOpeningTab,
                                    getter_AddRefs(newWindow));
   }
 
   // if anybody gave us anything to work with, use it
   if (newWindow) {
     newWindow->SetContextFlags(aContextFlags);
     nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(newWindow));
     if (thing)
--- a/widget/tests/TestAppShellSteadyState.cpp
+++ b/widget/tests/TestAppShellSteadyState.cpp
@@ -402,17 +402,17 @@ Test4Internal(nsIAppShell* aAppShell)
   if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), "about:", nullptr))) {
     fail("Failed to create new uri");
     return false;
   }
 
   uint32_t flags = nsIWebBrowserChrome::CHROME_DEFAULT;
 
   nsCOMPtr<nsIXULWindow> xulWindow;
-  if (NS_FAILED(appService->CreateTopLevelWindow(nullptr, uri, flags, 100, 100,
+  if (NS_FAILED(appService->CreateTopLevelWindow(nullptr, uri, flags, 100, 100, nullptr,
                                                  getter_AddRefs(xulWindow)))) {
     fail("Failed to create new window");
     return false;
   }
 
   nsCOMPtr<nsIDOMWindow> window = do_GetInterface(xulWindow);
   if (!window) {
     fail("Can't get dom window!");
--- a/xpfe/appshell/public/nsIAppShellService.idl
+++ b/xpfe/appshell/public/nsIAppShellService.idl
@@ -5,24 +5,25 @@
 
 #include "nsISupports.idl"
 
 interface nsIXULWindow;
 interface nsIWebNavigation;
 interface nsIURI;
 interface nsIDOMWindow;
 interface nsIAppShell;
+interface nsITabParent;
 
 [ptr] native JSContext(JSContext);
 
 %{C++
 #include "js/TypeDecls.h"
 %}
 
-[scriptable, uuid(2fa2f813-c216-4efb-8a8c-de60108ce5e5)]
+[scriptable, uuid(41a2f0c6-3ca1-44f9-8efa-744a43aa399d)]
 interface nsIAppShellService : nsISupports
 {
   /**
    * Create a window, which will be initially invisible.
    * @param aParent the parent window.  Can be null.
    * @param aUrl the contents of the new window.
    * @param aChromeMask chrome flags affecting the kind of OS border
    *                    given to the window. see nsIBrowserWindow for
@@ -30,24 +31,26 @@ interface nsIAppShellService : nsISuppor
    * @param aCallbacks interface providing C++ hooks for window initialization
    *                   before the window is made visible.  Can be null.
    *                   Deprecated.
    * @param aInitialWidth width, in pixels, of the window.  Width of window
    *                      at creation.  Can be overridden by the "width"
    *                      tag in the XUL.  Set to NS_SIZETOCONTENT to force
    *                      the window to wrap to its contents.
    * @param aInitialHeight like aInitialWidth, but subtly different.
-   * @param aResult the newly created window is returned here.
+   * @param aOpeningTab The TabParent that requested that this window be opened.
+   *                    Can be left null.
    */
   const long SIZE_TO_CONTENT = -1;
   nsIXULWindow createTopLevelWindow(in nsIXULWindow aParent,
                                     in nsIURI aUrl, 
                                     in uint32_t aChromeMask,
                                     in long aInitialWidth,
-                                    in long aInitialHeight);
+                                    in long aInitialHeight,
+                                    in nsITabParent aOpeningTab);
 
   /**
    * This is the constructor for creating an invisible DocShell.
    * It is used to simulate DOM windows without an actual physical
    * representation.
    * @param aIsChrome Set true if you want to use it for chrome content.
    */
   nsIWebNavigation createWindowlessBrowser([optional] in bool aIsChrome);
--- a/xpfe/appshell/public/nsIXULWindow.idl
+++ b/xpfe/appshell/public/nsIXULWindow.idl