Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 29 Jul 2014 17:01:18 -0700
changeset 196658 f61a27b00e05b7e0799fca19ea8194f975afa872
parent 196563 3b682051f3ad74bb34d4310e0fa3186074365d46 (current diff)
parent 196657 17d70e1ff57b28a4208e37cefcab1ff9deaa7464 (diff)
child 196667 1bfa75463a211bbe7c911e72482ea8e3dff79a19
child 196702 6c4af376d77abfa1ca5a22c4682c8769b46ac255
child 196738 d80e2e170f4124d600013300e227aee7f81ad7bc
push id27220
push userkwierso@gmail.com
push dateWed, 30 Jul 2014 00:01:50 +0000
treeherdermozilla-central@f61a27b00e05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
f61a27b00e05 / 34.0a1 / 20140730030201 / files
nightly linux64
f61a27b00e05 / 34.0a1 / 20140730030201 / files
nightly mac
f61a27b00e05 / 34.0a1 / 20140730030201 / files
nightly win32
f61a27b00e05 / 34.0a1 / 20140730030201 / files
nightly win64
f61a27b00e05 / 34.0a1 / 20140730030201 / 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
accessible/tests/mochitest/actions/moz.build
accessible/tests/mochitest/attributes/moz.build
accessible/tests/mochitest/bounds/moz.build
accessible/tests/mochitest/editabletext/moz.build
accessible/tests/mochitest/elm/moz.build
accessible/tests/mochitest/focus/moz.build
accessible/tests/mochitest/hittest/moz.build
accessible/tests/mochitest/hyperlink/moz.build
accessible/tests/mochitest/hypertext/moz.build
accessible/tests/mochitest/jsat/moz.build
accessible/tests/mochitest/name/moz.build
accessible/tests/mochitest/pivot/moz.build
accessible/tests/mochitest/relations/moz.build
accessible/tests/mochitest/role/moz.build
accessible/tests/mochitest/scroll/moz.build
accessible/tests/mochitest/selectable/moz.build
accessible/tests/mochitest/states/moz.build
accessible/tests/mochitest/table/moz.build
accessible/tests/mochitest/text/moz.build
accessible/tests/mochitest/textattrs/moz.build
accessible/tests/mochitest/textcaret/moz.build
accessible/tests/mochitest/textselection/moz.build
accessible/tests/mochitest/treeupdate/moz.build
accessible/tests/mochitest/value/moz.build
accessible/tests/moz.build
browser/components/loop/content/shared/css/readme.html
browser/devtools/projecteditor/test/moz.build
browser/extensions/pdfjs/test/moz.build
browser/fuel/test/moz.build
browser/modules/test/chrome/moz.build
browser/modules/test/moz.build
browser/modules/test/unit/moz.build
content/media/webrtc/LoadManager.cpp
content/media/webrtc/LoadManager.h
content/media/webrtc/LoadManagerFactory.cpp
content/media/webrtc/LoadManagerFactory.h
content/media/webrtc/LoadMonitor.cpp
content/media/webrtc/LoadMonitor.h
dom/browser-element/BrowserElementChildPreload.js
dom/browser-element/BrowserElementParent.jsm
dom/push/tests/moz.build
dom/wappush/interfaces/moz.build
dom/webidl/moz.build
media/libpng/arm/moz.build
view/public/moz.build
view/public/nsView.h
view/public/nsViewManager.h
view/src/Makefile.in
view/src/moz.build
view/src/nsView.cpp
view/src/nsViewManager.cpp
--- a/accessible/base/TextRange.cpp
+++ b/accessible/base/TextRange.cpp
@@ -5,17 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TextRange.h"
 
 #include "Accessible-inl.h"
 #include "HyperTextAccessible.h"
 #include "nsAccUtils.h"
 
-using namespace mozilla::a11y;
+namespace mozilla {
+namespace a11y {
 
 ////////////////////////////////////////////////////////////////////////////////
 // TextPoint
 
 bool
 TextPoint::operator <(const TextPoint& aPoint) const
 {
   if (mContainer == aPoint.mContainer)
@@ -289,8 +290,11 @@ TextRange::TextInternal(nsAString& aText
 
 void
 TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
                         HyperTextAccessible& aContainer, int32_t aOffset,
                         HyperTextAccessible* aStopContainer, int32_t aStopOffset)
 {
 
 }
+
+} // namespace a11y
+} // namespace mozilla
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -15,9 +15,9 @@ elif toolkit == 'cocoa':
 else:
     DIRS += ['other']
 
 DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom']
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['xul']
 
-TEST_DIRS += ['tests']
+TEST_DIRS += ['tests/mochitest']
deleted file mode 100644
--- a/accessible/tests/mochitest/actions/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/attributes/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/bounds/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/editabletext/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/elm/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/focus/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/hittest/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/hyperlink/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/hypertext/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/jsat/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
--- a/accessible/tests/mochitest/moz.build
+++ b/accessible/tests/mochitest/moz.build
@@ -1,39 +1,36 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-DIRS += [
-    'actions',
-    'attributes',
-    'bounds',
-    'editabletext',
-    'elm',
-    'focus',
-    'hittest',
-    'hyperlink',
-    'hypertext',
-    'jsat',
-    'name',
-    'pivot',
-    'relations',
-    'role',
-    'scroll',
-    'selectable',
-    'states',
-    'table',
-    'text',
-    'textattrs',
-    'textcaret',
-    'textselection',
-    'treeupdate',
-    'value',
-]
-
 A11Y_MANIFESTS += [
     'a11y.ini',
+    'actions/a11y.ini',
+    'attributes/a11y.ini',
+    'bounds/a11y.ini',
+    'editabletext/a11y.ini',
+    'elm/a11y.ini',
     'events/a11y.ini',
+    'focus/a11y.ini',
+    'hittest/a11y.ini',
+    'hyperlink/a11y.ini',
+    'hypertext/a11y.ini',
+    'jsat/a11y.ini',
+    'name/a11y.ini',
+    'pivot/a11y.ini',
+    'relations/a11y.ini',
+    'role/a11y.ini',
+    'scroll/a11y.ini',
+    'selectable/a11y.ini',
+    'states/a11y.ini',
+    'table/a11y.ini',
+    'text/a11y.ini',
+    'textattrs/a11y.ini',
+    'textcaret/a11y.ini',
     'textrange/a11y.ini',
+    'textselection/a11y.ini',
     'tree/a11y.ini',
+    'treeupdate/a11y.ini',
+    'value/a11y.ini',
 ]
deleted file mode 100644
--- a/accessible/tests/mochitest/name/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/pivot/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/relations/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/role/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/scroll/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/selectable/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/states/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/table/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/text/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/textattrs/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/textcaret/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/textselection/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/treeupdate/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/mochitest/value/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-A11Y_MANIFESTS += ['a11y.ini']
-
deleted file mode 100644
--- a/accessible/tests/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-DIRS += ['mochitest']
-
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -341,16 +341,17 @@ var shell = {
     chromeEventHandler.addEventListener('keyup', this, true);
 
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('mozfullscreenchange', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
+    this.contentBrowser.addEventListener('mozbrowserselectionchange', this, true);
 
     CustomEventManager.init();
     WebappsHelper.init();
     UserAgentOverrides.init();
     IndexedDBPromptHelper.init();
     CaptivePortalLoginHelper.init();
 
     this.contentBrowser.src = homeURL;
@@ -367,16 +368,17 @@ var shell = {
     window.removeEventListener('unload', this);
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('mozfullscreenchange', this);
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
+    this.contentBrowser.removeEventListener('mozbrowserselectionchange', this, true);
     ppmm.removeMessageListener("content-handler", this);
 
     UserAgentOverrides.uninit();
     IndexedDBPromptHelper.uninit();
   },
 
   // If this key event actually represents a hardware button, filter it here
   // and send a mozChromeEvent with detail.type set to xxx-button-press or
@@ -509,16 +511,40 @@ var shell = {
       case 'mozbrowserlocationchange':
         if (content.document.location == 'about:blank') {
           return;
         }
 
         this.notifyContentStart();
        break;
 
+      case 'mozbrowserselectionchange':
+        // The mozbrowserselectionchange event, may have crossed the chrome-content boundary.
+        // This event always dispatch to shell.js. But the offset we got from this event is
+        // based on tab's coordinate. So get the actual offsets between shell and evt.target.
+        let elt = evt.target;
+        let win = elt.ownerDocument.defaultView;
+        let offsetX = win.mozInnerScreenX;
+        let offsetY = win.mozInnerScreenY;
+
+        let rect = elt.getBoundingClientRect();
+        offsetX += rect.left;
+        offsetY += rect.top;
+
+        let data = evt.detail;
+        data.offsetX = offsetX;
+        data.offsetY = offsetY;
+
+        DoCommandHelper.setEvent(evt);
+        shell.sendChromeEvent({
+          type: 'selectionchange',
+          detail: data,
+        });
+        break;
+
       case 'MozApplicationManifest':
         try {
           if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
             return;
 
           let contentWindow = evt.originalTarget.defaultView;
           let documentElement = contentWindow.document.documentElement;
           if (!documentElement)
@@ -708,16 +734,33 @@ var CustomEventManager = {
         RemoteDebugger.handleEvent(detail);
         break;
       case 'captive-portal-login-cancel':
         CaptivePortalLoginHelper.handleEvent(detail);
         break;
       case 'inputmethod-update-layouts':
         KeyboardHelper.handleEvent(detail);
         break;
+      case 'do-command':
+        DoCommandHelper.handleEvent(detail.cmd);
+        break;
+    }
+  }
+}
+
+let DoCommandHelper = {
+  _event: null,
+  setEvent: function docommand_setEvent(evt) {
+    this._event = evt;
+  },
+
+  handleEvent: function docommand_handleEvent(cmd) {
+    if (this._event) {
+      shell.sendEvent(this._event.target, 'mozdocommand', { cmd: cmd });
+      this._event = null;
     }
   }
 }
 
 var WebappsHelper = {
   _installers: {},
   _count: 0,
 
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -1157,20 +1157,35 @@ var gPluginHandler = {
       return;
 
     let propBag = aEvent.detail.QueryInterface(Ci.nsIPropertyBag2);
     let submittedReport = propBag.getPropertyAsBool("submittedCrashReport");
     let doPrompt        = true; // XXX followup for .getPropertyAsBool("doPrompt");
     let submitReports   = true; // XXX followup for .getPropertyAsBool("submitReports");
     let pluginName      = propBag.getPropertyAsAString("pluginName");
     let pluginDumpID    = propBag.getPropertyAsAString("pluginDumpID");
-    let browserDumpID   = propBag.getPropertyAsAString("browserDumpID");
+    let browserDumpID   = null;
+    let gmpPlugin       = false;
+
+    try {
+      browserDumpID = propBag.getPropertyAsAString("browserDumpID");
+    } catch (e) {
+      // For GMP crashes we don't get a browser dump.
+    }
 
-    // Remap the plugin name to a more user-presentable form.
-    pluginName = this.makeNicePluginName(pluginName);
+    try {
+      gmpPlugin = propBag.getPropertyAsBool("gmpPlugin");
+    } catch (e) {
+      // This property is only set for GMP plugins.
+    }
+
+    // For non-GMP plugins, remap the plugin name to a more user-presentable form.
+    if (!gmpPlugin) {
+      pluginName = this.makeNicePluginName(pluginName);
+    }
 
     let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
 
     let plugin = null, doc;
     if (target instanceof Ci.nsIObjectLoadingContent) {
       plugin = target;
       doc = plugin.ownerDocument;
     } else {
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -236,17 +236,17 @@ let MozLoopServiceInternal = {
 
   /**
    * Registers with the Loop server.
    *
    * @param {String} pushUrl The push url given by the push server.
    * @param {Boolean} noRetry Optional, don't retry if authentication fails.
    */
   registerWithLoopServer: function(pushUrl, noRetry) {
-    this.hawkRequest("/registration", "POST", { simple_push_url: pushUrl})
+    this.hawkRequest("/registration", "POST", { simplePushURL: pushUrl})
       .then((response) => {
         // If this failed we got an invalid token. storeSessionToken rejects
         // the gRegisteredDeferred promise for us, so here we just need to
         // early return.
         if (!this.storeSessionToken(response.headers))
           return;
 
         gRegisteredDeferred.resolve();
--- a/browser/components/loop/content/js/client.js
+++ b/browser/components/loop/content/js/client.js
@@ -112,22 +112,16 @@ loop.Client = (function($) {
         if (error) {
           this._failureHandler(cb, error);
           return;
         }
 
         try {
           var urlData = JSON.parse(responseText);
 
-          // XXX Support an alternate call_url property for
-          // backwards compatibility whilst we switch over servers.
-          // Bug 1033988 will want to remove these two lines.
-          if (urlData.call_url)
-            urlData.callUrl = urlData.call_url;
-
           cb(null, this._validate(urlData, expectedCallUrlProperties));
 
           this.mozLoop.noteCallUrlExpiry(urlData.expiresAt);
         } catch (err) {
           console.log("Error requesting call info", err);
           cb(err);
         }
       });
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -19,17 +19,17 @@ loop.conversation = (function(OT, mozL10
    * App router.
    * @type {loop.desktopRouter.DesktopConversationRouter}
    */
   var router;
 
   var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
 
     propTypes: {
-      model: React.PropTypes.func.isRequired
+      model: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {showDeclineMenu: false};
     },
 
     componentDidMount: function() {
       window.addEventListener('click', this.clickHandler);
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -19,17 +19,17 @@ loop.conversation = (function(OT, mozL10
    * App router.
    * @type {loop.desktopRouter.DesktopConversationRouter}
    */
   var router;
 
   var IncomingCallView = React.createClass({
 
     propTypes: {
-      model: React.PropTypes.func.isRequired
+      model: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {showDeclineMenu: false};
     },
 
     componentDidMount: function() {
       window.addEventListener('click', this.clickHandler);
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -72,32 +72,32 @@ loop.panel = (function(_, mozL10n) {
         'dnd-menu': true,
         'hide': !this.state.showMenu
       });
       var availabilityText = this.state.doNotDisturb ?
                               __("display_name_dnd_status") :
                               __("display_name_available_status");
 
       return (
-        React.DOM.div({className: "footer component-spacer"}, 
-          React.DOM.div({className: "do-not-disturb"}, 
-            React.DOM.p({className: "dnd-status", onClick: this.showDropdownMenu}, 
-              React.DOM.span(null, availabilityText), 
-              React.DOM.i({className: availabilityStatus})
-            ), 
-            React.DOM.ul({className: availabilityDropdown, 
-                onMouseLeave: this.hideDropdownMenu}, 
-              React.DOM.li({onClick: this.changeAvailability("available"), 
-                  className: "dnd-menu-item dnd-make-available"}, 
-                React.DOM.i({className: "status status-available"}), 
+        React.DOM.div( {className:"footer component-spacer"}, 
+          React.DOM.div( {className:"do-not-disturb"}, 
+            React.DOM.p( {className:"dnd-status", onClick:this.showDropdownMenu}, 
+              React.DOM.span(null, availabilityText),
+              React.DOM.i( {className:availabilityStatus})
+            ),
+            React.DOM.ul( {className:availabilityDropdown,
+                onMouseLeave:this.hideDropdownMenu}, 
+              React.DOM.li( {onClick:this.changeAvailability("available"),
+                  className:"dnd-menu-item dnd-make-available"}, 
+                React.DOM.i( {className:"status status-available"}),
                 React.DOM.span(null, __("display_name_available_status"))
-              ), 
-              React.DOM.li({onClick: this.changeAvailability("do-not-disturb"), 
-                  className: "dnd-menu-item dnd-make-unavailable"}, 
-                React.DOM.i({className: "status status-dnd"}), 
+              ),
+              React.DOM.li( {onClick:this.changeAvailability("do-not-disturb"),
+                  className:"dnd-menu-item dnd-make-unavailable"}, 
+                React.DOM.i( {className:"status status-dnd"}),
                 React.DOM.span(null, __("display_name_dnd_status"))
               )
             )
           )
         )
       );
     }
   });
@@ -110,36 +110,36 @@ loop.panel = (function(_, mozL10n) {
     render: function() {
       var tosHTML = __("legal_text_and_links", {
         "terms_of_use_url": "https://accounts.firefox.com/legal/terms",
         "privacy_notice_url": "www.mozilla.org/privacy/"
       });
 
       if (this.state.seenToS == "unseen") {
         navigator.mozLoop.setLoopCharPref('seenToS', 'seen');
-        return React.DOM.p({className: "terms-service", 
-                  dangerouslySetInnerHTML: {__html: tosHTML}});
+        return React.DOM.p( {className:"terms-service",
+                  dangerouslySetInnerHTML:{__html: tosHTML}});
       } else {
-        return React.DOM.div(null);
+        return React.DOM.div(null );
       }
     }
   });
 
   var PanelLayout = React.createClass({displayName: 'PanelLayout',
     propTypes: {
       summary: React.PropTypes.string.isRequired
     },
 
     render: function() {
       return (
-        React.DOM.div({className: "component-spacer share generate-url"}, 
-          React.DOM.div({className: "description"}, 
-            React.DOM.p({className: "description-content"}, this.props.summary)
-          ), 
-          React.DOM.div({className: "action"}, 
+        React.DOM.div( {className:"component-spacer share generate-url"}, 
+          React.DOM.div( {className:"description"}, 
+            React.DOM.p( {className:"description-content"}, this.props.summary)
+          ),
+          React.DOM.div( {className:"action"}, 
             this.props.children
           )
         )
       );
     }
   });
 
   var CallUrlResult = React.createClass({displayName: 'CallUrlResult',
@@ -173,18 +173,17 @@ loop.panel = (function(_, mozL10n) {
     _onCallUrlReceived: function(err, callUrlData) {
       this.props.notifier.clear();
 
       if (err) {
         this.props.notifier.errorL10n("unable_retrieve_url");
         this.setState({pending: false});
       } else {
         try {
-          var callUrl = new window.URL(callUrlData.callUrl ||
-                                       callUrlData.call_url);
+          var callUrl = new window.URL(callUrlData.callUrl);
           // XXX the current server vers does not implement the callToken field
           // but it exists in the API. This workaround should be removed in the future
           var token = callUrlData.callToken ||
                       callUrl.pathname.split('/').pop();
 
           navigator.mozLoop.setLoopCharPref('loopToken', token);
           this.setState({pending: false, callUrl: callUrl.href});
         } catch(e) {
@@ -197,20 +196,20 @@ loop.panel = (function(_, mozL10n) {
 
     render: function() {
       // XXX setting elem value from a state (in the callUrl input)
       // makes it immutable ie read only but that is fine in our case.
       // readOnly attr will suppress a warning regarding this issue
       // from the react lib.
       var cx = React.addons.classSet;
       return (
-        PanelLayout({summary: __("share_link_header_text")}, 
-          React.DOM.div({className: "invite"}, 
-            React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true", 
-                   className: cx({'pending': this.state.pending})})
+        PanelLayout( {summary:__("share_link_header_text")}, 
+          React.DOM.div( {className:"invite"}, 
+            React.DOM.input( {type:"url", value:this.state.callUrl, readOnly:"true",
+                   className:cx({'pending': this.state.pending})} )
           )
         )
       );
     }
   });
 
   /**
    * Panel view.
@@ -219,20 +218,20 @@ loop.panel = (function(_, mozL10n) {
     propTypes: {
       notifier: React.PropTypes.object.isRequired,
       client: React.PropTypes.object.isRequired
     },
 
     render: function() {
       return (
         React.DOM.div(null, 
-          CallUrlResult({client: this.props.client, 
-                       notifier: this.props.notifier}), 
-          ToSView(null), 
-          AvailabilityDropdown(null)
+          CallUrlResult( {client:this.props.client,
+                       notifier:this.props.notifier} ),
+          ToSView(null ),
+          AvailabilityDropdown(null )
         )
       );
     }
   });
 
   var PanelRouter = loop.desktopRouter.DesktopRouter.extend({
     /**
      * DOM document object.
@@ -289,18 +288,18 @@ loop.panel = (function(_, mozL10n) {
     /**
      * Resets this router to its initial state.
      */
     reset: function() {
       this._notifier.clear();
       var client = new loop.Client({
         baseServerUrl: navigator.mozLoop.serverUrl
       });
-      this.loadReactComponent(PanelView({client: client, 
-                                         notifier: this._notifier}));
+      this.loadReactComponent(PanelView( {client:client,
+                                         notifier:this._notifier} ));
     }
   });
 
   /**
    * Panel initialisation.
    */
   function init() {
     // Do the initial L10n setup, we do this before anything
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -173,18 +173,17 @@ loop.panel = (function(_, mozL10n) {
     _onCallUrlReceived: function(err, callUrlData) {
       this.props.notifier.clear();
 
       if (err) {
         this.props.notifier.errorL10n("unable_retrieve_url");
         this.setState({pending: false});
       } else {
         try {
-          var callUrl = new window.URL(callUrlData.callUrl ||
-                                       callUrlData.call_url);
+          var callUrl = new window.URL(callUrlData.callUrl);
           // XXX the current server vers does not implement the callToken field
           // but it exists in the API. This workaround should be removed in the future
           var token = callUrlData.callToken ||
                       callUrl.pathname.split('/').pop();
 
           navigator.mozLoop.setLoopCharPref('loopToken', token);
           this.setState({pending: false, callUrl: callUrl.href});
         } catch(e) {
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -3,106 +3,106 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Conversation window styles */
 
 .conversation {
   position: relative;
 }
 
-.conversation .controls {
+.conversation-toolbar {
   position: absolute;
   z-index: 999; /* required to have it superimposed to the video element */
   left: 0;
   right: 0;
   background: rgba(0, 0, 0, .70);
   border: 1px solid #5a5a5a;
   list-style-type: none;
   margin: 0;
   padding: 0;
 }
 
-.conversation .controls li {
+.conversation-toolbar li {
   float: left;
   font-size: 0px; /* prevents vertical bottom padding added to buttons in google
                      chrome */
 }
 
-.conversation .controls .btn {
+.conversation-toolbar .btn {
   width: 40px;
   height: 30px;
   background: transparent;
   background-repeat: no-repeat;
   background-position: 12px 8px;
   background-size: 14px 14px;
   border-right: 1px solid #5a5a5a;
   border-radius: 0;
   cursor: pointer;
 }
 
-.conversation .controls .btn:hover {
+.conversation-toolbar .btn:hover {
   background-color: rgba(255, 255, 255, .35);
 }
 
 /* Hangup button */
-.conversation .controls .btn-hangup {
+.conversation-toolbar .btn-hangup {
   background-color: #D74345;
   background-image: url(../img/hangup-inverse-14x14.png);
 }
-.conversation .controls .btn-hangup:hover {
+.conversation-toolbar .btn-hangup:hover {
   background-color: #C53436;
 }
 @media (min-resolution: 2dppx) {
-  .conversation .controls .btn-hangup {
+  .conversation-toolbar .btn-hangup {
     background-image: url(../img/hangup-inverse-14x14@2x.png);
   }
 }
 
 /* Common media control buttons behavior */
-.conversation .controls .media-control {
+.conversation-toolbar .media-control {
   background-color: transparent;
   opacity: 1;
 }
-.conversation .controls .media-control:hover {
+.conversation-toolbar .media-control:hover {
   background-color: rgba(255, 255, 255, .35);
   opacity: 1;
 }
-.conversation .controls .media-control.muted {
+.conversation-toolbar .media-control.muted {
   background-color: #0096DD;
   opacity: 1;
 }
 
 /* Audio mute button */
-.conversation .controls .local-media.btn-mute-audio {
+.conversation-toolbar .local-media.btn-mute-audio {
   background-image: url(../img/audio-inverse-14x14.png);
 }
-.conversation .controls .local-media.btn-mute-audio.muted {
+.conversation-toolbar .local-media.btn-mute-audio.muted {
   background-image: url(../img/mute-inverse-14x14.png);
 }
 @media (min-resolution: 2dppx) {
-  .conversation .controls .local-media.btn-mute-audio {
+  .conversation-toolbar .local-media.btn-mute-audio {
     background-image: url(../img/audio-inverse-14x14@2x.png);
   }
-  .conversation .controls .local-media.btn-mute-audio.muted {
+  .conversation-toolbar .local-media.btn-mute-audio.muted {
     background-image: url(../img/mute-inverse-14x14@2x.png);
   }
 }
 
 /* Video mute button */
-.conversation .controls .local-media.btn-mute-video {
+.conversation-toolbar .local-media.btn-mute-video {
   background-image: url(../img/video-inverse-14x14.png);
 }
-.conversation .controls .local-media.btn-mute-video.muted {
+.conversation-toolbar .local-media.btn-mute-video.muted {
   background-image: url(../img/facemute-14x14.png);
 }
 @media (min-resolution: 2dppx) {
-  .conversation .controls .local-media.btn-mute-video {
+  .conversation-toolbar .local-media.btn-mute-video {
     background-image: url(../img/video-inverse-14x14@2x.png);
   }
-  .conversation .controls .local-media.btn-mute-video.muted {
+  .conversation-toolbar .local-media.btn-mute-video.muted {
     background-image: url(../img/facemute-14x14@2x.png);
   }
 }
 
 /* Video elements */
 
 .conversation .media video {
   background: #eee;
deleted file mode 100644
--- a/browser/components/loop/content/shared/css/readme.html
+++ /dev/null
@@ -1,274 +0,0 @@
-  <!DOCTYPE html>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/.  -->
-
-<!-- This file is intended to help frontend developers to easily identify what
-     are the available styles for the Loop UI components. -->
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Loop UI shared CSS information/demo</title>
-  <link type="text/css" rel="stylesheet" href="common.css">
-  <link type="text/css" rel="stylesheet" href="panel.css">
-  <link type="text/css" rel="stylesheet" href="conversation.css">
-  <style>
-    body {
-      width: 600px;
-      margin: 1em auto;
-      background: #fff;
-      font-size: 12px;
-    }
-    h2 {
-      margin-top: 3em;
-    }
-  </style>
-</head>
-<body>
-  <h1>Loop UI toolkit</h1>
-
-  <h2>Logo icons</h2>
-
-  <p>
-    <img src="../img/icon_32.png"> 32x32 transparent PNG
-    <img src="../img/icon_64.png"> 64x64 transparent PNG
-  </p>
-
-  <p><em><strong>Note:</strong> these are temporary.</em></p>
-
-  <h2>Share panel</h2>
-
-  <h3>Simple</h3>
-
-  <div class="share">
-    <form class="description">
-      <p>This is a simple message.</p>
-    </form>
-    <div class="action">
-      <p><input type="url" value="http://loop.im/plop75"></p>
-      <p>Your name will appear as <a href="">Unnamed</a>.</p>
-    </div>
-  </div>
-
-  <h3>Featuring options</h3>
-
-  <div class="share">
-    <form class="description">
-      <p class="field">
-        <label>Share this link with a friend to
-          <select>
-            <option>browse together</option>
-            <option selected="selected">video chat</option>
-            <option>audio chat</option>
-            <option>text chat</option>
-          </select>
-        </label>
-      </p>
-      <p class="field">
-        <label>
-          Use webcam <select><option>Foo</option></select>
-        </label>
-      </p>
-      <p class="field">
-        <label>Use whatever
-          <select><option>Long foo is long indeed</option></select>
-        </label>
-      </p>
-      <p class="preview cf">
-        Preview <video></video>
-      </p>
-    </form>
-    <div class="action">
-      <p><input type="url" value="http://loop.im/plop75"></p>
-      <p>Your name will appear as <a href="">Unnamed</a>.</p>
-    </div>
-  </div>
-
-  <h2>Conversation window</h2>
-
-  <p><em>The conversation component adapts automatically to its container to
-    occupy all the available space.</em></p>
-
-  <h3>Large</h3>
-
-  <div class="conversation">
-    <div class="media nested">
-      <div class="remote_wrapper">
-        <video class="remote"></video>
-      </div>
-      <video class="local"></video>
-    </div>
-  </div>
-
-  <h3>Large with controls</h3>
-
-  <div class="conversation">
-    <ul class="controls">
-      <li><button class="btn btn-hangup" title="Hangup"></button></li>
-      <li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
-      <li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
-    </ul>
-    <div class="media nested">
-      <div class="remote_wrapper">
-        <video class="remote"></video>
-      </div>
-      <video class="local"></video>
-    </div>
-  </div>
-
-  <h3>Small (think chat window)</h3>
-
-  <div style="width: 204px">
-    <div class="conversation">
-      <ul class="controls">
-        <li><button class="btn btn-hangup" title="Hangup"></button></li>
-        <li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
-        <li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
-      </ul>
-      <div class="media nested">
-        <div class="remote_wrapper">
-          <video class="remote"></video>
-        </div>
-        <video class="local"></video>
-      </div>
-    </div>
-  </div>
-
-  <h3>Side by side</h3>
-
-  <div class="conversation">
-    <div class="media side-by-side">
-      <div class="remote_wrapper">
-        <video class="remote"></video>
-      </div>
-      <video class="local"></video>
-    </div>
-  </div>
-
-  <h2>Controls button variants</h2>
-
-  <h3>Nothing muted</h3>
-
-  <div style="width: 204px; min-height: 26px">
-    <div class="conversation">
-      <ul class="controls">
-        <li><button class="btn btn-hangup" title="Hangup"></button></li>
-        <li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
-        <li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
-      </ul>
-    </div>
-  </div>
-
-  <h3>Local audio muted</h3>
-
-  <div style="width: 204px; min-height: 26px">
-    <div class="conversation">
-      <ul class="controls">
-        <li><button class="btn btn-hangup" title="Hangup"></button></li>
-        <li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
-        <li><button class="btn media-control local-media btn-mute-audio muted" title="Mute audio"></button></li>
-      </ul>
-    </div>
-  </div>
-
-  <h3>Local video muted</h3>
-
-  <div style="width: 204px; min-height: 26px">
-    <div class="conversation">
-      <ul class="controls">
-        <li><button class="btn btn-hangup" title="Hangup"></button></li>
-        <li><button class="btn media-control local-media btn-mute-video muted" title="Mute video"></button></li>
-        <li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
-      </ul>
-    </div>
-  </div>
-
-  <h2>Expired call url view</h2>
-
-  <div class="expired-url-info">
-    <div class="info-panel">
-      <div class="firefox-logo"></div>
-      <h1 >Oops!</h1>
-      <h4 >This URL is unavailable.</h4>
-    </div>
-    <div class="promote-firefox">
-      <h3>Download Firefox to make free audio and video calls!</h3>
-      <p>
-        <a class="btn btn-large btn-success" href="https://www.mozilla.org/firefox/" data-reactid=".0.1.1.0">Get Firefox</a>
-      </p>
-    </div>
-  </div>
-
-  <h2>Buttons</h2>
-
-  <h3>Using <code>&lt;a&gt;</code></h3>
-
-  <p>
-    <a href="" class="btn">default</a>
-    <a href="" class="btn btn-info">info</a>
-    <a href="" class="btn btn-success">success</a>
-    <a href="" class="btn btn-warning">warning</a>
-    <a href="" class="btn btn-error">error</a>
-  </p>
-
-  <h3>Inline</h3>
-
-  <p>Click <a href="" class="btn btn-info">here</a>.</p>
-
-  <h3>Using <code>&lt;button&gt;</code></h3>
-
-  <p>
-    <button class="btn">default</button>
-    <button class="btn btn-info">info</button>
-    <button class="btn btn-success">success</button>
-    <button class="btn btn-warning">warning</button>
-    <button class="btn btn-error">error</button>
-  </p>
-
-  <h3>Large buttons</h3>
-
-  <p>
-    <a class="btn btn-large">default</a>
-    <a class="btn btn-large btn-info">info</a>
-    <a class="btn btn-large btn-success">success</a>
-    <a class="btn btn-large btn-warning">warning</a>
-    <a class="btn btn-large btn-error">error</a>
-  </p>
-
-  <h2>Alerts</h2>
-
-  <div class="alert alert-error">
-    <button class="close"></button>
-    <p class="message">Oops! Something went really wrong.</p>
-  </div>
-
-  <div class="alert alert-warning">
-    <button class="close"></button>
-    <p class="message">Oops! This is a warning.</p>
-  </div>
-
-  <h2>Logos</h2>
-
-  <h3>Centered Firefox logo</h3>
-
-  <div class="firefox-logo"></div>
-
-  <h2>Incoming call</h2>
-
-  <div class="incoming-call">
-    <h2>Incoming call</h2>
-    <p>
-      <button class="btn btn-success btn-accept">Accept</button>
-      <button class="btn btn-error btn-decline">Decline</button>
-    </p>
-  </div>
-
-  <script>
-  window.onload = function() {
-    [].forEach.call(document.querySelectorAll("video"), function(video) {
-      video.setAttribute("src", "http://v2v.cc/~j/theora_testsuite/320x240.ogg");
-    });
-  };
-  </script>
-</body>
-</html>
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -175,17 +175,17 @@ loop.shared.views = (function(_, OT, l10
 
     handleToggleAudio: function() {
       this.props.publishStream("audio", !this.props.audio.enabled);
     },
 
     render: function() {
       /* jshint ignore:start */
       return (
-        React.DOM.ul({className: "controls"}, 
+        React.DOM.ul({className: "conversation-toolbar"}, 
           React.DOM.li(null, React.DOM.button({className: "btn btn-hangup", 
                       onClick: this.handleClickHangup, 
                       title: __("hangup_button_title")})), 
           React.DOM.li(null, MediaControlButton({action: this.handleToggleVideo, 
                                   enabled: this.props.video.enabled, 
                                   scope: "local", type: "video"})), 
           React.DOM.li(null, MediaControlButton({action: this.handleToggleAudio, 
                                   enabled: this.props.audio.enabled, 
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -175,17 +175,17 @@ loop.shared.views = (function(_, OT, l10
 
     handleToggleAudio: function() {
       this.props.publishStream("audio", !this.props.audio.enabled);
     },
 
     render: function() {
       /* jshint ignore:start */
       return (
-        <ul className="controls">
+        <ul className="conversation-toolbar">
           <li><button className="btn btn-hangup"
                       onClick={this.handleClickHangup}
                       title={__("hangup_button_title")}></button></li>
           <li><MediaControlButton action={this.handleToggleVideo}
                                   enabled={this.props.video.enabled}
                                   scope="local" type="video" /></li>
           <li><MediaControlButton action={this.handleToggleAudio}
                                   enabled={this.props.audio.enabled}
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -37,24 +37,24 @@ loop.webapp = (function($, _, OT, webL10
    */
   var PromoteFirefoxView = React.createClass({displayName: 'PromoteFirefoxView',
     propTypes: {
       helper: React.PropTypes.object.isRequired
     },
 
     render: function() {
       if (this.props.helper.isFirefox(navigator.userAgent)) {
-        return React.DOM.div(null );
+        return React.DOM.div(null);
       }
       return (
-        React.DOM.div( {className:"promote-firefox"}, 
-          React.DOM.h3(null, __("promote_firefox_hello_heading")),
+        React.DOM.div({className: "promote-firefox"}, 
+          React.DOM.h3(null, __("promote_firefox_hello_heading")), 
           React.DOM.p(null, 
-            React.DOM.a( {className:"btn btn-large btn-success",
-               href:"https://www.mozilla.org/firefox/"}, 
+            React.DOM.a({className: "btn btn-large btn-success", 
+               href: "https://www.mozilla.org/firefox/"}, 
               __("get_firefox_button")
             )
           )
         )
       );
     }
   });
 
@@ -64,23 +64,23 @@ loop.webapp = (function($, _, OT, webL10
   var CallUrlExpiredView = React.createClass({displayName: 'CallUrlExpiredView',
     propTypes: {
       helper: React.PropTypes.object.isRequired
     },
 
     render: function() {
       /* jshint ignore:start */
       return (
-        React.DOM.div( {className:"expired-url-info"}, 
-          React.DOM.div( {className:"info-panel"}, 
-            React.DOM.div( {className:"firefox-logo"} ),
-            React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
+        React.DOM.div({className: "expired-url-info"}, 
+          React.DOM.div({className: "info-panel"}, 
+            React.DOM.div({className: "firefox-logo"}), 
+            React.DOM.h1(null, __("call_url_unavailable_notification_heading")), 
             React.DOM.h4(null, __("call_url_unavailable_notification_message"))
-          ),
-          PromoteFirefoxView( {helper:this.props.helper} )
+          ), 
+          PromoteFirefoxView({helper: this.props.helper})
         )
       );
       /* jshint ignore:end */
     }
   });
 
   /**
    * Conversation launcher view. A ConversationModel is associated and attached
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -195,17 +195,17 @@ describe("loop.panel", function() {
     });
   });
 
   describe("loop.panel.PanelView", function() {
     var fakeClient, callUrlData, view;
 
     beforeEach(function() {
       callUrlData = {
-        call_url: "http://call.invalid/",
+        callUrl: "http://call.invalid/",
         expiresAt: 1000
       };
 
       fakeClient = {
         requestCallUrl: function(_, cb) {
           cb(null, callUrlData);
         }
       };
@@ -224,17 +224,17 @@ describe("loop.panel", function() {
 
   });
 
   describe("loop.panel.CallUrlResult", function() {
     var fakeClient, callUrlData, view;
 
     beforeEach(function() {
       callUrlData = {
-        call_url: "http://call.invalid/fakeToken",
+        callUrl: "http://call.invalid/fakeToken",
         expiresAt: 1000
       };
 
       fakeClient = {
         requestCallUrl: function(_, cb) {
           cb(null, callUrlData);
         }
       };
@@ -267,28 +267,28 @@ describe("loop.panel", function() {
           client: fakeClient
         }));
 
         expect(view.state.pending).eql(true);
       });
 
       it("should update state with the call url received", function() {
         expect(view.state.pending).eql(false);
-        expect(view.state.callUrl).eql(callUrlData.call_url);
+        expect(view.state.callUrl).eql(callUrlData.callUrl);
       });
 
       it("should clear the pending state when a response is received",
         function() {
           expect(view.state.pending).eql(false);
         });
 
       it("should update CallUrlResult with the call url", function() {
         var urlField = view.getDOMNode().querySelector("input[type='url']");
 
-        expect(urlField.value).eql(callUrlData.call_url);
+        expect(urlField.value).eql(callUrlData.callUrl);
       });
 
       it("should reset all pending notifications", function() {
         sinon.assert.calledOnce(view.props.notifier.clear);
       });
 
       it("should notify the user when the operation failed", function() {
         fakeClient.requestCallUrl = function(_, cb) {
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -42,22 +42,23 @@ function setupFakeLoopServer() {
  * This is used to fake push registration and notifications for
  * MozLoopService tests. There is only one object created per test instance, as
  * once registration has taken place, the object cannot currently be changed.
  */
 let mockPushHandler = {
   // This sets the registration result to be returned when initialize
   // is called. By default, it is equivalent to success.
   registrationResult: null,
+  registrationPushURL: undefined,
 
   /**
    * MozLoopPushHandler API
    */
   initialize: function(registerCallback, notificationCallback) {
-    registerCallback(this.registrationResult);
+    registerCallback(this.registrationResult, this.registrationPushURL);
     this._notificationCallback = notificationCallback;
   },
 
   /**
    * Test-only API to simplify notifying a push notification result.
    */
   notify: function(version) {
     this._notificationCallback(version);
--- a/browser/components/loop/test/xpcshell/test_loopservice_registration.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_registration.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+Cu.import("resource://services-common/utils.js");
+
 /**
  * This file is to test general registration. Note that once successful
  * registration has taken place, we can no longer test the server side
  * parts as the service protects against this, hence, they need testing in
  * other test files.
  */
 
 /**
@@ -44,17 +46,24 @@ add_test(function test_register_websocke
   });
 });
 
 /**
  * Tests that we get a success response when both websocket and Loop server
  * registration are complete.
  */
 add_test(function test_register_success() {
+  mockPushHandler.registrationPushURL = kEndPointUrl;
+
   loopServer.registerPathHandler("/registration", (request, response) => {
+    let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
+    let data = JSON.parse(body);
+    Assert.equal(data.simplePushURL, kEndPointUrl,
+                 "Should send correct push url");
+
     response.setStatusLine(null, 200, "OK");
     response.processAsync();
     response.finish();
   });
   MozLoopService.register(mockPushHandler).then(() => {
     run_next_test();
   }, err => {
     do_throw("shouldn't error on a successful request");
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/README.md
@@ -0,0 +1,10 @@
+Loop UI Components Showcase
+===========================
+
+This app file showcases all Loop's view components.
+
+If you want to modify the app, launch the following command:
+
+    browser/components/loop/build-jsx --watch
+
+And start editing the `ui-showcase.jsx` file.
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/fake-l10n.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
+ * objects and makes them returning "fake string" for any requested string id.
+ * @type {Object}
+ */
+document.webL10n = document.mozL10n = {
+  get: function() {
+    return "fake text";
+  }
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/fake-mozLoop.js
@@ -0,0 +1,11 @@
+/* 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/. */
+
+/**
+ * Faking the mozLoop object which doesn't exist in regular web pages.
+ * @type {Object}
+ */
+navigator.mozLoop = {
+  getLoopCharPref: function() {}
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<!-- 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/.  -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Loop UI Components Showcase</title>
+    <link rel="stylesheet" type="text/css" href="../content/shared/css/common.css">
+    <link rel="stylesheet" type="text/css" href="../content/shared/css/conversation.css">
+    <link rel="stylesheet" type="text/css" href="../content/shared/css/panel.css">
+    <link rel="stylesheet" type="text/css" href="ui-showcase.css">
+ </head>
+  <body>
+    <div id="main"></div>
+    <script src="fake-mozLoop.js"></script>
+    <script src="fake-l10n.js"></script>
+    <script src="../content/libs/sdk.js"></script>
+    <script src="../content/shared/libs/react-0.10.0.js"></script>
+    <script src="../content/shared/libs/jquery-2.1.0.js"></script>
+    <script src="../content/shared/libs/lodash-2.4.1.js"></script>
+    <script src="../content/shared/libs/backbone-1.1.2.js"></script>
+    <script src="../content/shared/js/models.js"></script>
+    <script src="../content/shared/js/router.js"></script>
+    <script src="../content/shared/js/views.js"></script>
+    <script src="../content/js/client.js"></script>
+    <script src="../content/js/desktopRouter.js"></script>
+    <script src="../standalone/content/js/webapp.js"></script>
+    <script src="../content/js/panel.js"></script>
+    <script src="../content/js/conversation.js"></script>
+    <script src="ui-showcase.js"></script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/ui-showcase.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+.showcase {
+  width: 730px;
+  margin: 0 auto;
+}
+
+.showcase > header {
+  position: fixed;
+  top: 0;
+  background-color: #fbfbfb;
+  z-index: 1000;
+  width: 100%;
+  padding-bottom: 1em;
+}
+
+.showcase .menu > a {
+  margin-right: .5em;
+}
+
+.showcase > section {
+  position: relative;
+  padding-top: 10em;
+  clear: both;
+}
+
+.showcase > section > h1 {
+  border-bottom: 1px solid #aaa;
+}
+
+.showcase > section .comp {
+  margin: 0 auto; /* width is usually set programmatically */
+}
+
+.showcase > section .comp.dashed {
+  border: 1px dashed #ccc;
+}
+
+.showcase > section > .example {
+  margin-bottom: 6em;
+}
+
+.showcase > section .example > h3 {
+  border-bottom: 1px dashed #aaa;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -0,0 +1,133 @@
+/** @jsx React.DOM */
+
+/* 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/. */
+
+/* jshint newcap:false */
+/* global loop:true, React */
+
+(function() {
+  "use strict";
+
+  // 1. Desktop components
+  // 1.1 Panel
+  var PanelView = loop.panel.PanelView;
+  // 1.2. Conversation Window
+  var IncomingCallView = loop.conversation.IncomingCallView;
+
+  // 2. Standalone webapp
+  var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
+
+  // 3. Shared components
+  var ConversationToolbar = loop.shared.views.ConversationToolbar;
+  var ConversationView = loop.shared.views.ConversationView;
+
+  // Local helpers
+  function returnTrue() {
+    return true;
+  }
+
+  function returnFalse() {
+    return false;
+  }
+
+  var Example = React.createClass({displayName: 'Example',
+    render: function() {
+      var cx = React.addons.classSet;
+      return (
+        React.DOM.div({className: "example"}, 
+          React.DOM.h3(null, this.props.summary), 
+          React.DOM.div({className: cx({comp: true, dashed: this.props.dashed}), 
+               style: this.props.style || {}}, 
+            this.props.children
+          )
+        )
+      );
+    }
+  });
+
+  var Section = React.createClass({displayName: 'Section',
+    render: function() {
+      return (
+        React.DOM.section({id: this.props.name}, 
+          React.DOM.h1(null, this.props.name), 
+          this.props.children
+        )
+      );
+    }
+  });
+
+  var ShowCase = React.createClass({displayName: 'ShowCase',
+    render: function() {
+      return (
+        React.DOM.div({className: "showcase"}, 
+          React.DOM.header(null, 
+            React.DOM.h1(null, "Loop UI Components Showcase"), 
+            React.DOM.nav({className: "menu"}, 
+              React.Children.map(this.props.children, function(section) {
+                return (
+                  React.DOM.a({className: "btn btn-info", href: "#" + section.props.name}, 
+                    section.props.name
+                  )
+                );
+              })
+            )
+          ), 
+          this.props.children
+        )
+      );
+    }
+  });
+
+  var App = React.createClass({displayName: 'App',
+    render: function() {
+      return (
+        ShowCase(null, 
+          Section({name: "PanelView"}, 
+            Example({summary: "332px wide", dashed: "true", style: {width: "332px"}}, 
+              PanelView(null)
+            )
+          ), 
+
+          Section({name: "IncomingCallView"}, 
+            Example({summary: "Default", dashed: "true", style: {width: "280px"}}, 
+              IncomingCallView(null)
+            )
+          ), 
+
+          Section({name: "ConversationToolbar"}, 
+            Example({summary: "Default"}, 
+              ConversationToolbar({video: {enabled: true}, audio: {enabled: true}})
+            ), 
+            Example({summary: "Video muted"}, 
+              ConversationToolbar({video: {enabled: false}, audio: {enabled: true}})
+            ), 
+            Example({summary: "Audio muted"}, 
+              ConversationToolbar({video: {enabled: true}, audio: {enabled: false}})
+            )
+          ), 
+
+          Section({name: "ConversationView"}, 
+            Example({summary: "Default"}, 
+              ConversationView({video: {enabled: true}, audio: {enabled: true}})
+            )
+          ), 
+
+          Section({name: "CallUrlExpiredView"}, 
+            Example({summary: "Firefox User"}, 
+              CallUrlExpiredView({helper: {isFirefox: returnTrue}})
+            ), 
+            Example({summary: "Non-Firefox User"}, 
+              CallUrlExpiredView({helper: {isFirefox: returnFalse}})
+            )
+          )
+        )
+      );
+    }
+  });
+
+  window.addEventListener("DOMContentLoaded", function() {
+    React.renderComponent(App(null), document.body);
+  });
+})();
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -0,0 +1,133 @@
+/** @jsx React.DOM */
+
+/* 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/. */
+
+/* jshint newcap:false */
+/* global loop:true, React */
+
+(function() {
+  "use strict";
+
+  // 1. Desktop components
+  // 1.1 Panel
+  var PanelView = loop.panel.PanelView;
+  // 1.2. Conversation Window
+  var IncomingCallView = loop.conversation.IncomingCallView;
+
+  // 2. Standalone webapp
+  var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
+
+  // 3. Shared components
+  var ConversationToolbar = loop.shared.views.ConversationToolbar;
+  var ConversationView = loop.shared.views.ConversationView;
+
+  // Local helpers
+  function returnTrue() {
+    return true;
+  }
+
+  function returnFalse() {
+    return false;
+  }
+
+  var Example = React.createClass({
+    render: function() {
+      var cx = React.addons.classSet;
+      return (
+        <div className="example">
+          <h3>{this.props.summary}</h3>
+          <div className={cx({comp: true, dashed: this.props.dashed})}
+               style={this.props.style || {}}>
+            {this.props.children}
+          </div>
+        </div>
+      );
+    }
+  });
+
+  var Section = React.createClass({
+    render: function() {
+      return (
+        <section id={this.props.name}>
+          <h1>{this.props.name}</h1>
+          {this.props.children}
+        </section>
+      );
+    }
+  });
+
+  var ShowCase = React.createClass({
+    render: function() {
+      return (
+        <div className="showcase">
+          <header>
+            <h1>Loop UI Components Showcase</h1>
+            <nav className="menu">{
+              React.Children.map(this.props.children, function(section) {
+                return (
+                  <a className="btn btn-info" href={"#" + section.props.name}>
+                    {section.props.name}
+                  </a>
+                );
+              })
+            }</nav>
+          </header>
+          {this.props.children}
+        </div>
+      );
+    }
+  });
+
+  var App = React.createClass({
+    render: function() {
+      return (
+        <ShowCase>
+          <Section name="PanelView">
+            <Example summary="332px wide" dashed="true" style={{width: "332px"}}>
+              <PanelView />
+            </Example>
+          </Section>
+
+          <Section name="IncomingCallView">
+            <Example summary="Default" dashed="true" style={{width: "280px"}}>
+              <IncomingCallView />
+            </Example>
+          </Section>
+
+          <Section name="ConversationToolbar">
+            <Example summary="Default">
+              <ConversationToolbar video={{enabled: true}} audio={{enabled: true}} />
+            </Example>
+            <Example summary="Video muted">
+              <ConversationToolbar video={{enabled: false}} audio={{enabled: true}} />
+            </Example>
+            <Example summary="Audio muted">
+              <ConversationToolbar video={{enabled: true}} audio={{enabled: false}} />
+            </Example>
+          </Section>
+
+          <Section name="ConversationView">
+            <Example summary="Default">
+              <ConversationView video={{enabled: true}} audio={{enabled: true}} />
+            </Example>
+          </Section>
+
+          <Section name="CallUrlExpiredView">
+            <Example summary="Firefox User">
+              <CallUrlExpiredView helper={{isFirefox: returnTrue}} />
+            </Example>
+            <Example summary="Non-Firefox User">
+              <CallUrlExpiredView helper={{isFirefox: returnFalse}} />
+            </Example>
+          </Section>
+        </ShowCase>
+      );
+    }
+  });
+
+  window.addEventListener("DOMContentLoaded", function() {
+    React.renderComponent(<App />, document.body);
+  });
+})();
--- a/browser/devtools/projecteditor/moz.build
+++ b/browser/devtools/projecteditor/moz.build
@@ -1,6 +1,6 @@
 # vim: set filetype=python:
 # 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/.
 
-TEST_DIRS += ['test']
\ No newline at end of file
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
deleted file mode 100644
--- a/browser/devtools/projecteditor/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,8 +1,7 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-TEST_DIRS += ['pdfjs/test']
-
+BROWSER_CHROME_MANIFESTS += ['pdfjs/test/browser.ini']
deleted file mode 100644
--- a/browser/extensions/pdfjs/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/fuel/moz.build
+++ b/browser/fuel/moz.build
@@ -1,8 +1,8 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['public', 'src']
-TEST_DIRS += ['test']
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
deleted file mode 100644
--- a/browser/fuel/test/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -1,15 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-TEST_DIRS += ['test']
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
+XPCSHELL_TESTS_MANIFESTS += ['test/unit/social/xpcshell.ini']
 
 EXTRA_JS_MODULES += [
     'BrowserNewTabPreloader.jsm',
     'BrowserUITelemetry.jsm',
     'Chat.jsm',
     'ContentClick.jsm',
     'ContentLinkHandler.jsm',
     'ContentSearch.jsm',
deleted file mode 100644
--- a/browser/modules/test/chrome/moz.build
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
-
deleted file mode 100644
--- a/browser/modules/test/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-DIRS += ['chrome', 'unit']
-
-BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
deleted file mode 100644
--- a/browser/modules/test/unit/moz.build
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-XPCSHELL_TESTS_MANIFESTS += ['social/xpcshell.ini']
--- a/caps/nsNullPrincipal.cpp
+++ b/caps/nsNullPrincipal.cpp
@@ -13,16 +13,18 @@
 
 #include "nsNullPrincipal.h"
 #include "nsNullPrincipalURI.h"
 #include "nsMemory.h"
 #include "nsIUUIDGenerator.h"
 #include "nsID.h"
 #include "nsNetUtil.h"
 #include "nsIClassInfoImpl.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
 #include "nsNetCID.h"
 #include "nsError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPrincipal.h"
 #include "nsScriptSecurityManager.h"
 #include "pratom.h"
 
 using namespace mozilla;
@@ -61,21 +63,34 @@ nsNullPrincipal::Release()
 nsNullPrincipal::nsNullPrincipal()
 {
 }
 
 nsNullPrincipal::~nsNullPrincipal()
 {
 }
 
+/* static */ already_AddRefed<nsNullPrincipal>
+nsNullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
+{
+  nsRefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
+  nsresult rv = nullPrin->Init(aInheritFrom->GetAppId(),
+                               aInheritFrom->GetIsInBrowserElement());
+  return NS_SUCCEEDED(rv) ? nullPrin.forget() : nullptr;
+}
+
 #define NS_NULLPRINCIPAL_PREFIX NS_NULLPRINCIPAL_SCHEME ":"
 
 nsresult
-nsNullPrincipal::Init()
+nsNullPrincipal::Init(uint32_t aAppId, bool aInMozBrowser)
 {
+  MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
+  mAppId = aAppId;
+  mInMozBrowser = aInMozBrowser;
+
   // FIXME: bug 327161 -- make sure the uuid generator is reseeding-resistant.
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsID id;
   rv = uuidgen->GenerateUUIDInPlace(&id);
@@ -251,31 +266,31 @@ nsNullPrincipal::GetJarPrefix(nsACString
 {
   aJarPrefix.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
-  *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+  *aAppStatus = nsScriptSecurityManager::AppStatusForPrincipal(this);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetAppId(uint32_t* aAppId)
 {
-  *aAppId = nsIScriptSecurityManager::NO_APP_ID;
+  *aAppId = mAppId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
-  *aIsInBrowserElement = false;
+  *aIsInBrowserElement = mInMozBrowser;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetUnknownAppId(bool* aUnknownAppId)
 {
   *aUnknownAppId = false;
   return NS_OK;
@@ -296,21 +311,29 @@ nsNullPrincipal::GetBaseDomain(nsACStrin
 }
 
 /**
  * nsISerializable implementation
  */
 NS_IMETHODIMP
 nsNullPrincipal::Read(nsIObjectInputStream* aStream)
 {
-  // no-op: CID is sufficient to create a useful nsNullPrincipal, since the URI
-  // is not really relevant.
+  // Note - nsNullPrincipal use NS_GENERIC_FACTORY_CONSTRUCTOR_INIT, which means
+  // that the Init() method has already been invoked by the time we deserialize.
+  // This is in contrast to nsPrincipal, which uses NS_GENERIC_FACTORY_CONSTRUCTOR,
+  // in which case ::Read needs to invoke Init().
+  nsresult rv = aStream->Read32(&mAppId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = aStream->ReadBoolean(&mInMozBrowser);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::Write(nsIObjectOutputStream* aStream)
 {
-  // no-op: CID is sufficient to create a useful nsNullPrincipal, since the URI
-  // is not really relevant.
+  aStream->Write32(mAppId);
+  aStream->WriteBoolean(mInMozBrowser);
   return NS_OK;
 }
 
--- a/caps/nsNullPrincipal.h
+++ b/caps/nsNullPrincipal.h
@@ -9,24 +9,25 @@
  * same-origin with anything but themselves.
  */
 
 #ifndef nsNullPrincipal_h__
 #define nsNullPrincipal_h__
 
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsCOMPtr.h"
 #include "nsIContentSecurityPolicy.h"
 
 class nsIURI;
 
 #define NS_NULLPRINCIPAL_CID \
-{ 0xdd156d62, 0xd26f, 0x4441, \
- { 0x9c, 0xdb, 0xe8, 0xf0, 0x91, 0x07, 0xc2, 0x73 } }
+{ 0xa0bd8b42, 0xf6bf, 0x4fb9, \
+  { 0x93, 0x42, 0x90, 0xbf, 0xc9, 0xb7, 0xa1, 0xab } }
 #define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
 
 #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
 
 class nsNullPrincipal : public nsJSPrincipals
 {
 public:
   nsNullPrincipal();
@@ -36,24 +37,29 @@ public:
 
   // FIXME: bug 327245 -- I sorta wish there were a clean way to share the
   // nsJSPrincipals munging code between the various principal classes without
   // giving up the NS_DECL_NSIPRINCIPAL goodness.
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRINCIPAL
   NS_DECL_NSISERIALIZABLE
 
-  nsresult Init();
+  static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIPrincipal *aInheritFrom);
+
+  nsresult Init(uint32_t aAppId = nsIScriptSecurityManager::NO_APP_ID,
+                bool aInMozBrowser = false);
 
   virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
 
 #ifdef DEBUG
   virtual void dumpImpl() MOZ_OVERRIDE;
 #endif 
 
  protected:
   virtual ~nsNullPrincipal();
 
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
+  uint32_t mAppId;
+  bool mInMozBrowser;
 };
 
 #endif // nsNullPrincipal_h__
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -572,58 +572,21 @@ nsPrincipal::Write(nsIObjectOutputStream
   // on the deserialized URIs in Read().
 
   return NS_OK;
 }
 
 uint16_t
 nsPrincipal::GetAppStatus()
 {
-  NS_WARN_IF_FALSE(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
-                   "Asking for app status on a principal with an unknown app id");
-  // Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
-  // and they are not inside a mozbrowser.
-  if (mAppId == nsIScriptSecurityManager::NO_APP_ID ||
-      mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID || mInMozBrowser) {
+  if (mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+    NS_WARNING("Asking for app status on a principal with an unknown app id");
     return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   }
-
-  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(appsService, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-
-  nsCOMPtr<mozIApplication> app;
-  appsService->GetAppByLocalId(mAppId, getter_AddRefs(app));
-  NS_ENSURE_TRUE(app, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-
-  uint16_t status = nsIPrincipal::APP_STATUS_INSTALLED;
-  NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
-                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-
-  nsAutoCString origin;
-  NS_ENSURE_SUCCESS(GetOrigin(getter_Copies(origin)),
-                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-  nsString appOrigin;
-  NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
-                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-
-  // We go from string -> nsIURI -> origin to be sure we
-  // compare two punny-encoded origins.
-  nsCOMPtr<nsIURI> appURI;
-  NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
-                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-
-  nsAutoCString appOriginPunned;
-  NS_ENSURE_SUCCESS(GetOriginForURI(appURI, getter_Copies(appOriginPunned)),
-                    nsIPrincipal::APP_STATUS_NOT_INSTALLED);
-
-  if (!appOriginPunned.Equals(origin)) {
-    return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
-  }
-
-  return status;
+  return nsScriptSecurityManager::AppStatusForPrincipal(this);
 }
 
 /************************************************************************************************************************/
 
 static const char EXPANDED_PRINCIPAL_SPEC[] = "[Expanded Principal]";
 
 NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_EXPANDEDPRINCIPAL_CID)
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -6,16 +6,17 @@
 
 #include "nsScriptSecurityManager.h"
 
 #include "mozilla/ArrayUtils.h"
 
 #include "js/OldDebugAPI.h"
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
+#include "nsIAppsService.h"
 #include "nsILoadContext.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIURL.h"
 #include "nsINestedURI.h"
 #include "nspr.h"
 #include "nsJSPrincipals.h"
@@ -51,16 +52,17 @@
 #include "nsDOMJSUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsIClassInfo.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIChromeRegistry.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
+#include "mozIApplication.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include <stdint.h>
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsJSUtils.h"
@@ -247,16 +249,67 @@ nsScriptSecurityManager::SecurityCompare
 // SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI
 // is consistent with NS_SecurityCompareURIs.  See nsNetUtil.h.
 uint32_t
 nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
 {
     return NS_SecurityHashURI(aURI);
 }
 
+uint16_t
+nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin)
+{
+    uint32_t appId = aPrin->GetAppId();
+    bool inMozBrowser = aPrin->GetIsInBrowserElement();
+    NS_WARN_IF_FALSE(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+                     "Asking for app status on a principal with an unknown app id");
+    // Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
+    // and they are not inside a mozbrowser.
+    if (appId == nsIScriptSecurityManager::NO_APP_ID ||
+        appId == nsIScriptSecurityManager::UNKNOWN_APP_ID || inMozBrowser)
+    {
+        return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+    }
+
+    nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+    NS_ENSURE_TRUE(appsService, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+    nsCOMPtr<mozIApplication> app;
+    appsService->GetAppByLocalId(appId, getter_AddRefs(app));
+    NS_ENSURE_TRUE(app, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+    uint16_t status = nsIPrincipal::APP_STATUS_INSTALLED;
+    NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
+                      nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+    nsAutoCString origin;
+    NS_ENSURE_SUCCESS(aPrin->GetOrigin(getter_Copies(origin)),
+                      nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+    nsString appOrigin;
+    NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
+                      nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+    // We go from string -> nsIURI -> origin to be sure we
+    // compare two punny-encoded origins.
+    nsCOMPtr<nsIURI> appURI;
+    NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
+                      nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+    nsAutoCString appOriginPunned;
+    NS_ENSURE_SUCCESS(nsPrincipal::GetOriginForURI(appURI, getter_Copies(appOriginPunned)),
+                      nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+
+    if (!appOriginPunned.Equals(origin)) {
+        return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
+    }
+
+    return status;
+
+}
+
 NS_IMETHODIMP
 nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
                                              nsIPrincipal** aPrincipal)
 {
     NS_PRECONDITION(aChannel, "Must have channel!");
     nsCOMPtr<nsISupports> owner;
     aChannel->GetOwner(getter_AddRefs(owner));
     if (owner) {
@@ -266,17 +319,21 @@ nsScriptSecurityManager::GetChannelPrinc
         }
     }
 
     // Check whether we have an nsILoadInfo that says what we should do.
     nsCOMPtr<nsILoadInfo> loadInfo;
     aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     if (loadInfo) {
         if (loadInfo->GetLoadingSandboxed()) {
-            return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, aPrincipal);
+            nsRefPtr<nsNullPrincipal> prin =
+              nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
+            NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
+            prin.forget(aPrincipal);
+            return NS_OK;
         }
 
         if (loadInfo->GetForceInheritPrincipal()) {
             NS_ADDREF(*aPrincipal = loadInfo->LoadingPrincipal());
             return NS_OK;
         }
     }
 
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -63,16 +63,18 @@ public:
      * Utility method for comparing two URIs.  For security purposes, two URIs
      * are equivalent if their schemes, hosts, and ports (if any) match.  This
      * method returns true if aSubjectURI and aObjectURI have the same origin,
      * false otherwise.
      */
     static bool SecurityCompareURIs(nsIURI* aSourceURI, nsIURI* aTargetURI);
     static uint32_t SecurityHashURI(nsIURI* aURI);
 
+    static uint16_t AppStatusForPrincipal(nsIPrincipal *aPrin);
+
     static nsresult 
     ReportError(JSContext* cx, const nsAString& messageTag,
                 nsIURI* aSource, nsIURI* aTarget);
 
     static uint32_t
     HashPrincipalByOrigin(nsIPrincipal* aPrincipal);
 
     static bool
--- a/configure.in
+++ b/configure.in
@@ -8005,20 +8005,23 @@ if test "$MOZ_TREE_CAIRO"; then
     fi
 
     # Define macros for cairo-features.h
     TEE_SURFACE_FEATURE="#define CAIRO_HAS_TEE_SURFACE 1"
     if test "$MOZ_X11"; then
         XLIB_SURFACE_FEATURE="#define CAIRO_HAS_XLIB_SURFACE 1"
         XLIB_XRENDER_SURFACE_FEATURE="#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1"
         PS_SURFACE_FEATURE="#define CAIRO_HAS_PS_SURFACE 1"
+    fi
+    if test "$_HAVE_FREETYPE2"; then
         FT_FONT_FEATURE="#define CAIRO_HAS_FT_FONT 1"
         MOZ_ENABLE_CAIRO_FT=1
         CAIRO_FT_CFLAGS="$FT2_CFLAGS"
     fi
+
     case "$MOZ_WIDGET_TOOLKIT" in
       qt)
         QT_SURFACE_FEATURE="#define CAIRO_HAS_QT_SURFACE 1"
         ;;
       cocoa | uikit)
         QUARTZ_SURFACE_FEATURE="#define CAIRO_HAS_QUARTZ_SURFACE 1"
         QUARTZ_IMAGE_SURFACE_FEATURE="#define CAIRO_HAS_QUARTZ_IMAGE_SURFACE 1"
         QUARTZ_FONT_FEATURE="#define CAIRO_HAS_QUARTZ_FONT 1"
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -710,13 +710,13 @@ nsCopySupport::FireClipboardEvent(int32_
         return false;
       }
     }
   }
 
   // Now that we have copied, update the clipboard commands. This should have
   // the effect of updating the enabled state of the paste menu item.
   if (doDefault || count) {
-    piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"));
+    piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"), nullptr, 0);
   }
 
   return doDefault;
 }
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -2744,36 +2744,41 @@ static void ExtractRectFromOffset(nsIFra
     aR->width = point.x - aR->x;
   } else {
     aR->width = aR->XMost() - point.x;
     aR->x = point.x;
   }
 }
 
 static nsTextFrame*
-GetTextFrameForContent(nsIContent* aContent)
+GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
 {
   nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
   if (presShell) {
     presShell->FrameConstructor()->EnsureFrameForTextNode(
         static_cast<nsGenericDOMDataNode*>(aContent));
-    aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
+
+    if (aFlushLayout) {
+      aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
+    }
+
     nsIFrame* frame = aContent->GetPrimaryFrame();
     if (frame && frame->GetType() == nsGkAtoms::textFrame) {
       return static_cast<nsTextFrame*>(frame);
     }
   }
   return nullptr;
 }
 
 static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
                                    nsIContent* aContent, int32_t aStartOffset,
-                                   int32_t aEndOffset, bool aClampToEdge)
+                                   int32_t aEndOffset, bool aClampToEdge,
+                                   bool aFlushLayout)
 {
-  nsTextFrame* textFrame = GetTextFrameForContent(aContent);
+  nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
   if (textFrame) {
     nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
     for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
       int32_t fstart = f->GetContentOffset(), fend = f->GetContentEnd();
       if (fend <= aStartOffset || fstart >= aEndOffset)
         continue;
 
       // overlapping with the offset we want
@@ -2796,44 +2801,45 @@ static nsresult GetPartialTextRect(nsLay
   return NS_OK;
 }
 
 /* static */ void
 nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
                             nsRange* aRange,
                             nsINode* aStartParent, int32_t aStartOffset,
                             nsINode* aEndParent, int32_t aEndOffset,
-                            bool aClampToEdge)
+                            bool aClampToEdge, bool aFlushLayout)
 {
   // Hold strong pointers across the flush
   nsCOMPtr<nsINode> startContainer = aStartParent;
   nsCOMPtr<nsINode> endContainer = aEndParent;
 
   // Flush out layout so our frames are up to date.
   if (!aStartParent->IsInDoc()) {
     return;
   }
 
-  aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
-
-  // Recheck whether we're still in the document
-  if (!aStartParent->IsInDoc()) {
-    return;
+  if (aFlushLayout) {
+    aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
+    // Recheck whether we're still in the document
+    if (!aStartParent->IsInDoc()) {
+      return;
+    }
   }
 
   RangeSubtreeIterator iter;
 
   nsresult rv = iter.Init(aRange);
   if (NS_FAILED(rv)) return;
 
   if (iter.IsDone()) {
     // the range is collapsed, only continue if the cursor is in a text node
     nsCOMPtr<nsIContent> content = do_QueryInterface(aStartParent);
     if (content && content->IsNodeOfType(nsINode::eTEXT)) {
-      nsTextFrame* textFrame = GetTextFrameForContent(content);
+      nsTextFrame* textFrame = GetTextFrameForContent(content, aFlushLayout);
       if (textFrame) {
         int32_t outOffset;
         nsIFrame* outFrame;
         textFrame->GetChildFrameContainingOffset(aStartOffset, false,
           &outOffset, &outFrame);
         if (outFrame) {
            nsIFrame* relativeTo =
              nsLayoutUtils::GetContainingBlockForClientRect(outFrame);
@@ -2853,20 +2859,22 @@ nsRange::CollectClientRects(nsLayoutUtil
     iter.Next();
     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
     if (!content)
       continue;
     if (content->IsNodeOfType(nsINode::eTEXT)) {
        if (node == startContainer) {
          int32_t offset = startContainer == endContainer ?
            aEndOffset : content->GetText()->GetLength();
-         GetPartialTextRect(aCollector, content, aStartOffset, offset, aClampToEdge);
+         GetPartialTextRect(aCollector, content, aStartOffset, offset,
+                            aClampToEdge, aFlushLayout);
          continue;
        } else if (node == endContainer) {
-         GetPartialTextRect(aCollector, content, 0, aEndOffset, aClampToEdge);
+         GetPartialTextRect(aCollector, content, 0, aEndOffset,
+                            aClampToEdge, aFlushLayout);
          continue;
        }
     }
 
     nsIFrame* frame = content->GetPrimaryFrame();
     if (frame) {
       nsLayoutUtils::GetAllInFlowRects(frame,
         nsLayoutUtils::GetContainingBlockForClientRect(frame), aCollector,
@@ -2878,54 +2886,54 @@ nsRange::CollectClientRects(nsLayoutUtil
 NS_IMETHODIMP
 nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult)
 {
   *aResult = GetBoundingClientRect(true).take();
   return NS_OK;
 }
 
 already_AddRefed<DOMRect>
-nsRange::GetBoundingClientRect(bool aClampToEdge)
+nsRange::GetBoundingClientRect(bool aClampToEdge, bool aFlushLayout)
 {
   nsRefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
   if (!mStartParent) {
     return rect.forget();
   }
 
   nsLayoutUtils::RectAccumulator accumulator;
   CollectClientRects(&accumulator, this, mStartParent, mStartOffset, 
-    mEndParent, mEndOffset, aClampToEdge);
+    mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
 
   nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect : 
     accumulator.mResultRect;
   rect->SetLayoutRect(r);
   return rect.forget();
 }
 
 NS_IMETHODIMP
 nsRange::GetClientRects(nsIDOMClientRectList** aResult)
 {
   *aResult = GetClientRects(true).take();
   return NS_OK;
 }
 
 already_AddRefed<DOMRectList>
-nsRange::GetClientRects(bool aClampToEdge)
+nsRange::GetClientRects(bool aClampToEdge, bool aFlushLayout)
 {
   if (!mStartParent) {
     return nullptr;
   }
 
   nsRefPtr<DOMRectList> rectList =
     new DOMRectList(static_cast<nsIDOMRange*>(this));
 
   nsLayoutUtils::RectListBuilder builder(rectList);
 
   CollectClientRects(&builder, this, mStartParent, mStartOffset, 
-    mEndParent, mEndOffset, aClampToEdge);
+    mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
   return rectList.forget();
 }
 
 NS_IMETHODIMP
 nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
 {
   *aResult = nullptr;
 
--- a/content/base/src/nsRange.h
+++ b/content/base/src/nsRange.h
@@ -212,18 +212,20 @@ public:
   void SelectNodeContents(nsINode& aNode, ErrorResult& aErr);
   void SetEnd(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
   void SetEndAfter(nsINode& aNode, ErrorResult& aErr);
   void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
   void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
   void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
   void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
   void SurroundContents(nsINode& aNode, ErrorResult& aErr);
-  already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true);
-  already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true);
+  already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
+                                                  bool aFlushLayout = true);
+  already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
+                                               bool aFlushLayout = true);
 
   nsINode* GetParentObject() const { return mOwner; }
   virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE MOZ_FINAL;
 
 private:
   // no copy's or assigns
   nsRange(const nsRange&);
   nsRange& operator=(const nsRange&);
@@ -255,17 +257,17 @@ public:
 
   static bool IsNodeSelected(nsINode* aNode, uint32_t aStartOffset,
                              uint32_t aEndOffset);
 
   static void CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
                                  nsRange* aRange,
                                  nsINode* aStartParent, int32_t aStartOffset,
                                  nsINode* aEndParent, int32_t aEndOffset,
-                                 bool aClampToEdge);
+                                 bool aClampToEdge, bool aFlushLayout);
 
   typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
 protected:
   void RegisterCommonAncestor(nsINode* aNode);
   void UnregisterCommonAncestor(nsINode* aNode);
   nsINode* IsValidBoundary(nsINode* aNode);
 
   // CharacterDataChanged set aNotInsertedYet to true to disable an assertion
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -671,17 +671,19 @@ public:
 
   NS_DECL_NSIEDITOROBSERVER
 
 protected:
   /** the default destructor. virtual due to the possibility of derivation.
    */
   virtual ~nsTextInputListener();
 
-  nsresult  UpdateTextInputCommands(const nsAString& commandsToUpdate);
+  nsresult  UpdateTextInputCommands(const nsAString& commandsToUpdate,
+                                    nsISelection* sel = nullptr,
+                                    int16_t reason = 0);
 
 protected:
 
   nsIFrame* mFrame;
 
   nsITextControlElement* const mTxtCtrlElement;
 
   bool            mSelectionWasCollapsed;
@@ -775,26 +777,28 @@ nsTextInputListener::NotifySelectionChan
           WidgetEvent event(true, NS_FORM_SELECTED);
 
           presShell->HandleEventWithTarget(&event, mFrame, content, &status);
         }
       }
     }
   }
 
+  UpdateTextInputCommands(NS_LITERAL_STRING("selectionchange"), aSel, aReason);
+
   // if the collapsed state did not change, don't fire notifications
   if (collapsed == mSelectionWasCollapsed)
     return NS_OK;
-  
+
   mSelectionWasCollapsed = collapsed;
 
   if (!weakFrame.IsAlive() || !nsContentUtils::IsFocusedContent(mFrame->GetContent()))
     return NS_OK;
 
-  return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
+  return UpdateTextInputCommands(NS_LITERAL_STRING("select"), aSel, aReason);
 }
 
 // END nsIDOMSelectionListener
 
 static void
 DoCommandCallback(Command aCommand, void* aData)
 {
   nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
@@ -925,28 +929,30 @@ nsTextInputListener::EditAction()
 
   return NS_OK;
 }
 
 // END nsIEditorObserver
 
 
 nsresult
-nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
+nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate,
+                                             nsISelection* sel,
+                                             int16_t reason)
 {
   nsIContent* content = mFrame->GetContent();
   NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
   
   nsCOMPtr<nsIDocument> doc = content->GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   nsPIDOMWindow *domWindow = doc->GetWindow();
   NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
 
-  return domWindow->UpdateCommands(commandsToUpdate);
+  return domWindow->UpdateCommands(commandsToUpdate, sel, reason);
 }
 
 // END nsTextInputListener
 
 // nsTextEditorState
 
 nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
   : mTextCtrlElement(aOwningElement),
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -1,26 +1,30 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 <stdio.h>
 #include <math.h>
+#include <string.h>
 #include "prlog.h"
 #include "prdtoa.h"
 #include "AudioStream.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include <algorithm>
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "soundtouch/SoundTouch.h"
 #include "Latency.h"
+#ifdef XP_MACOSX
+#include <sys/sysctl.h>
+#endif
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #ifdef PR_LOGGING
@@ -241,20 +245,20 @@ AudioStream::AudioStream()
   , mOutRate(0)
   , mChannels(0)
   , mOutChannels(0)
   , mWritten(0)
   , mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST())
   , mLatencyRequest(HighLatency)
   , mReadPoint(0)
   , mDumpFile(nullptr)
-  , mVolume(1.0)
   , mBytesPerFrame(0)
   , mState(INITIALIZED)
   , mNeedsStart(false)
+  , mShouldDropFrames(false)
 {
   // keep a ref in case we shut down later than nsLayoutStatics
   mLatencyLog = AsyncLatencyLogger::Get(true);
 }
 
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
@@ -493,18 +497,20 @@ AudioStream::Init(int32_t aNumChannels, 
 
   mDumpFile = OpenDumpFile(this);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
+  mAudioChannel = aAudioChannel;
   params.stream_type = ConvertChannelToCubebType(aAudioChannel);
 #else
+  mAudioChannel = dom::AudioChannel::Content;
   params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
 #endif
 
   if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 #endif
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
@@ -539,16 +545,72 @@ AudioStream::Init(int32_t aNumChannels, 
   // thread for now (cubeb API issue)
   {
     MonitorAutoLock mon(mMonitor);
     CheckForStart();
   }
   return rv;
 }
 
+// On certain MacBookPro, the microphone is located near the left speaker.
+// We need to pan the sound output to the right speaker if we are using the mic
+// and the built-in speaker, or we will have terrible echo.
+void AudioStream::PanOutputIfNeeded(bool aMicrophoneActive)
+{
+#ifdef XP_MACOSX
+  cubeb_device* device;
+  int rv;
+  char name[128];
+  size_t length = sizeof(name);
+  bool panCenter = false;
+
+  rv = sysctlbyname("hw.model", name, &length, NULL, 0);
+  if (rv) {
+    return;
+  }
+
+  if (!strncmp(name, "MacBookPro", 10)) {
+    if (cubeb_stream_get_current_device(mCubebStream, &device) == CUBEB_OK) {
+      // Check if we are currently outputing sound on external speakers.
+      if (!strcmp(device->output_name, "ispk")) {
+        // Pan everything to the right speaker.
+        if (aMicrophoneActive) {
+          LOG(("%p Panning audio output to the right.", this));
+          if (cubeb_stream_set_panning(mCubebStream, 1.0) != CUBEB_OK) {
+            NS_WARNING("Could not pan audio output to the right.");
+          }
+        } else {
+          panCenter = true;
+        }
+      } else {
+        panCenter = true;
+      }
+      if (panCenter) {
+        LOG(("%p Panning audio output to the center.", this));
+        if (cubeb_stream_set_panning(mCubebStream, 0.0) != CUBEB_OK) {
+          NS_WARNING("Could not pan audio output to the center.");
+        }
+        // This a microphone that goes through the headphone plug, reset the
+        // output to prevent echo building up.
+        if (strcmp(device->input_name, "emic") == 0) {
+          Reset();
+        }
+      }
+      cubeb_stream_device_destroy(mCubebStream, device);
+    }
+  }
+#endif
+}
+
+void AudioStream::DeviceChangedCallback() {
+  MonitorAutoLock mon(mMonitor);
+  PanOutputIfNeeded(mMicrophoneActive);
+  mShouldDropFrames = true;
+}
+
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams,
                        LatencyRequest aLatencyRequest)
 {
   {
     MonitorAutoLock mon(mMonitor);
@@ -595,22 +657,27 @@ AudioStream::OpenCubeb(cubeb_stream_para
     } else {
       MonitorAutoLock mon(mMonitor);
       mState = ERRORED;
       LOG(("AudioStream::OpenCubeb() %p failed to init cubeb", this));
       return NS_ERROR_FAILURE;
     }
   }
 
+  cubeb_stream_register_device_changed_callback(mCubebStream,
+                                                AudioStream::DeviceChangedCallback_s);
+
+  mState = INITIALIZED;
+
   if (!mStartTime.IsNull()) {
-		TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
+    TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
     LOG(("AudioStream creation time %sfirst: %u ms", mIsFirst ? "" : "not ",
-         (uint32_t) timeDelta.ToMilliseconds()));
-		Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
-                          Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds());
+          (uint32_t) timeDelta.ToMilliseconds()));
+    Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
+        Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds());
   }
 
   return NS_OK;
 }
 
 void
 AudioStream::CheckForStart()
 {
@@ -655,25 +722,30 @@ AudioInitTask::Run()
   return rv;
 }
 
 // aTime is the time in ms the samples were inserted into MediaStreamGraph
 nsresult
 AudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime)
 {
   MonitorAutoLock mon(mMonitor);
+
+  // See if we need to start() the stream, since we must do that from this thread
+  CheckForStart();
+
+  if (mShouldDropFrames) {
+    mBuffer.ContractTo(0);
+    return NS_OK;
+  }
   if (mState == ERRORED) {
     return NS_ERROR_FAILURE;
   }
   NS_ASSERTION(mState == INITIALIZED || mState == STARTED || mState == RUNNING,
     "Stream write in unexpected state.");
 
-  // See if we need to start() the stream, since we must do that from this thread
-  CheckForStart();
-
   // Downmix to Stereo.
   if (mChannels > 2 && mChannels <= 8) {
     DownmixAudioToStereo(const_cast<AudioDataValue*> (aBuf), mChannels, aFrames);
   }
   else if (mChannels > 8) {
     return NS_ERROR_FAILURE;
   }
 
@@ -746,19 +818,31 @@ AudioStream::Available()
   MonitorAutoLock mon(mMonitor);
   NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
   return BytesToFrames(mBuffer.Available());
 }
 
 void
 AudioStream::SetVolume(double aVolume)
 {
+  NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
+
+  if (cubeb_stream_set_volume(mCubebStream, aVolume * GetVolumeScale()) != CUBEB_OK) {
+    NS_WARNING("Could not change volume on cubeb stream.");
+  }
+}
+
+void
+AudioStream::SetMicrophoneActive(bool aActive)
+{
   MonitorAutoLock mon(mMonitor);
-  NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
-  mVolume = aVolume;
+
+  mMicrophoneActive = aActive;
+
+  PanOutputIfNeeded(mMicrophoneActive);
 }
 
 void
 AudioStream::Cancel()
 {
   MonitorAutoLock mon(mMonitor);
   mState = ERRORED;
   mon.NotifyAll();
@@ -789,21 +873,24 @@ AudioStream::Start()
 void
 AudioStream::StartUnlocked()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (!mCubebStream) {
     mNeedsStart = true;
     return;
   }
+
   if (mState == INITIALIZED) {
     int r;
     {
       MonitorAutoUnlock mon(mMonitor);
       r = cubeb_stream_start(mCubebStream);
+
+      PanOutputIfNeeded(mMicrophoneActive);
     }
     mState = r == CUBEB_OK ? STARTED : ERRORED;
     LOG(("AudioStream: started %p, state %s", this, mState == STARTED ? "STARTED" : "ERRORED"));
   }
 }
 
 void
 AudioStream::Pause()
@@ -1032,28 +1119,83 @@ AudioStream::GetTimeStretched(void* aBuf
     processedFrames += receivedFrames;
   } while (processedFrames < aFrames && !lowOnBufferedData);
 
   GetBufferInsertTime(aTimeMs);
 
   return processedFrames;
 }
 
+void
+AudioStream::Reset()
+{
+  mShouldDropFrames = true;
+  mNeedsStart = true;
+
+  cubeb_stream_params params;
+  params.rate = mInRate;
+  params.channels = mOutChannels;
+#if defined(__ANDROID__)
+#if defined(MOZ_B2G)
+  params.stream_type = ConvertChannelToCubebType(mAudioChannel);
+#else
+  params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#endif
+
+  if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
+    return;
+  }
+#endif
+
+  if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
+    params.format = CUBEB_SAMPLE_S16NE;
+  } else {
+    params.format = CUBEB_SAMPLE_FLOAT32NE;
+  }
+  mBytesPerFrame = sizeof(AudioDataValue) * mOutChannels;
+
+  // Size mBuffer for one second of audio.  This value is arbitrary, and was
+  // selected based on the observed behaviour of the existing AudioStream
+  // implementations.
+  uint32_t bufferLimit = FramesToBytes(mInRate);
+  NS_ABORT_IF_FALSE(bufferLimit % mBytesPerFrame == 0, "Must buffer complete frames");
+  mBuffer.Reset();
+  mBuffer.SetCapacity(bufferLimit);
+
+
+  if (mLatencyRequest == LowLatency) {
+    // Don't block this thread to initialize a cubeb stream.
+    // When this is done, it will start callbacks from Cubeb.  Those will
+    // cause us to move from INITIALIZED to RUNNING.  Until then, we
+    // can't access any cubeb functions.
+    // Use a RefPtr to avoid leaks if Dispatch fails
+    RefPtr<AudioInitTask> init = new AudioInitTask(this, mLatencyRequest, params);
+    init->Dispatch();
+    return;
+  }
+  // High latency - open synchronously
+  OpenCubeb(params, mLatencyRequest);
+
+  CheckForStart();
+}
+
 long
 AudioStream::DataCallback(void* aBuffer, long aFrames)
 {
   MonitorAutoLock mon(mMonitor);
   MOZ_ASSERT(mState != SHUTDOWN, "No data callback after shutdown");
   uint32_t available = std::min(static_cast<uint32_t>(FramesToBytes(aFrames)), mBuffer.Length());
   NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
   AudioDataValue* output = reinterpret_cast<AudioDataValue*>(aBuffer);
   uint32_t underrunFrames = 0;
   uint32_t servicedFrames = 0;
   int64_t insertTime;
 
+  mShouldDropFrames = false;
+
   // NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN)
   // Bug 996162
 
   // callback tells us cubeb succeeded initializing
   if (mState == STARTED) {
     // For low-latency streams, we want to minimize any built-up data when
     // we start getting callbacks.
     // Simple version - contract on first callback only.
@@ -1094,19 +1236,16 @@ AudioStream::DataCallback(void* aBuffer,
       if (mLatencyRequest == LowLatency && !mWritten) {
         servicedFrames = GetUnprocessedWithSilencePadding(output, aFrames, insertTime);
       } else {
         servicedFrames = GetUnprocessed(output, aFrames, insertTime);
       }
     } else {
       servicedFrames = GetTimeStretched(output, aFrames, insertTime);
     }
-    float scaled_volume = float(GetVolumeScale() * mVolume);
-
-    ScaleAudioSamples(output, aFrames * mOutChannels, scaled_volume);
 
     NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
 
     // Notify any blocked Write() call that more space is available in mBuffer.
     mon.NotifyAll();
   } else {
     GetBufferInsertTime(insertTime);
   }
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -153,29 +153,37 @@ public:
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   {
     size_t amount = 0;
     amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
     return amount;
   }
 
+  void Reset()
+  {
+    mBuffer = nullptr;
+    mCapacity = 0;
+    mStart = 0;
+    mCount = 0;
+  }
+
 private:
   nsAutoArrayPtr<uint8_t> mBuffer;
   uint32_t mCapacity;
   uint32_t mStart;
   uint32_t mCount;
 };
 
 class AudioInitTask;
 
 // Access to a single instance of this class must be synchronized by
 // callers, or made from a single thread.  One exception is that access to
-// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}
-// is thread-safe without external synchronization.
+// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels},
+// SetMicrophoneActive is thread-safe without external synchronization.
 class AudioStream MOZ_FINAL
 {
   virtual ~AudioStream();
 
 public:
   // Initialize Audio Library. Some Audio backends require initializing the
   // library before using it.
   static void InitLibrary();
@@ -207,30 +215,37 @@ public:
   // (22050Hz, 44100Hz, etc).
   nsresult Init(int32_t aNumChannels, int32_t aRate,
                 const dom::AudioChannel aAudioStreamChannel,
                 LatencyRequest aLatencyRequest);
 
   // Closes the stream. All future use of the stream is an error.
   void Shutdown();
 
+  void Reset();
+
   // Write audio data to the audio hardware.  aBuf is an array of AudioDataValues
   // AudioDataValue of length aFrames*mChannels.  If aFrames is larger
   // than the result of Available(), the write will block until sufficient
   // buffer space is available.  aTime is the time in ms associated with the first sample
   // for latency calculations
   nsresult Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp* aTime = nullptr);
 
   // Return the number of audio frames that can be written without blocking.
   uint32_t Available();
 
   // Set the current volume of the audio playback. This is a value from
   // 0 (meaning muted) to 1 (meaning full volume).  Thread-safe.
   void SetVolume(double aVolume);
 
+  // Informs the AudioStream that a microphone is being used by someone in the
+  // application.
+  void SetMicrophoneActive(bool aActive);
+  void PanOutputIfNeeded(bool aMicrophoneActive);
+
   // Block until buffered audio data has been consumed.
   void Drain();
 
   // Break any blocking operation and set the stream to shutdown.
   void Cancel();
 
   // Start the stream.
   void Start();
@@ -299,18 +314,24 @@ private:
     return static_cast<AudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
   }
 
   static void StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
   {
     static_cast<AudioStream*>(aThis)->StateCallback(aState);
   }
 
+
+  static void DeviceChangedCallback_s(void * aThis) {
+    static_cast<AudioStream*>(aThis)->DeviceChangedCallback();
+  }
+
   long DataCallback(void* aBuffer, long aFrames);
   void StateCallback(cubeb_state aState);
+  void DeviceChangedCallback();
 
   nsresult EnsureTimeStretcherInitializedUnlocked();
 
   // aTime is the time in ms the samples were inserted into MediaStreamGraph
   long GetUnprocessed(void* aBuffer, long aFrames, int64_t &aTime);
   long GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTime);
   long GetUnprocessedWithSilencePadding(void* aBuffer, long aFrames, int64_t &aTime);
 
@@ -326,16 +347,19 @@ private:
   Monitor mMonitor;
 
   // Input rate in Hz (characteristic of the media being played)
   int mInRate;
   // Output rate in Hz (characteristic of the playback rate)
   int mOutRate;
   int mChannels;
   int mOutChannels;
+#if defined(__ANDROID__)
+  dom::AudioChannel mAudioChannel;
+#endif
   // Number of frames written to the buffers.
   int64_t mWritten;
   AudioClock mAudioClock;
   nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
 
   // copy of Latency logger's starting time for offset calculations
   TimeStamp mStartTime;
@@ -356,19 +380,16 @@ private:
   FILE* mDumpFile;
 
   // Temporary audio buffer.  Filled by Write() and consumed by
   // DataCallback().  Once mBuffer is full, Write() blocks until sufficient
   // space becomes available in mBuffer.  mBuffer is sized in bytes, not
   // frames.
   CircularByteBuffer mBuffer;
 
-  // Software volume level.  Applied during the servicing of DataCallback().
-  double mVolume;
-
   // Owning reference to a cubeb_stream.  cubeb_stream_destroy is called by
   // nsAutoRef's destructor.
   nsAutoRef<cubeb_stream> mCubebStream;
 
   uint32_t mBytesPerFrame;
 
   uint32_t BytesToFrames(uint32_t aBytes) {
     NS_ASSERTION(aBytes % mBytesPerFrame == 0,
@@ -392,16 +413,22 @@ private:
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED,     // Stream disabled due to an internal error.
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
   bool mNeedsStart; // needed in case Start() is called before cubeb is open
   bool mIsFirst;
+  // True if a microphone is active.
+  bool mMicrophoneActive;
+  // When we are in the process of changing the output device, and the callback
+  // is not going to be called for a little while, simply drop incoming frames.
+  // This is only on OSX for now, because other systems handle this gracefully.
+  bool mShouldDropFrames;
 
   // This mutex protects the static members below.
   static StaticMutex sMutex;
   static cubeb* sCubebContext;
 
   // Prefered samplerate, in Hz (characteristic of the
   // hardware/mixer/platform/API used).
   static uint32_t sPreferredSampleRate;
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -105,16 +105,17 @@ MediaStreamGraphImpl::RemoveStream(Media
     MonitorAutoLock lock(mMonitor);
     for (uint32_t i = 0; i < mStreamUpdates.Length(); ++i) {
       if (mStreamUpdates[i].mStream == aStream) {
         mStreamUpdates[i].mStream = nullptr;
       }
     }
   }
 
+  // Ensure that mFirstCycleBreaker and mMixer are updated when necessary.
   SetStreamOrderDirty();
 
   mStreams.RemoveElement(aStream);
   NS_RELEASE(aStream); // probably destroying it
 
   STREAM_LOG(PR_LOG_DEBUG, ("Removing media stream %p from the graph", aStream));
 }
 
@@ -563,18 +564,28 @@ MediaStreamGraphImpl::UpdateStreamOrder(
     if (stream->AsSourceStream() &&
         stream->AsSourceStream()->NeedsMixing()) {
       shouldMix = true;
     }
   }
 
   if (!mMixer && shouldMix) {
     mMixer = new AudioMixer(AudioMixerCallback);
+    for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+      for (uint32_t i = 0; i < mStreams[i]->mAudioOutputStreams.Length(); ++i) {
+        mStreams[i]->mAudioOutputStreams[i].mStream->SetMicrophoneActive(true);
+      }
+    }
   } else if (mMixer && !shouldMix) {
     mMixer = nullptr;
+    for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+      for (uint32_t i = 0; i < mStreams[i]->mAudioOutputStreams.Length(); ++i) {
+        mStreams[i]->mAudioOutputStreams[i].mStream->SetMicrophoneActive(false);
+      }
+    }
   }
 
   // The algorithm for finding cycles is based on Tim Leslie's iterative
   // implementation [1][2] of Pearce's variant [3] of Tarjan's strongly
   // connected components (SCC) algorithm.  There are variations (a) to
   // distinguish whether streams in SCCs of size 1 are in a cycle and (b) to
   // re-run the algorithm over SCCs with breaks at DelayNodes.
   //
@@ -687,58 +698,60 @@ MediaStreamGraphImpl::UpdateStreamOrder(
     }
 
     // |ps| is the root of an SCC involving no other streams on dfsStack, the
     // complete SCC has been recorded, and streams in this SCC are part of at
     // least one cycle.
     MOZ_ASSERT(cycleStackMarker == ps->mCycleMarker);
     // If there are DelayNodes in this SCC, then they may break the cycles.
     bool haveDelayNode = false;
-    auto next = static_cast<ProcessedMediaStream*>(sccStack.getFirst());
+    auto next = sccStack.getFirst();
     // Streams in this SCC are identified by mCycleMarker <= cycleStackMarker.
     // (There may be other streams later in sccStack from other incompletely
     // searched SCCs, involving streams still on dfsStack.)
     //
     // DelayNodes in cycles must behave differently from those not in cycles,
     // so all DelayNodes in the SCC must be identified.
-    while (next && next->mCycleMarker <= cycleStackMarker) {
+    while (next && static_cast<ProcessedMediaStream*>(next)->
+           mCycleMarker <= cycleStackMarker) {
       auto ns = next->AsAudioNodeStream();
       // Get next before perhaps removing from list below.
-      next = static_cast<ProcessedMediaStream*>(next->getNext());
+      next = next->getNext();
       if (ns && ns->Engine()->AsDelayNodeEngine()) {
         haveDelayNode = true;
         // DelayNodes break cycles by producing their output in a
         // preprocessing phase; they do not need to be ordered before their
         // consumers.  Order them at the tail of mStreams so that they can be
         // handled specially.  Do so now, so that DFS ignores them.
         ns->remove();
         ns->mCycleMarker = 0;
         --mFirstCycleBreaker;
         mStreams[mFirstCycleBreaker] = ns;
       }
     }
     auto after_scc = next;
-    while ((next = static_cast<ProcessedMediaStream*>(sccStack.popFirst()))
-           != after_scc) {
+    while ((next = sccStack.getFirst()) != after_scc) {
+      next->remove();
+      auto removed = static_cast<ProcessedMediaStream*>(next);
       if (haveDelayNode) {
         // Return streams to the DFS stack again (to order and detect cycles
         // without delayNodes).  Any of these streams that are still inputs
         // for streams on the visited stack must be returned to the front of
         // the stack to be ordered before their dependents.  We know that none
         // of these streams need input from streams on the visited stack, so
         // they can all be searched and ordered before the current stack head
         // is popped.
-        next->mCycleMarker = NOT_VISITED;
-        dfsStack.insertFront(next);
+        removed->mCycleMarker = NOT_VISITED;
+        dfsStack.insertFront(removed);
       } else {
         // Streams in cycles without any DelayNodes must be muted, and so do
         // not need input and can be ordered now.  They must be ordered before
         // their consumers so that their muted output is available.
-        next->mCycleMarker = IN_MUTED_CYCLE;
-        mStreams[orderedStreamCount] = next;
+        removed->mCycleMarker = IN_MUTED_CYCLE;
+        mStreams[orderedStreamCount] = removed;
         ++orderedStreamCount;
       }
     }
   }
 
   MOZ_ASSERT(orderedStreamCount == mFirstCycleBreaker);
 }
 
@@ -945,16 +958,19 @@ MediaStreamGraphImpl::CreateOrDestroyAud
         // XXX for now, allocate stereo output. But we need to fix this to
         // match the system's ideal channel configuration.
         // NOTE: we presume this is either fast or async-under-the-covers
         audioOutputStream->mStream->Init(2, mSampleRate,
                                          aStream->mAudioChannelType,
                                          AudioStream::LowLatency);
         audioOutputStream->mTrackID = tracks->GetID();
 
+        // If there is a mixer, there is a micrphone active.
+        audioOutputStream->mStream->SetMicrophoneActive(mMixer);
+
         LogLatency(AsyncLatencyLogger::AudioStreamCreate,
                    reinterpret_cast<uint64_t>(aStream),
                    reinterpret_cast<int64_t>(audioOutputStream->mStream.get()));
       }
     }
   }
 
   for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
@@ -2029,40 +2045,41 @@ MediaStream::RemoveAllListenersImpl()
     listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
   }
   mListeners.Clear();
 }
 
 void
 MediaStream::DestroyImpl()
 {
-  RemoveAllListenersImpl();
-
   for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
     mConsumers[i]->Disconnect();
   }
   for (uint32_t i = 0; i < mAudioOutputStreams.Length(); ++i) {
     mAudioOutputStreams[i].mStream->Shutdown();
   }
   mAudioOutputStreams.Clear();
+  mGraph = nullptr;
 }
 
 void
 MediaStream::Destroy()
 {
   // Keep this stream alive until we leave this method
   nsRefPtr<MediaStream> kungFuDeathGrip = this;
 
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream) : ControlMessage(aStream) {}
     virtual void Run()
     {
+      mStream->RemoveAllListenersImpl();
+      auto graph = mStream->GraphImpl();
       mStream->DestroyImpl();
-      mStream->GraphImpl()->RemoveStream(mStream);
+      graph->RemoveStream(mStream);
     }
     virtual void RunDuringShutdown()
     { Run(); }
   };
   mWrapper = nullptr;
   GraphImpl()->AppendMessage(new Message(this));
   // Message::RunDuringShutdown may have removed this stream from the graph,
   // but our kungFuDeathGrip above will have kept this stream alive if
@@ -2333,29 +2350,28 @@ MediaStream::ApplyTrackDisabling(TrackID
   if (aRawSegment) {
     aRawSegment->ReplaceWithDisabled();
   }
 }
 
 void
 SourceMediaStream::DestroyImpl()
 {
-  {
-    MutexAutoLock lock(mMutex);
-    mDestroyed = true;
-  }
+  // Hold mMutex while mGraph is reset so that other threads holding mMutex
+  // can null-check know that the graph will not destroyed.
+  MutexAutoLock lock(mMutex);
   MediaStream::DestroyImpl();
 }
 
 void
 SourceMediaStream::SetPullEnabled(bool aEnabled)
 {
   MutexAutoLock lock(mMutex);
   mPullEnabled = aEnabled;
-  if (mPullEnabled && !mDestroyed) {
+  if (mPullEnabled && GraphImpl()) {
     GraphImpl()->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
                             MediaSegment* aSegment)
 {
@@ -2365,18 +2381,18 @@ SourceMediaStream::AddTrack(TrackID aID,
   data->mInputRate = aRate;
   // We resample all audio input tracks to the sample rate of the audio mixer.
   data->mOutputRate = aSegment->GetType() == MediaSegment::AUDIO ?
                       GraphImpl()->AudioSampleRate() : aRate;
   data->mStart = aStart;
   data->mCommands = TRACK_CREATE;
   data->mData = aSegment;
   data->mHaveEnough = false;
-  if (!mDestroyed) {
-    GraphImpl()->EnsureNextIteration();
+  if (auto graph = GraphImpl()) {
+    graph->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
 {
   if (aSegment->GetType() != MediaSegment::AUDIO ||
       aTrackData->mInputRate == GraphImpl()->AudioSampleRate()) {
@@ -2408,17 +2424,18 @@ SourceMediaStream::ResampleAudioToGraphS
 }
 
 bool
 SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment)
 {
   MutexAutoLock lock(mMutex);
   // ::EndAllTrackAndFinished() can end these before the sources notice
   bool appended = false;
-  if (!mFinished) {
+  auto graph = GraphImpl();
+  if (!mFinished && graph) {
     TrackData *track = FindDataForTrack(aID);
     if (track) {
       // Data goes into mData, and on the next iteration of the MSG moves
       // into the track's segment after NotifyQueuedTrackChanges().  This adds
       // 0-10ms of delay before data gets to direct listeners.
       // Indirect listeners (via subsequent TrackUnion nodes) are synced to
       // playout time, and so can be delayed by buffering.
 
@@ -2427,23 +2444,21 @@ SourceMediaStream::AppendToTrack(TrackID
       ApplyTrackDisabling(aID, aSegment, aRawSegment);
 
       ResampleAudioToGraphSampleRate(track, aSegment);
 
       // Must notify first, since AppendFrom() will empty out aSegment
       NotifyDirectConsumers(track, aRawSegment ? aRawSegment : aSegment);
       track->mData->AppendFrom(aSegment); // note: aSegment is now dead
       appended = true;
+      graph->EnsureNextIteration();
     } else {
       aSegment->Clear();
     }
   }
-  if (!mDestroyed) {
-    GraphImpl()->EnsureNextIteration();
-  }
   return appended;
 }
 
 void
 SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack,
                                          MediaSegment *aSegment)
 {
   // Call with mMutex locked
@@ -2530,39 +2545,39 @@ SourceMediaStream::EndTrack(TrackID aID)
   MutexAutoLock lock(mMutex);
   // ::EndAllTrackAndFinished() can end these before the sources call this
   if (!mFinished) {
     TrackData *track = FindDataForTrack(aID);
     if (track) {
       track->mCommands |= TRACK_END;
     }
   }
-  if (!mDestroyed) {
-    GraphImpl()->EnsureNextIteration();
+  if (auto graph = GraphImpl()) {
+    graph->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
 {
   MutexAutoLock lock(mMutex);
   MOZ_ASSERT(aKnownTime >= mUpdateKnownTracksTime);
   mUpdateKnownTracksTime = aKnownTime;
-  if (!mDestroyed) {
-    GraphImpl()->EnsureNextIteration();
+  if (auto graph = GraphImpl()) {
+    graph->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::FinishWithLockHeld()
 {
   mMutex.AssertCurrentThreadOwns();
   mUpdateFinished = true;
-  if (!mDestroyed) {
-    GraphImpl()->EnsureNextIteration();
+  if (auto graph = GraphImpl()) {
+    graph->EnsureNextIteration();
   }
 }
 
 void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
@@ -2655,16 +2670,17 @@ MediaInputPort::Destroy()
   class Message : public ControlMessage {
   public:
     Message(MediaInputPort* aPort)
       : ControlMessage(nullptr), mPort(aPort) {}
     virtual void Run()
     {
       mPort->Disconnect();
       --mPort->GraphImpl()->mPortCount;
+      mPort->SetGraphImpl(nullptr);
       NS_RELEASE(mPort);
     }
     virtual void RunDuringShutdown()
     {
       Run();
     }
     MediaInputPort* mPort;
   };
@@ -2681,17 +2697,17 @@ MediaStreamGraph*
 MediaInputPort::Graph()
 {
   return mGraph;
 }
 
 void
 MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
 {
-  MOZ_ASSERT(!mGraph, "Should only be called once");
+  MOZ_ASSERT(!mGraph || !aGraph, "Should only be set once");
   mGraph = aGraph;
 }
 
 already_AddRefed<MediaInputPort>
 ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags,
                                         uint16_t aInputNumber, uint16_t aOutputNumber)
 {
   // This method creates two references to the MediaInputPort: one for
@@ -2754,17 +2770,20 @@ ProcessedMediaStream::SetAutofinish(bool
 
 void
 ProcessedMediaStream::DestroyImpl()
 {
   for (int32_t i = mInputs.Length() - 1; i >= 0; --i) {
     mInputs[i]->Disconnect();
   }
   MediaStream::DestroyImpl();
-  GraphImpl()->SetStreamOrderDirty();
+  // The stream order is only important if there are connections, in which
+  // case MediaInputPort::Disconnect() called SetStreamOrderDirty().
+  // MediaStreamGraphImpl::RemoveStream() will also call
+  // SetStreamOrderDirty(), for other reasons.
 }
 
 MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate)
   : mCurrentTime(0)
   , mStateComputedTime(0)
   , mProcessingGraphUpdateIndex(0)
   , mPortCount(0)
   , mMonitor("MediaStreamGraphImpl")
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -672,17 +672,17 @@ protected:
 
   // This state is only used on the main thread.
   DOMMediaStream* mWrapper;
   // Main-thread views of state
   StreamTime mMainThreadCurrentTime;
   bool mMainThreadFinished;
   bool mMainThreadDestroyed;
 
-  // Our media stream graph
+  // Our media stream graph.  null if destroyed on the graph thread.
   MediaStreamGraphImpl* mGraph;
 
   dom::AudioChannel mAudioChannelType;
 };
 
 /**
  * This is a stream into which a decoder can write audio and video.
  *
@@ -692,17 +692,17 @@ protected:
 class SourceMediaStream : public MediaStream {
 public:
   SourceMediaStream(DOMMediaStream* aWrapper) :
     MediaStream(aWrapper),
     mLastConsumptionState(MediaStreamListener::NOT_CONSUMED),
     mMutex("mozilla::media::SourceMediaStream"),
     mUpdateKnownTracksTime(0),
     mPullEnabled(false),
-    mUpdateFinished(false), mDestroyed(false)
+    mUpdateFinished(false)
   {}
 
   virtual SourceMediaStream* AsSourceStream() { return this; }
 
   // Media graph thread only
   virtual void DestroyImpl();
 
   // Call these on any thread.
@@ -722,18 +722,16 @@ public:
    * Add a new track to the stream starting at the given base time (which
    * must be greater than or equal to the last time passed to
    * AdvanceKnownTracksTime). Takes ownership of aSegment. aSegment should
    * contain data starting after aStart.
    */
   void AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
                 MediaSegment* aSegment);
 
-  struct TrackData;
-  void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
   /**
    * Append media data to a track. Ownership of aSegment remains with the caller,
    * but aSegment is emptied.
    * Returns false if the data was not appended because no such track exists
    * or the stream was already finished.
    */
   bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
   /**
@@ -790,20 +788,23 @@ public:
    * Returns amount of time (data) that is currently buffered in the track,
    * assuming playout via PlayAudio or via a TrackUnion - note that
    * NotifyQueuedTrackChanges() on a SourceMediaStream will occur without
    * any "extra" buffering, but NotifyQueued TrackChanges() on a TrackUnion
    * will be buffered.
    */
   TrackTicks GetBufferedTicks(TrackID aID);
 
+  void RegisterForAudioMixing();
+
   // XXX need a Reset API
 
   friend class MediaStreamGraphImpl;
 
+protected:
   struct ThreadAndRunnable {
     void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable)
     {
       mTarget = aTarget;
       mRunnable = aRunnable;
     }
 
     nsCOMPtr<nsIEventTarget> mTarget;
@@ -835,20 +836,20 @@ public:
     uint32_t mCommands;
     // Each time the track updates are flushed to the media graph thread,
     // the segment buffer is emptied.
     nsAutoPtr<MediaSegment> mData;
     nsTArray<ThreadAndRunnable> mDispatchWhenNotEnough;
     bool mHaveEnough;
   };
 
-  void RegisterForAudioMixing();
   bool NeedsMixing();
 
-protected:
+  void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
+
   TrackData* FindDataForTrack(TrackID aID)
   {
     for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
       if (mUpdateTracks[i].mID == aID) {
         return &mUpdateTracks[i];
       }
     }
     return nullptr;
@@ -870,17 +871,16 @@ protected:
   // held together.
   Mutex mMutex;
   // protected by mMutex
   StreamTime mUpdateKnownTracksTime;
   nsTArray<TrackData> mUpdateTracks;
   nsTArray<nsRefPtr<MediaStreamDirectListener> > mDirectListeners;
   bool mPullEnabled;
   bool mUpdateFinished;
-  bool mDestroyed;
   bool mNeedsMixing;
 };
 
 /**
  * Represents a connection between a ProcessedMediaStream and one of its
  * input streams.
  * We make these refcounted so that stream-related messages with MediaInputPort*
  * pointers can be sent to the main thread safely.
--- a/content/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegDataDecoder.cpp
@@ -92,16 +92,20 @@ FFmpegDataDecoder<LIBAV_VER>::Init()
   mCodecContext->thread_safe_callbacks = false;
 
   mCodecContext->extradata_size = mExtraData.length();
   for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) {
     mExtraData.append(0);
   }
   mCodecContext->extradata = mExtraData.begin();
 
+  if (codec->capabilities & CODEC_CAP_DR1) {
+    mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
+  }
+
   if (avcodec_open2(mCodecContext, codec, nullptr) < 0) {
     NS_WARNING("Couldn't initialise ffmpeg decoder");
     return NS_ERROR_FAILURE;
   }
 
   if (mCodecContext->codec_type == AVMEDIA_TYPE_AUDIO &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLTP) {
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
@@ -73,58 +73,62 @@ FFmpegH264Decoder<LIBAV_VER>::DecodeFram
   if (bytesConsumed < 0) {
     NS_WARNING("FFmpeg video decoder error.");
     mCallback->Error();
     return;
   }
 
   // If we've decoded a frame then we need to output it
   if (decoded) {
-    nsAutoPtr<VideoData> data;
-
     VideoInfo info;
     info.mDisplay = nsIntSize(mCodecContext->width, mCodecContext->height);
     info.mStereoMode = StereoMode::MONO;
     info.mHasVideo = true;
 
-    data = VideoData::CreateFromImage(
-      info, mImageContainer, aSample->byte_offset, mFrame->pkt_pts,
-      aSample->duration, static_cast<Image*>(mFrame->opaque),
-      aSample->is_sync_point, -1,
-      gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
+    VideoData::YCbCrBuffer b;
+    b.mPlanes[0].mData = mFrame->data[0];
+    b.mPlanes[0].mStride = mFrame->linesize[0];
+    b.mPlanes[0].mHeight = mFrame->height;
+    b.mPlanes[0].mWidth = mFrame->width;
+    b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
+
+    b.mPlanes[1].mData = mFrame->data[1];
+    b.mPlanes[1].mStride = mFrame->linesize[1];
+    b.mPlanes[1].mHeight = (mFrame->height + 1) >> 1;
+    b.mPlanes[1].mWidth = (mFrame->width + 1) >> 1;
+    b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
 
-    mCallback->Output(data.forget());
+    b.mPlanes[2].mData = mFrame->data[2];
+    b.mPlanes[2].mStride = mFrame->linesize[2];
+    b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
+    b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
+    b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
+
+    VideoData *v = VideoData::Create(info,
+                                     mImageContainer,
+                                     aSample->byte_offset,
+                                     mFrame->pkt_pts,
+                                     aSample->duration,
+                                     b,
+                                     aSample->is_sync_point,
+                                     -1,
+                                     gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
+    if (!v) {
+      NS_WARNING("image allocation error.");
+      mCallback->Error();
+      return;
+    }
+    mCallback->Output(v);
   }
 
   if (mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
-static void
-PlanarYCbCrDataFromAVFrame(mozilla::layers::PlanarYCbCrData& aData,
-                           AVFrame* aFrame)
-{
-  aData.mPicX = aData.mPicY = 0;
-  aData.mPicSize = mozilla::gfx::IntSize(aFrame->width, aFrame->height);
-  aData.mStereoMode = StereoMode::MONO;
-
-  aData.mYChannel = aFrame->data[0];
-  aData.mYStride = aFrame->linesize[0];
-  aData.mYSize = aData.mPicSize;
-  aData.mYSkip = 0;
-
-  aData.mCbChannel = aFrame->data[1];
-  aData.mCrChannel = aFrame->data[2];
-  aData.mCbCrStride = aFrame->linesize[1];
-  aData.mCbSkip = aData.mCrSkip = 0;
-  aData.mCbCrSize =
-    mozilla::gfx::IntSize((aFrame->width + 1) / 2, (aFrame->height + 1) / 2);
-}
-
 /* static */ int
 FFmpegH264Decoder<LIBAV_VER>::AllocateBufferCb(AVCodecContext* aCodecContext,
                                                AVFrame* aFrame)
 {
   MOZ_ASSERT(aCodecContext->codec_type == AVMEDIA_TYPE_VIDEO);
 
   FFmpegH264Decoder* self =
     static_cast<FFmpegH264Decoder*>(aCodecContext->opaque);
@@ -154,26 +158,27 @@ FFmpegH264Decoder<LIBAV_VER>::ReleaseBuf
       break;
   }
 }
 
 int
 FFmpegH264Decoder<LIBAV_VER>::AllocateYUV420PVideoBuffer(
   AVCodecContext* aCodecContext, AVFrame* aFrame)
 {
-  // Older versions of ffmpeg require that edges be allocated* around* the
-  // actual image.
-  int edgeWidth = avcodec_get_edge_width();
+  bool needAlign = aCodecContext->codec->capabilities & CODEC_CAP_DR1;
+  int edgeWidth =  needAlign ? avcodec_get_edge_width() : 0;
   int decodeWidth = aCodecContext->width + edgeWidth * 2;
   int decodeHeight = aCodecContext->height + edgeWidth * 2;
 
-  // Align width and height to possibly speed up decode.
-  int stride_align[AV_NUM_DATA_POINTERS];
-  avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight,
-                            stride_align);
+  if (needAlign) {
+    // Align width and height to account for CODEC_FLAG_EMU_EDGE.
+    int stride_align[AV_NUM_DATA_POINTERS];
+    avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight,
+                              stride_align);
+  }
 
   // Get strides for each plane.
   av_image_fill_linesizes(aFrame->linesize, aCodecContext->pix_fmt,
                           decodeWidth);
 
   // Let FFmpeg set up its YUV plane pointers and tell us how much memory we
   // need.
   // Note that we're passing |nullptr| here as the base address as we haven't
@@ -210,20 +215,16 @@ FFmpegH264Decoder<LIBAV_VER>::AllocateYU
 
   // Unused, but needs to be non-zero to keep ffmpeg happy.
   aFrame->type = GECKO_FRAME_TYPE;
 
   aFrame->extended_data = aFrame->data;
   aFrame->width = aCodecContext->width;
   aFrame->height = aCodecContext->height;
 
-  mozilla::layers::PlanarYCbCrData data;
-  PlanarYCbCrDataFromAVFrame(data, aFrame);
-  ycbcr->SetDataNoCopy(data);
-
   aFrame->opaque = static_cast<void*>(image.forget().take());
 
   return 0;
 }
 
 nsresult
 FFmpegH264Decoder<LIBAV_VER>::Input(mp4_demuxer::MP4Sample* aSample)
 {
--- a/content/media/mediasource/MediaSourceDecoder.cpp
+++ b/content/media/mediasource/MediaSourceDecoder.cpp
@@ -226,16 +226,22 @@ private:
 
   MediaDecoderReader* GetVideoReader() {
     if (mActiveVideoDecoder == -1) {
       return nullptr;
     }
     return mDecoders[mActiveVideoDecoder]->GetReader();
   }
 
+  void SetMediaSourceDuration(double aDuration) {
+    MOZ_ASSERT(NS_IsMainThread());
+    ErrorResult dummy;
+    mMediaSource->SetDuration(aDuration, dummy);
+  }
+
   nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders;
   nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
 
   int32_t mActiveVideoDecoder;
   int32_t mActiveAudioDecoder;
   dom::MediaSource* mMediaSource;
 };
 
@@ -580,18 +586,19 @@ MediaSourceReader::ReadMetadata(MediaInf
       maxDuration = std::max(maxDuration, mDecoders[i]->GetMediaDuration());
       MSE_DEBUG("%p: MSR::ReadMetadata audio decoder=%u maxDuration=%lld", this, i, maxDuration);
     }
   }
 
   if (maxDuration != -1) {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mDecoder->SetMediaDuration(maxDuration);
-    ErrorResult dummy;
-    mMediaSource->SetDuration(maxDuration, dummy);
+    nsRefPtr<nsIRunnable> task (
+      NS_NewRunnableMethodWithArg<double>(this, &MediaSourceReader::SetMediaSourceDuration, maxDuration));
+    NS_DispatchToMainThread(task);
   }
 
   *aInfo = mInfo;
   *aTags = nullptr; // TODO: Handle metadata.
 
   return NS_OK;
 }
 
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
   'encoder',
   'gmp',
   'mediasource',
   'ogg',
+  'systemservices',
   'webaudio',
   'webvtt'
 ]
 
 TEST_DIRS += ['compiledtest']
 
 if CONFIG['MOZ_RAW']:
     DIRS += ['raw']
rename from content/media/webrtc/LoadManager.cpp
rename to content/media/systemservices/LoadManager.cpp
rename from content/media/webrtc/LoadManager.h
rename to content/media/systemservices/LoadManager.h
rename from content/media/webrtc/LoadManagerFactory.cpp
rename to content/media/systemservices/LoadManagerFactory.cpp
rename from content/media/webrtc/LoadManagerFactory.h
rename to content/media/systemservices/LoadManagerFactory.h
rename from content/media/webrtc/LoadMonitor.cpp
rename to content/media/systemservices/LoadMonitor.cpp
rename from content/media/webrtc/LoadMonitor.h
rename to content/media/systemservices/LoadMonitor.h
new file mode 100644
--- /dev/null
+++ b/content/media/systemservices/OSXRunLoopSingleton.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OSXRunLoopSingleton.h"
+#include <mozilla/StaticMutex.h>
+#include <mozilla/NullPtr.h>
+
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/AudioHardware.h>
+#include <CoreAudio/HostTime.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+static bool gRunLoopSet = false;
+static mozilla::StaticMutex gMutex;
+
+void mozilla_set_coreaudio_notification_runloop_if_needed()
+{
+  mozilla::StaticMutexAutoLock lock(gMutex);
+  if (gRunLoopSet) {
+    return;
+  }
+
+  /* This is needed so that AudioUnit listeners get called on this thread, and
+   * not the main thread. If we don't do that, they are not called, or a crash
+   * occur, depending on the OSX version. */
+  AudioObjectPropertyAddress runloop_address = {
+    kAudioHardwarePropertyRunLoop,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+  };
+
+  CFRunLoopRef run_loop = nullptr;
+
+  OSStatus r;
+  r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
+                                 &runloop_address,
+                                 0, NULL, sizeof(CFRunLoopRef), &run_loop);
+  if (r != noErr) {
+    NS_WARNING("Could not make global CoreAudio notifications use their own thread.");
+  }
+
+  gRunLoopSet = true;
+}
new file mode 100644
--- /dev/null
+++ b/content/media/systemservices/OSXRunLoopSingleton.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef OSXRUNLOOPSINGLETON_H_
+#define OSXRUNLOOPSINGLETON_H_
+
+#include <mozilla/Types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* This function tells CoreAudio to use its own thread for device change
+ * notifications, and can be called from any thread without external
+ * synchronization. */
+void MOZ_EXPORT
+mozilla_set_coreaudio_notification_runloop_if_needed();
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // OSXRUNLOOPSINGLETON_H_
new file mode 100644
--- /dev/null
+++ b/content/media/systemservices/OpenSLESProvider.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OpenSLESProvider.h"
+#include "prlog.h"
+#include "nsDebug.h"
+#include "mozilla/NullPtr.h"
+
+#include <dlfcn.h>
+#include <SLES/OpenSLES_Android.h>
+#include <SLES/OpenSLES_AndroidConfiguration.h>
+
+// NSPR_LOG_MODULES=OpenSLESProvider:5
+#undef LOG
+#undef LOG_ENABLED
+#if defined(PR_LOGGING)
+PRLogModuleInfo *gOpenSLESProviderLog;
+#define LOG(args) PR_LOG(gOpenSLESProviderLog, PR_LOG_DEBUG, args)
+#define LOG_ENABLED() PR_LOG_TEST(gLoadManagerLog, 5)
+#else
+#define LOG(args)
+#define LOG_ENABLED() (false)
+#endif
+
+namespace mozilla {
+
+OpenSLESProvider::OpenSLESProvider()
+  : mLock("OpenSLESProvider.mLock"),
+    mSLEngine(nullptr),
+    mSLEngineUsers(0),
+    mIsRealized(false),
+    mOpenSLESLib(nullptr)
+{
+#if defined(PR_LOGGING)
+  if (!gOpenSLESProviderLog)
+    gOpenSLESProviderLog = PR_NewLogModule("OpenSLESProvider");
+  LOG(("OpenSLESProvider being initialized"));
+#endif
+}
+
+OpenSLESProvider::~OpenSLESProvider()
+{
+  if (mOpenSLESLib) {
+    LOG(("OpenSLES Engine was not properly Destroyed"));
+    (void)dlclose(mOpenSLESLib);
+  }
+}
+
+/* static */
+OpenSLESProvider& OpenSLESProvider::getInstance()
+{
+  // This doesn't need a Mutex in C++11 or GCC 4.3+, see N2660 and
+  // https://gcc.gnu.org/projects/cxx0x.html
+  static OpenSLESProvider instance;
+  return instance;
+}
+
+/* static */
+SLresult OpenSLESProvider::Get(SLObjectItf * aObjectm,
+                               SLuint32 aOptionCount,
+                               const SLEngineOption *aOptions)
+{
+  OpenSLESProvider& provider = OpenSLESProvider::getInstance();
+  return provider.GetEngine(aObjectm, aOptionCount, aOptions);
+}
+
+SLresult OpenSLESProvider::GetEngine(SLObjectItf * aObjectm,
+                                     SLuint32 aOptionCount,
+                                     const SLEngineOption *aOptions)
+{
+  MutexAutoLock lock(mLock);
+  LOG(("Getting OpenSLES engine"));
+  // Bug 1042051: Validate options are the same
+  if (mSLEngine != nullptr) {
+    *aObjectm = mSLEngine;
+    mSLEngineUsers++;
+    LOG(("Returning existing engine, %d users", mSLEngineUsers));
+    return SL_RESULT_SUCCESS;
+  } else {
+    int res = ConstructEngine(aObjectm, aOptionCount, aOptions);
+    if (res == SL_RESULT_SUCCESS) {
+      // Bug 1042051: Store engine options
+      mSLEngine = *aObjectm;
+      mSLEngineUsers++;
+      LOG(("Returning new engine"));
+    } else {
+      LOG(("Error getting engine: %d", res));
+    }
+    return res;
+  }
+}
+
+SLresult OpenSLESProvider::ConstructEngine(SLObjectItf * aObjectm,
+                                           SLuint32 aOptionCount,
+                                           const SLEngineOption *aOptions)
+{
+  mLock.AssertCurrentThreadOwns();
+
+  if (!mOpenSLESLib) {
+    mOpenSLESLib = dlopen("libOpenSLES.so", RTLD_LAZY);
+    if (!mOpenSLESLib) {
+      LOG(("Failed to dlopen OpenSLES library"));
+      return SL_RESULT_MEMORY_FAILURE;
+    }
+  }
+
+  typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
+                                       SLuint32,
+                                       const SLEngineOption *,
+                                       SLuint32,
+                                       const SLInterfaceID *,
+                                       const SLboolean *);
+
+  slCreateEngine_t f_slCreateEngine =
+    (slCreateEngine_t)dlsym(mOpenSLESLib, "slCreateEngine");
+  int result = f_slCreateEngine(aObjectm, aOptionCount, aOptions, 0, NULL, NULL);
+  return result;
+}
+
+/* static */
+void OpenSLESProvider::Destroy(SLObjectItf * aObjectm)
+{
+  OpenSLESProvider& provider = OpenSLESProvider::getInstance();
+  provider.DestroyEngine(aObjectm);
+}
+
+void OpenSLESProvider::DestroyEngine(SLObjectItf * aObjectm)
+{
+  MutexAutoLock lock(mLock);
+  NS_ASSERTION(mOpenSLESLib, "OpenSLES destroy called but library is not open");
+
+  mSLEngineUsers--;
+  LOG(("Freeing engine, %d users left", mSLEngineUsers));
+  if (mSLEngineUsers) {
+    return;
+  }
+
+  (*(*aObjectm))->Destroy(*aObjectm);
+  // This assumes SLObjectItf is a pointer, but given the previous line,
+  // that's a given.
+  *aObjectm = nullptr;
+
+  (void)dlclose(mOpenSLESLib);
+  mOpenSLESLib = nullptr;
+  mIsRealized = false;
+}
+
+/* static */
+SLresult OpenSLESProvider::Realize(SLObjectItf aObjectm)
+{
+  OpenSLESProvider& provider = OpenSLESProvider::getInstance();
+  return provider.RealizeEngine(aObjectm);
+}
+
+SLresult OpenSLESProvider::RealizeEngine(SLObjectItf aObjectm)
+{
+  MutexAutoLock lock(mLock);
+  NS_ASSERTION(mOpenSLESLib, "OpenSLES realize called but library is not open");
+  NS_ASSERTION(aObjectm != nullptr, "OpenSLES realize engine with empty ObjectItf");
+
+  if (mIsRealized) {
+    LOG(("Not realizing already realized engine"));
+    return SL_RESULT_SUCCESS;
+  } else {
+    SLresult res = (*aObjectm)->Realize(aObjectm, SL_BOOLEAN_FALSE);
+    if (res != SL_RESULT_SUCCESS) {
+      LOG(("Error realizing OpenSLES engine: %d", res));
+    } else {
+      LOG(("Realized OpenSLES engine"));
+      mIsRealized = true;
+    }
+    return res;
+  }
+}
+
+} // namespace mozilla
+
+extern "C" {
+SLresult mozilla_get_sles_engine(SLObjectItf * aObjectm,
+                                 SLuint32 aOptionCount,
+                                 const SLEngineOption *aOptions)
+{
+  return mozilla::OpenSLESProvider::Get(aObjectm, aOptionCount, aOptions);
+}
+
+void mozilla_destroy_sles_engine(SLObjectItf * aObjectm)
+{
+  mozilla::OpenSLESProvider::Destroy(aObjectm);
+}
+
+SLresult mozilla_realize_sles_engine(SLObjectItf aObjectm)
+{
+  return mozilla::OpenSLESProvider::Realize(aObjectm);
+}
+
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/systemservices/OpenSLESProvider.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _OPENSLESPROVIDER_H_
+#define _OPENSLESPROVIDER_H_
+
+#include <SLES/OpenSLES.h>
+#include <mozilla/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern MOZ_EXPORT
+SLresult mozilla_get_sles_engine(SLObjectItf * aObjectm,
+                                 SLuint32 aOptionCount,
+                                 const SLEngineOption *aOptions);
+extern MOZ_EXPORT
+void mozilla_destroy_sles_engine(SLObjectItf * aObjectm);
+/* Realize is always in synchronous mode. */
+extern MOZ_EXPORT
+SLresult mozilla_realize_sles_engine(SLObjectItf aObjectm);
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+#include "mozilla/Mutex.h"
+
+extern PRLogModuleInfo *gOpenSLESProviderLog;
+
+namespace mozilla {
+
+class OpenSLESProvider {
+public:
+    static SLresult Get(SLObjectItf * aObjectm,
+                   SLuint32 aOptionCount,
+                   const SLEngineOption *aOptions);
+    static void Destroy(SLObjectItf * aObjectm);
+    static SLresult Realize(SLObjectItf aObjectm);
+private:
+    OpenSLESProvider();
+    ~OpenSLESProvider();
+    OpenSLESProvider(OpenSLESProvider const&); // NO IMPLEMENTATION
+    void operator=(OpenSLESProvider const&);   // NO IMPLEMENTATION
+    static OpenSLESProvider& getInstance();
+    SLresult GetEngine(SLObjectItf * aObjectm,
+                       SLuint32 aOptionCount,
+                       const SLEngineOption *aOptions);
+    SLresult ConstructEngine(SLObjectItf * aObjectm,
+                             SLuint32 aOptionCount,
+                             const SLEngineOption *aOptions);
+    SLresult RealizeEngine(SLObjectItf aObjectm);
+    void DestroyEngine(SLObjectItf * aObjectm);
+
+    // Protect all our internal variables
+    mozilla::Mutex mLock;
+    SLObjectItf mSLEngine;
+    int mSLEngineUsers;
+    bool mIsRealized;
+    void *mOpenSLESLib;
+};
+
+} //namespace
+#endif // cplusplus
+
+#endif /* _OPENSLESPROVIDER_H_ */
new file mode 100644
--- /dev/null
+++ b/content/media/systemservices/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['MOZ_WEBRTC']:
+    EXPORTS += ['LoadManager.h',
+                'LoadManagerFactory.h',
+                'LoadMonitor.h',
+    ]
+    UNIFIED_SOURCES += ['LoadManager.cpp',
+                        'LoadManagerFactory.cpp',
+                        'LoadMonitor.cpp',
+    ]
+    LOCAL_INCLUDES += [
+        '/media/webrtc/trunk',
+    ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'):
+    EXPORTS += [
+        'OpenSLESProvider.h'
+    ]
+    UNIFIED_SOURCES += [
+        'OpenSLESProvider.cpp',
+    ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+  UNIFIED_SOURCES += ['OSXRunLoopSingleton.cpp']
+  EXPORTS += ['OSXRunLoopSingleton.h']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+    CXXFLAGS += [
+        '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
+            'frameworks/wilhelm/include',
+            'system/media/wilhelm/include',
+        ]
+    ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -3,17 +3,17 @@
 // "bogus/duh" in each list.
 
 // These are small test files, good for just seeing if something loads. We
 // really only need one test file per backend here.
 var gSmallTests = [
   { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
   { name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
   { name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 },
-  { name:"small-shot-mp3.mp4", type:"audio/mp4; codecs=mp3", duration:0.34 },
+  // { name:"small-shot-mp3.mp4", type:"audio/mp4; codecs=mp3", duration:0.34 },
   { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
   { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266 },
   { name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 },
   { name:"vp9.webm", type:"video/webm", width:320, height:240, duration:4 },
   { name:"detodos.opus", type:"audio/ogg; codecs=opus", duration:2.9135 },
   { name:"gizmo.mp4", type:"video/mp4", duration:5.56 },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -91,19 +91,20 @@ AudioContext::AudioContext(nsPIDOMWindow
   , mIsShutDown(false)
 {
   aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
-  // We skip calling SetIsOnlyNodeForContext during mDestination's constructor,
-  // because we can only call SetIsOnlyNodeForContext after mDestination has
-  // been set up.
+  // We skip calling SetIsOnlyNodeForContext and the creation of the
+  // audioChannelAgent during mDestination's constructor, because we can only
+  // call them after mDestination has been set up.
+  mDestination->CreateAudioChannelAgent();
   mDestination->SetIsOnlyNodeForContext(true);
 }
 
 AudioContext::~AudioContext()
 {
   nsPIDOMWindow* window = GetOwner();
   if (window) {
     window->RemoveAudioContext(this);
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -253,18 +253,50 @@ private:
   bool mLastInputMuted;
 };
 
 static bool UseAudioChannelService()
 {
   return Preferences::GetBool("media.useAudioChannelService");
 }
 
+class EventProxyHandler MOZ_FINAL : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit EventProxyHandler(nsIDOMEventListener* aNode)
+  {
+    MOZ_ASSERT(aNode);
+    mWeakNode = do_GetWeakReference(aNode);
+  }
+
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE
+  {
+    nsCOMPtr<nsIDOMEventListener> listener = do_QueryReferent(mWeakNode);
+    if (!listener) {
+      return NS_OK;
+    }
+
+    auto node = static_cast<AudioDestinationNode*>(listener.get());
+    return node->HandleEvent(aEvent);
+  }
+
+private:
+  ~EventProxyHandler()
+  { }
+
+  nsWeakPtr mWeakNode;
+};
+
+NS_IMPL_ISUPPORTS(EventProxyHandler, nsIDOMEventListener)
+
 NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
-                                   mAudioChannelAgent)
+                                   mAudioChannelAgent, mEventProxyHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(AudioDestinationNode, AudioNode)
@@ -300,27 +332,16 @@ AudioDestinationNode::AudioDestinationNo
   mStream->SetAudioChannelType(aChannel);
   mStream->AddMainThreadListener(this);
   mStream->AddAudioOutput(&gWebAudioOutputKey);
 
   if (aChannel != AudioChannel::Normal) {
     ErrorResult rv;
     SetMozAudioChannelType(aChannel, rv);
   }
-
-  if (!aIsOffline && UseAudioChannelService()) {
-    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
-    if (target) {
-      target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
-                                     /* useCapture = */ true,
-                                     /* wantsUntrusted = */ false);
-    }
-
-    CreateAudioChannelAgent();
-  }
 }
 
 AudioDestinationNode::~AudioDestinationNode()
 {
 }
 
 size_t
 AudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
@@ -342,17 +363,18 @@ AudioDestinationNode::DestroyMediaStream
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
 
     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
     NS_ENSURE_TRUE_VOID(target);
 
-    target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
+    target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                      mEventProxyHelper,
                                       /* useCapture = */ true);
   }
 
   if (!mStream)
     return;
 
   mStream->RemoveMainThreadListener(this);
   MediaStreamGraph* graph = mStream->Graph();
@@ -560,16 +582,34 @@ AudioDestinationNode::CheckAudioChannelP
     &perm);
 
   return perm == nsIPermissionManager::ALLOW_ACTION;
 }
 
 void
 AudioDestinationNode::CreateAudioChannelAgent()
 {
+  if (mIsOffline || !UseAudioChannelService()) {
+    return;
+  }
+
+  if (!mEventProxyHelper) {
+    nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
+    if (target) {
+      // We use a proxy because otherwise the event listerner would hold a
+      // reference of the destination node, and by extension, everything
+      // connected to it.
+      mEventProxyHelper = new EventProxyHandler(this);
+      target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                     mEventProxyHelper,
+                                     /* useCapture = */ true,
+                                     /* wantsUntrusted = */ false);
+    }
+  }
+
   if (mAudioChannelAgent) {
     mAudioChannelAgent->StopPlaying();
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
   mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -12,16 +12,17 @@
 #include "nsIDOMEventListener.h"
 #include "nsIAudioChannelAgent.h"
 #include "AudioChannelCommon.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
+class EventProxyHandler;
 
 class AudioDestinationNode : public AudioNode
                            , public nsIDOMEventListener
                            , public nsIAudioChannelAgentCallback
                            , public MainThreadMediaStreamListener
 {
 public:
   // This node type knows what MediaStreamGraph to use based on
@@ -52,59 +53,62 @@ public:
 
   void Mute();
   void Unmute();
 
   void StartRendering();
 
   void OfflineShutdown();
 
-  // nsIDOMEventListener
+  // nsIDOMEventListener - by proxy
   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
 
   AudioChannel MozAudioChannelType() const;
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
   void FireOfflineCompletionEvent();
 
   // An amount that should be added to the MediaStream's current time to
   // get the AudioContext.currentTime.
   double ExtraCurrentTime();
 
   // When aIsOnlyNode is true, this is the only node for the AudioContext.
   void SetIsOnlyNodeForContext(bool aIsOnlyNode);
 
+  void CreateAudioChannelAgent();
+
   virtual const char* NodeType() const
   {
     return "AudioDestinationNode";
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
 
   void InputMuted(bool aInputMuted);
 
 protected:
   virtual ~AudioDestinationNode();
 
 private:
   bool CheckAudioChannelPermissions(AudioChannel aValue);
-  void CreateAudioChannelAgent();
 
   void SetCanPlay(bool aCanPlay);
 
   void NotifyStableState();
   void ScheduleStableStateNotification();
 
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
   uint32_t mFramesToProduce;
 
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 
+  nsRefPtr<EventProxyHandler> mEventProxyHelper;
+
   // Audio Channel Type.
   AudioChannel mAudioChannel;
   bool mIsOffline;
   bool mHasFinished;
   bool mAudioChannelAgentPlaying;
 
   TimeStamp mStartedBlockingDueToBeingOnlyNode;
   double mExtraCurrentTime;
--- a/content/media/webrtc/moz.build
+++ b/content/media/webrtc/moz.build
@@ -9,24 +9,18 @@ XPIDL_MODULE = 'content_webrtc'
 EXPORTS += [
     'MediaEngine.h',
     'MediaEngineDefault.h',
     'MediaTrackConstraints.h',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     EXPORTS += ['AudioOutputObserver.h',
-                'LoadManager.h',
-                'LoadManagerFactory.h',
-                'LoadMonitor.h',
                 'MediaEngineWebRTC.h']
     UNIFIED_SOURCES += [
-        'LoadManager.cpp',
-        'LoadManagerFactory.cpp',
-        'LoadMonitor.cpp',
         'MediaEngineTabVideoSource.cpp',
         'MediaEngineWebRTCAudio.cpp',
         'MediaEngineWebRTCVideo.cpp',
     ]
     # MediaEngineWebRTC.cpp needs to be built separately.
     SOURCES += [
         'MediaEngineWebRTC.cpp',
     ]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7410,17 +7410,18 @@ nsDocShell::CreateAboutBlankContentViewe
   mFiredUnloadEvent = false;
 
   nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
       nsContentUtils::FindInternalContentViewer("text/html");
 
   if (docFactory) {
     nsCOMPtr<nsIPrincipal> principal;
     if (mSandboxFlags & SANDBOXED_ORIGIN) {
-      principal = do_CreateInstance("@mozilla.org/nullprincipal;1");
+      principal = nsNullPrincipal::CreateWithInheritedAttributes(aPrincipal);
+      NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
     } else {
       principal = aPrincipal;
     }
     // generate (about:blank) document to load
     docFactory->CreateBlankDocument(mLoadGroup, principal,
                                     getter_AddRefs(blankDoc));
     if (blankDoc) {
       // Hack: set the base URI manually, since this document never
@@ -11141,20 +11142,18 @@ nsDocShell::AddToSessionHistory(nsIURI *
         }
         aChannel->GetOwner(getter_AddRefs(owner));
         if (!owner) {
             nsCOMPtr<nsILoadInfo> loadInfo;
             aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
             if (loadInfo) {
                 // For now keep storing just the principal in the SHEntry.
                 if (loadInfo->GetLoadingSandboxed()) {
-                    owner = do_CreateInstance(NS_NULLPRINCIPAL_CONTRACTID, &rv);
-                    if (NS_WARN_IF(NS_FAILED(rv))) {
-                        return rv;
-                    }
+                    owner = nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
+                    NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
                 } else if (loadInfo->GetForceInheritPrincipal()) {
                     owner = loadInfo->LoadingPrincipal();
                 }
             }
         }
     }
 
     //Title is set in nsDocShell::SetTitle()
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -354,17 +354,17 @@ nsDOMWindowUtils::SetDisplayPortForEleme
                      nsPresContext::CSSPixelsToAppUnits(aYPx),
                      nsPresContext::CSSPixelsToAppUnits(aWidthPx),
                      nsPresContext::CSSPixelsToAppUnits(aHeightPx));
 
   content->SetProperty(nsGkAtoms::DisplayPort,
                        new DisplayPortPropertyData(displayport, aPriority),
                        nsINode::DeleteProperty<DisplayPortPropertyData>);
 
-  if (gfxPrefs::AsyncPanZoomEnabled()) {
+  if (nsLayoutUtils::UsesAsyncScrolling()) {
     nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
     if (rootScrollFrame && content == rootScrollFrame->GetContent()) {
       // We are setting a root displayport for a document.
       // The pres shell needs a special flag set.
       presShell->SetIgnoreViewportScrolling(true);
     }
   }
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -918,17 +918,17 @@ nsFocusManager::WindowHidden(nsIDOMWindo
 
   nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
   nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
 
   if (oldFocusedContent && oldFocusedContent->IsInDoc()) {
     NotifyFocusStateChange(oldFocusedContent,
                            mFocusedWindow->ShouldShowFocusRing(),
                            false);
-    window->UpdateCommands(NS_LITERAL_STRING("focus"));
+    window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
     if (presShell) {
       SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
                            oldFocusedContent->GetCurrentDoc(),
                            oldFocusedContent, 1, false);
     }
   }
 
@@ -1261,17 +1261,17 @@ nsFocusManager::SetFocusInner(nsIContent
       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
       if (presShell)
         ScrollIntoView(presShell, contentToFocus, aFlags);
     }
 
     // update the commands even when inactive so that the attributes for that
     // window are up to date.
     if (allowFrameSwitch)
-      newWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
+      newWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
     if (aFlags & FLAG_RAISE)
       RaiseWindow(newRootWindow);
   }
 }
 
 bool
 nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
@@ -1581,17 +1581,17 @@ nsFocusManager::Blur(nsPIDOMWindow* aWin
   }
 
   bool result = true;
   if (sendBlurEvent) {
     // if there is an active window, update commands. If there isn't an active
     // window, then this was a blur caused by the active window being lowered,
     // so there is no need to update the commands
     if (mActiveWindow)
-      window->UpdateCommands(NS_LITERAL_STRING("focus"));
+      window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
     SendFocusOrBlurEvent(NS_BLUR_CONTENT, presShell,
                          content->GetCurrentDoc(), content, 1, false);
   }
 
   // if we are leaving the document or the window was lowered, make the caret
   // invisible.
   if (aIsLeavingDocument || !mActiveWindow)
@@ -1802,27 +1802,27 @@ nsFocusManager::Focus(nsPIDOMWindow* aWi
 
       IMEStateManager::OnChangeFocus(presContext, aContent,
                                      GetFocusMoveActionCause(aFlags));
 
       // as long as this focus wasn't because a window was raised, update the
       // commands
       // XXXndeakin P2 someone could adjust the focus during the update
       if (!aWindowRaised)
-        aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
+        aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
       SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell,
                            aContent->GetCurrentDoc(),
                            aContent, aFlags & FOCUSMETHOD_MASK,
                            aWindowRaised, isRefocus);
     } else {
       IMEStateManager::OnChangeFocus(presContext, nullptr,
                                      GetFocusMoveActionCause(aFlags));
       if (!aWindowRaised) {
-        aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
+        aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
       }
     }
   }
   else {
     // If the window focus event (fired above when aIsNewDocument) caused
     // the plugin not to be focusable, update the system focus by focusing
     // the root widget.
     if (aAdjustWidgets && objectFrameWidget &&
@@ -1837,17 +1837,17 @@ nsFocusManager::Focus(nsPIDOMWindow* aWi
       }
     }
 
     nsPresContext* presContext = presShell->GetPresContext();
     IMEStateManager::OnChangeFocus(presContext, nullptr,
                                    GetFocusMoveActionCause(aFlags));
 
     if (!aWindowRaised)
-      aWindow->UpdateCommands(NS_LITERAL_STRING("focus"));
+      aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
   }
 
   // update the caret visibility and position to match the newly focused
   // element. However, don't update the position if this was a focus due to a
   // mouse click as the selection code would already have moved the caret as
   // needed. If this is a different document than was focused before, also
   // update the caret's visibility. If this is the same document, the caret
   // visibility should be the same as before so there is no need to update it.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -194,16 +194,18 @@
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "nsRefreshDriver.h"
 
+#include "mozilla/dom/SelectionChangeEvent.h"
+
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 #include "nsHTMLDocument.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
@@ -268,16 +270,17 @@ bool nsGlobalWindow::sIdleObserversAPIFu
 
 static nsIEntropyCollector *gEntropyCollector          = nullptr;
 static int32_t              gRefCnt                    = 0;
 static int32_t              gOpenPopupSpamCount        = 0;
 static PopupControlState    gPopupControlState         = openAbused;
 static int32_t              gRunningTimeoutDepth       = 0;
 static bool                 gMouseDown                 = false;
 static bool                 gDragServiceDisabled       = false;
+static bool                 gSelectionCaretPrefEnabled = false;
 static FILE                *gDumpFile                  = nullptr;
 static uint64_t             gNextWindowID              = 0;
 static uint32_t             gSerialCounter             = 0;
 static uint32_t             gTimeoutsRecentlySet       = 0;
 static TimeStamp            gLastRecordedRecentTimeouts;
 #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
 
 #ifdef DEBUG_jst
@@ -1170,16 +1173,19 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
                                 "dom.min_timeout_value",
                                 DEFAULT_MIN_TIMEOUT_VALUE);
     Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
                                 "dom.min_background_timeout_value",
                                 DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
     Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled, 
                                  "dom.idle-observers-api.fuzz_time.disabled",
                                  false);
+    Preferences::AddBoolVarCache(&gSelectionCaretPrefEnabled,
+                                 "selectioncaret.enabled",
+                                 false);
   }
 
   if (gDumpFile == nullptr) {
     const nsAdoptingCString& fname =
       Preferences::GetCString("browser.dom.window.dump.file");
     if (!fname.IsEmpty()) {
       // if this fails to open, Dump() knows to just go to stdout
       // on null.
@@ -9243,35 +9249,60 @@ public:
     return mDispatcher->UpdateCommands(mAction);
   }
 
   nsCOMPtr<nsIDOMXULCommandDispatcher> mDispatcher;
   nsString                             mAction;
 };
 
 NS_IMETHODIMP
-nsGlobalWindow::UpdateCommands(const nsAString& anAction)
+nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason)
 {
   nsPIDOMWindow *rootWindow = nsGlobalWindow::GetPrivateRoot();
   if (!rootWindow)
     return NS_OK;
 
   nsCOMPtr<nsIDOMXULDocument> xulDoc =
     do_QueryInterface(rootWindow->GetExtantDoc());
   // See if we contain a XUL document.
-  if (xulDoc) {
+  // selectionchange action is only used for mozbrowser, not for XUL. So we bypass
+  // XUL command dispatch if anAction is "selectionchange".
+  if (xulDoc && !anAction.EqualsLiteral("selectionchange")) {
     // Retrieve the command dispatcher and call updateCommands on it.
     nsCOMPtr<nsIDOMXULCommandDispatcher> xulCommandDispatcher;
     xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher));
     if (xulCommandDispatcher) {
       nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher,
                                                             anAction));
     }
   }
 
+  if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
+    SelectionChangeEventInit init;
+    init.mBubbles = true;
+    if (aSel) {
+      nsCOMPtr<nsIDOMRange> range;
+      nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
+      if (NS_SUCCEEDED(rv) && range) {
+        nsRefPtr<nsRange> nsrange = static_cast<nsRange*>(range.get());
+        init.mBoundingClientRect = nsrange->GetBoundingClientRect(true, false);
+        range->ToString(init.mSelectedText);
+        init.mReason = aReason;
+      }
+
+      nsRefPtr<SelectionChangeEvent> event =
+        SelectionChangeEvent::Constructor(mDoc, NS_LITERAL_STRING("mozselectionchange"), init);
+
+      event->SetTrusted(true);
+      event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
+      bool ret;
+      mDoc->DispatchEvent(event, &ret);
+    }
+  }
+
   return NS_OK;
 }
 
 Selection*
 nsGlobalWindow::GetSelection(ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetSelection, (aError), aError, nullptr);
 
new file mode 100644
--- /dev/null
+++ b/dom/base/test/iframe_main_bug1022229.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+  // Uncomment this definition of SimpleTest (and comment out the one below) to
+  // debug in mozBrowser mode.
+  /*
+  var SimpleTest = { ok: function(c, m) { dump(m + ": " + c + "\n"); },
+                     info: function(m) { dump(m + "\n"); },
+                     finish: function() { dump("Test done\n");} };
+  */
+  var SimpleTest = parent.SimpleTest;
+
+  var ok = SimpleTest.ok;
+  var info = SimpleTest.info;
+  var finish = SimpleTest.finish.bind(SimpleTest);
+
+  var gotTargetedMessage = false;
+  window.onmessage = function(evt) {
+    var message = evt.data;
+    info("Received message: " + message);
+    switch (message) {
+      case 'targeted':
+        gotTargetedMessage = true;
+        break;
+      case 'broadcast':
+        ok(gotTargetedMessage, "Should have received targeted message");
+        finish();
+        break;
+      default:
+        ok(false, "Unexpected message: " + message);
+        break;
+    }
+  }
+</script>
+</head>
+<body>
+<iframe src="iframe_sandbox_bug1022229.html" sandbox="allow-scripts"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/iframe_sandbox_bug1022229.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+  // First send an origin-restricted message, and then send a non-restricted
+  // message to end the test promptly even in a failure mode.
+  parent.postMessage('targeted', 'http://mochi.test:8888');
+  setTimeout(function() { parent.postMessage('broadcast', '*'); }, 0);
+</script>
+</head>
+<body>
+</body>
+</html>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -1,12 +1,14 @@
 [DEFAULT]
 support-files =
   audio.ogg
   iframe_bug976673.html
+  iframe_main_bug1022229.html
+  iframe_sandbox_bug1022229.html
   iframe_messageChannel_cloning.html
   iframe_messageChannel_chrome.html
   iframe_messageChannel_pingpong.html
   iframe_messageChannel_post.html
   file_empty.html
   iframe_postMessage_solidus.html
   file_setname.html
 
@@ -16,16 +18,17 @@ support-files =
 skip-if = buildapp == 'mulet'
 [test_bug793311.html]
 [test_bug913761.html]
 [test_bug976673.html]
 [test_bug978522.html]
 [test_bug979109.html]
 [test_bug989665.html]
 [test_bug999456.html]
+[test_bug1022229.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_consoleEmptyStack.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_dialogArguments.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s
 [test_document.all_unqualified.html]
 [test_domcursor.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1022229.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1022229
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1022229</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for postMessage between sandboxed iframe and non-sandboxed window.
+      This test is particularly interesting on b2g where we're in a mozBrowser.
+
+      We set the test up with an extra iframe so that we can easily run it in
+      an artificial mozbrowser for desktop builds.
+   **/
+  SimpleTest.waitForExplicitFinish();
+  function go() {
+    var ifr = document.createElement('iframe');
+
+    /* Uncomment this chunk to run in a mozBrowser. Make sure to uncomment the
+       chunk in iframe_main as well. */
+    /*
+    SpecialPowers.Services.prefs.setBoolPref("dom.mozBrowserFramesEnabled", true);
+    SpecialPowers.Services.prefs.setBoolPref("dom.ipc.browser_frames.oop_by_default", false);
+    SpecialPowers.addPermission("browser", true, document);
+    SpecialPowers.wrap(ifr).mozbrowser = true;
+    */
+
+    ifr.setAttribute('src', 'iframe_main_bug1022229.html');
+    document.body.appendChild(ifr);
+  }
+
+  </script>
+</head>
+<body onload="go()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1022229">Mozilla Bug 1022229</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2499,25 +2499,17 @@ ConvertExceptionToPromise(JSContext* cx,
 
   return WrapNewBindingObject(cx, promise, rval);
 }
 
 /* static */
 void
 CreateGlobalOptions<nsGlobalWindow>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
 {
-  mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
-
-  // We might be called from a GC during the creation of a global, before we've
-  // been able to set up the compartment private or the XPCWrappedNativeScope,
-  // so we need to null-check those.
-  xpc::CompartmentPrivate* compartmentPrivate = xpc::CompartmentPrivate::Get(aObj);
-  if (compartmentPrivate && compartmentPrivate->scope) {
-    compartmentPrivate->scope->TraceSelf(aTrc);
-  }
+  xpc::TraceXPCGlobal(aTrc, aObj);
 }
 
 /* static */
 bool
 CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
                                                       JS::Handle<JSObject*> aGlobal)
 {
   // Invoking the XPCWrappedNativeScope constructor automatically hooks it
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -103,16 +103,23 @@ function getErrorClass(errorCode) {
 const OBSERVED_EVENTS = [
   'fullscreen-origin-change',
   'ask-parent-to-exit-fullscreen',
   'ask-parent-to-rollback-fullscreen',
   'xpcom-shutdown',
   'activity-done'
 ];
 
+const COMMAND_MAP = {
+  'cut': 'cmd_cut',
+  'copy': 'cmd_copy',
+  'paste': 'cmd_paste',
+  'selectall': 'cmd_selectAll'
+};
+
 /**
  * The BrowserElementChild implements one half of <iframe mozbrowser>.
  * (The other half is, unsurprisingly, BrowserElementParent.)
  *
  * This script is injected into an <iframe mozbrowser> via
  * nsIMessageManager::LoadFrameScript().
  *
  * Our job here is to listen for events within this frame and bubble them up to
@@ -195,16 +202,20 @@ BrowserElementChild.prototype = {
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMMetaRemoved',
                      this._metaChangedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
+    addEventListener('mozselectionchange',
+                     this._selectionChangeHandler.bind(this),
+                     /* useCapture = */ false,
+                     /* wantsUntrusted = */ false);
 
     // This listens to unload events from our message manager, but /not/ from
     // the |content| window.  That's because the window's unload event doesn't
     // bubble, and we're not using a capturing listener.  If we'd used
     // useCapture == true, we /would/ hear unload events from the window, which
     // is not what we want!
     addEventListener('unload',
                      this._unloadHandler.bind(this),
@@ -233,17 +244,18 @@ BrowserElementChild.prototype = {
       "stop": this._recvStop,
       "zoom": this._recvZoom,
       "unblock-modal-prompt": this._recvStopWaiting,
       "fire-ctx-callback": this._recvFireCtxCallback,
       "owner-visibility-change": this._recvOwnerVisibilityChange,
       "exit-fullscreen": this._recvExitFullscreen.bind(this),
       "activate-next-paint-listener": this._activateNextPaintListener.bind(this),
       "set-input-method-active": this._recvSetInputMethodActive.bind(this),
-      "deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this)
+      "deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this),
+      "do-command": this._recvDoCommand
     }
 
     addMessageListener("browser-element-api:call", function(aMessage) {
       if (aMessage.data.msg_name in mmCalls) {
         return mmCalls[aMessage.data.msg_name].apply(self, arguments);
       }
     });
 
@@ -347,16 +359,25 @@ BrowserElementChild.prototype = {
 
     if (args.promptType == 'prompt' ||
         args.promptType == 'confirm' ||
         args.promptType == 'custom-prompt') {
       return returnValue;
     }
   },
 
+  _isCommandEnabled: function(cmd) {
+    let command = COMMAND_MAP[cmd];
+    if (!command) {
+      return false;
+    }
+
+    return docShell.isCommandEnabled(command);
+  },
+
   /**
    * Spin in a nested event loop until we receive a unblock-modal-prompt message for
    * this window.
    */
   _waitForResult: function(win) {
     debug("_waitForResult(" + win + ")");
     let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils);
@@ -466,23 +487,26 @@ BrowserElementChild.prototype = {
     if (win == content) {
       sendAsyncMsg('titlechange', { _payload_: e.target.title });
     }
     else {
       debug("Not top level!");
     }
   },
 
+  _maybeCopyAttribute: function(src, target, attribute) {
+    if (src.getAttribute(attribute)) {
+      target[attribute] = src.getAttribute(attribute);
+    }
+  },
+
   _iconChangedHandler: function(e) {
     debug('Got iconchanged: (' + e.target.href + ')');
     let icon = { href: e.target.href };
-    if (e.target.getAttribute('sizes')) {
-      icon.sizes = e.target.getAttribute('sizes');
-    }
-
+    this._maybeCopyAttribute(e.target, icon, 'sizes');
     sendAsyncMsg('iconchange', icon);
   },
 
   _openSearchHandler: function(e) {
     debug('Got opensearch: (' + e.target.href + ')');
 
     if (e.target.type !== "application/opensearchdescription+xml") {
       return;
@@ -506,17 +530,18 @@ BrowserElementChild.prototype = {
     // Ignore links which don't come from the top-level
     // <iframe mozbrowser> window.
     if (win != content) {
       debug('Not top level!');
       return;
     }
 
     let handlers = {
-      'icon': this._iconChangedHandler,
+      'icon': this._iconChangedHandler.bind(this),
+      'apple-touch-icon': this._iconChangedHandler.bind(this),
       'search': this._openSearchHandler,
       'manifest': this._manifestChangedHandler
     };
 
     debug('Got linkAdded: (' + e.target.href + ') ' + e.target.rel);
     e.target.rel.split(' ').forEach(function(x) {
       let token = x.toLowerCase();
       if (handlers[token]) {
@@ -585,16 +610,63 @@ BrowserElementChild.prototype = {
 
     if (lang) {
       meta.lang = lang;
     }
 
     sendAsyncMsg('metachange', meta);
   },
 
+  _selectionChangeHandler: function(e) {
+    let isMouseUp = e.reason & Ci.nsISelectionListener.MOUSEUP_REASON;
+    let isSelectAll = e.reason & Ci.nsISelectionListener.SELECTALL_REASON;
+    // When selectall happened, gecko will first collapse the range then
+    // select all. So we will receive two selection change events with
+    // SELECTALL_REASON. We filter first event by check the length of
+    // selectedText.
+    if (!(isMouseUp || (isSelectAll && e.selectedText.length > 0))) {
+      return;
+    }
+
+    e.stopPropagation();
+    let boundingClientRect = e.boundingClientRect;
+    let zoomFactor = content.screen.width / content.innerWidth;
+
+    let detail = {
+      rect: {
+        width: boundingClientRect.width,
+        height: boundingClientRect.height,
+        top: boundingClientRect.top,
+        bottom: boundingClientRect.bottom,
+        left: boundingClientRect.left,
+        right: boundingClientRect.right,
+      },
+      commands: {
+        canSelectAll: this._isCommandEnabled("selectall"),
+        canCut: this._isCommandEnabled("cut"),
+        canCopy: this._isCommandEnabled("copy"),
+        canPaste: this._isCommandEnabled("paste"),
+      },
+      zoomFactor: zoomFactor,
+    };
+
+    // Get correct geometry information if we have nested <iframe mozbrowser>
+    let currentWindow = e.target.defaultView;
+    while (currentWindow.realFrameElement) {
+      let currentRect = currentWindow.realFrameElement.getBoundingClientRect();
+      detail.rect.top += currentRect.top;
+      detail.rect.bottom += currentRect.top;
+      detail.rect.left += currentRect.left;
+      detail.rect.right += currentRect.left;
+      currentWindow = currentWindow.realFrameElement.ownerDocument.defaultView;
+    }
+
+    sendAsyncMsg("selectionchange", detail);
+  },
+
   _themeColorChangedHandler: function(eventType, target) {
     let meta = {
       name: 'theme-color',
       content: target.content,
       type: eventType.replace('DOMMeta', '').toLowerCase()
     };
     sendAsyncMsg('metachange', meta);
   },
@@ -901,33 +973,27 @@ BrowserElementChild.prototype = {
       this._ctxHandlers[data.json.menuitem].click();
       this._ctxHandlers = {};
     } else {
       debug("Ignored invalid contextmenu invocation");
     }
   },
 
   _buildMenuObj: function(menu, idPrefix) {
-    function maybeCopyAttribute(src, target, attribute) {
-      if (src.getAttribute(attribute)) {
-        target[attribute] = src.getAttribute(attribute);
-      }
-    }
-
     var menuObj = {type: 'menu', items: []};
-    maybeCopyAttribute(menu, menuObj, 'label');
+    this._maybeCopyAttribute(menu, menuObj, 'label');
 
     for (var i = 0, child; child = menu.children[i++];) {
       if (child.nodeName === 'MENU') {
         menuObj.items.push(this._buildMenuObj(child, idPrefix + i + '_'));
       } else if (child.nodeName === 'MENUITEM') {
         var id = this._ctxCounter + '_' + idPrefix + i;
         var menuitem = {id: id, type: 'menuitem'};
-        maybeCopyAttribute(child, menuitem, 'label');
-        maybeCopyAttribute(child, menuitem, 'icon');
+        this._maybeCopyAttribute(child, menuitem, 'label');
+        this._maybeCopyAttribute(child, menuitem, 'icon');
         this._ctxHandlers[id] = child;
         menuObj.items.push(menuitem);
       }
     }
     return menuObj;
   },
 
   _recvSetVisible: function(data) {
@@ -1031,16 +1097,22 @@ BrowserElementChild.prototype = {
     let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
     webNav.stop(webNav.STOP_NETWORK);
   },
 
   _recvZoom: function(data) {
     docShell.contentViewer.fullZoom = data.json.zoom;
   },
 
+  _recvDoCommand: function(data) {
+    if (this._isCommandEnabled(data.json.command)) {
+      docShell.doCommand(COMMAND_MAP[data.json.command]);
+    }
+  },
+
   _recvSetInputMethodActive: function(data) {
     let msgData = { id: data.json.id };
     if (!this._isContentWindowCreated) {
       if (data.json.args.isActive) {
         // To activate the input method, we should wait before the content
         // window is ready.
         this._pendingSetInputMethodActive.push(data);
         return;
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -155,16 +155,21 @@ function BrowserElementParent(frameLoade
   if (!this._window._browserElementParents) {
     this._window._browserElementParents = new WeakMap();
     this._window.addEventListener('visibilitychange',
                                   visibilityChangeHandler,
                                   /* useCapture = */ false,
                                   /* wantsUntrusted = */ false);
   }
 
+  this._frameElement.addEventListener('mozdocommand',
+                                      this._doCommandHandler.bind(this),
+                                      /* useCapture = */ false,
+                                      /* wantsUntrusted = */ false);
+
   this._window._browserElementParents.set(this, null);
 
   // Insert ourself into the prompt service.
   BrowserElementPromptService.mapFrameToBrowserElementParent(this._frameElement, this);
   if (!isPendingFrame) {
     this._setupMessageListener();
     this._registerAppManifest();
   } else {
@@ -243,17 +248,18 @@ BrowserElementParent.prototype = {
       "got-screenshot": this._gotDOMRequestResult,
       "got-can-go-back": this._gotDOMRequestResult,
       "got-can-go-forward": this._gotDOMRequestResult,
       "fullscreen-origin-change": this._remoteFullscreenOriginChange,
       "rollback-fullscreen": this._remoteFrameFullscreenReverted,
       "exit-fullscreen": this._exitFullscreen,
       "got-visible": this._gotDOMRequestResult,
       "visibilitychange": this._childVisibilityChange,
-      "got-set-input-method-active": this._gotDOMRequestResult
+      "got-set-input-method-active": this._gotDOMRequestResult,
+      "selectionchange": this._handleSelectionChange
     };
 
     this._mm.addMessageListener('browser-element-api:call', function(aMsg) {
       if (self._isAlive() && (aMsg.data.msg_name in mmCalls)) {
         return mmCalls[aMsg.data.msg_name].apply(self, arguments);
       }
     });
   },
@@ -445,16 +451,27 @@ BrowserElementParent.prototype = {
 
     if (!evt.defaultPrevented) {
       // Unblock the inner frame immediately.  Otherwise we'll unblock upon
       // evt.detail.unblock().
       sendUnblockMsg();
     }
   },
 
+  _handleSelectionChange: function(data) {
+    let evt = this._createEvent('selectionchange', data.json,
+                                /* cancelable = */ false);
+    this._frameElement.dispatchEvent(evt);
+  },
+
+  _doCommandHandler: function(e) {
+    e.stopPropagation();
+    this._sendAsyncMsg('do-command', { command: e.detail.cmd });
+  },
+
   _createEvent: function(evtName, detail, cancelable) {
     // This will have to change if we ever want to send a CustomEvent with null
     // detail.  For now, it's OK.
     if (detail !== undefined && detail !== null) {
       detail = Cu.cloneInto(detail, this._window);
       return new this._window.CustomEvent('mozbrowser' + evtName,
                                           { bubbles: true,
                                             cancelable: cancelable,
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -55,16 +55,20 @@ const browserElementTestHelpers = {
       ['dom.ipc.processPriorityManager.backgroundLRUPoolLevels', 2]
     );
   },
 
   setEnabledPref: function(value) {
     this._setPref('dom.mozBrowserFramesEnabled', value);
   },
 
+  setSelectionChangeEnabledPref: function(value) {
+    this._setPref('selectioncaret.enabled', value);
+  },
+
   getOOPByDefaultPref: function() {
     return this._getBoolPref("dom.ipc.browser_frames.oop_by_default");
   },
 
   addPermission: function() {
     SpecialPowers.addPermission("browser", true, document);
     this.tempPermissions.push(location.href)
   },
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_CopyPaste.js
@@ -0,0 +1,296 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that "cut, copy, paste, selectall" and selectionchange event works from inside an <iframe mozbrowser>.
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.setSelectionChangeEnabledPref(true);
+browserElementTestHelpers.addPermission();
+var gTextarea = null;
+var mm;
+var iframe;
+var state = 0;
+var stateMeaning;
+var defaultData;
+var pasteData;
+var focusScript;
+
+function copyToClipboard(str) {
+  gTextarea.value = str;
+  SpecialPowers.wrap(gTextarea).editor.selectAll();
+  SpecialPowers.wrap(gTextarea).editor.copy();
+}
+
+function getScriptForGetContent() {
+  var script = 'data:,\
+    var elt = content.document.getElementById("text"); \
+    var txt = ""; \
+    if (elt) { \
+      if (elt.tagName === "DIV" || elt.tagName === "BODY") { \
+        txt = elt.textContent; \
+      } else { \
+        txt = elt.value; \
+      } \
+    } \
+    sendAsyncMessage("content-text", txt);';
+  return script;
+}
+
+function getScriptForSetFocus() {
+  var script = 'data:,' + focusScript + 'sendAsyncMessage("content-focus")';
+  return script;
+}
+
+function runTest() {
+  iframe = document.createElement('iframe');
+  SpecialPowers.wrap(iframe).mozbrowser = true;
+  document.body.appendChild(iframe);
+
+  gTextarea = document.createElement('textarea');
+  document.body.appendChild(gTextarea);
+
+  mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+
+  iframe.addEventListener("mozbrowserloadend", function onloadend(e) {
+    iframe.removeEventListener("mozbrowserloadend", onloadend);
+    dispatchTest(e);
+  });
+}
+
+function doCommand(cmd) {
+  let doc = iframe.ownerDocument;
+  let event = doc.createEvent('CustomEvent');
+  event.initCustomEvent('mozdocommand', true, true, { cmd: cmd });
+  SpecialPowers.wrap(iframe).dispatchEvent(event);
+}
+
+function dispatchTest(e) {
+  iframe.addEventListener("mozbrowserloadend", function onloadend2(e) {
+    iframe.removeEventListener("mozbrowserloadend", onloadend2);
+    SimpleTest.executeSoon(function() { testSelectAll(e); });
+  });
+
+  switch (state) {
+    case 0: // test for textarea
+      defaultData = "Test for selection change event";
+      pasteData = "from parent ";
+      iframe.src = "data:text/html,<html><body>" +
+                   "<textarea id='text'>" + defaultData + "</textarea>" +
+                   "</body>" +
+                   "</html>";
+      stateMeaning = " (test: textarea)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();";
+      break;
+    case 1: // test for input text
+      defaultData = "Test for selection change event";
+      pasteData = "from parent ";
+      iframe.src = "data:text/html,<html><body>" +
+                   "<input type='text' id='text' value='" + defaultData + "'>" +
+                   "</body>" +
+                   "</html>";
+      stateMeaning = " (test: <input type=text>)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();";
+      break;
+    case 2: // test for input password
+      defaultData = "Test for selection change event";
+      pasteData = "from parent ";
+      iframe.src = "data:text/html,<html><body>" +
+                   "<input type='password' id='text' value='" + defaultData + "'>" +
+                   "</body>" +
+                   "</html>";
+      stateMeaning = " (test: <input type=password>)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();";
+      break;
+    case 3: // test for input number
+      defaultData = "12345";
+      pasteData = "67890";
+      iframe.src = "data:text/html,<html><body>" +
+                   "<input type='number' id='text' value='" + defaultData + "'>" +
+                   "</body>" +
+                   "</html>";
+      stateMeaning = " (test: <input type=number>)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();elt.select();";
+      break;
+    case 4: // test for div contenteditable
+      defaultData = "Test for selection change event";
+      pasteData = "from parent ";
+      iframe.src = "data:text/html,<html><body>" +
+                   "<div contenteditable='true' id='text'>" + defaultData + "</div>" +
+                   "</body>" +
+                   "</html>";
+      stateMeaning = " (test: content editable div)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();";
+      break;
+    case 5: // test for normal div
+      SimpleTest.finish();
+      return;
+      defaultData = "Test for selection change event";
+      pasteData = "from parent ";
+      iframe.src = "data:text/html,<html><body>" +
+                   "<div id='text'>" + defaultData + "</div>" +
+                   "</body>" +
+                   "</html>";
+      stateMeaning = " (test: normal div)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();";
+      break;
+    case 6: // test for normal div with designMode:on
+      defaultData = "Test for selection change event";
+      pasteData = "from parent ";
+      iframe.src = "data:text/html,<html><body id='text'>" +
+                   defaultData +
+                   "</body>" +
+                   "<script>document.designMode='on';</script>" +
+                   "</html>";
+      stateMeaning = " (test: normal div with designMode:on)";
+      focusScript = "var elt=content.document.getElementById('text');elt.focus();";
+      break;
+    default:
+      SimpleTest.finish();
+      break;
+  }
+}
+
+function testSelectAll(e) {
+  iframe.addEventListener("mozbrowserselectionchange", function selectchangeforselectall(e) {
+    iframe.removeEventListener("mozbrowserselectionchange", selectchangeforselectall, true);
+    ok(true, "got mozbrowserselectionchange event." + stateMeaning);
+    ok(e.detail, "event.detail is not null." + stateMeaning);
+    ok(e.detail.width != 0, "event.detail.width is not zero" + stateMeaning);
+    ok(e.detail.height != 0, "event.detail.height is not zero" + stateMeaning);
+    SimpleTest.executeSoon(function() { testCopy1(e); });
+  }, true);
+
+  mm.addMessageListener('content-focus', function messageforfocus(msg) {
+    mm.removeMessageListener('content-focus', messageforfocus);
+    // test selectall command, after calling this the selectionchange event should be fired.
+    doCommand('selectall');
+  });
+
+  mm.loadFrameScript(getScriptForSetFocus(), false);
+}
+
+function testCopy1(e) {
+  // Right now we're at "selectall" state, so we can test copy commnad by
+  // calling doCommand
+  copyToClipboard("");
+  let setup = function() {
+    doCommand("copy");
+  };
+
+  let nextTest = function(success) {
+    ok(success, "copy command works" + stateMeaning);
+    SimpleTest.executeSoon(function() { testPaste1(e); });
+  };
+
+  let success = function() {
+    nextTest(true);
+  }
+
+  let fail = function() {
+    nextTest(false);
+  }
+
+  let compareData = defaultData;
+  if (state == 2) {
+    // In password case, we just check length of text at clipboard is equal
+    // to length of defaultData
+    compareData = function(clipboardText) {
+      return clipboardText.length == defaultData.length;
+    };
+  }
+
+  SimpleTest.waitForClipboard(compareData, setup, success, fail);
+}
+
+function testPaste1(e) {
+  // Next test paste command, first we copy to global clipboard in parent side.
+  // Then paste it to child side.
+  copyToClipboard(pasteData);
+
+  doCommand("paste");
+  SimpleTest.executeSoon(function() { testPaste2(e); });
+}
+
+function testPaste2(e) {
+  mm.addMessageListener('content-text', function messageforpaste(msg) {
+    mm.removeMessageListener('content-text', messageforpaste);
+    if (state == 5) {
+      // normal div cannot paste, so the content remain unchange
+      ok(SpecialPowers.wrap(msg).json === defaultData, "paste command works" + stateMeaning);
+    } else if (state == 4 && browserElementTestHelpers.getOOPByDefaultPref()) {
+      // Something weird when we doCommand with content editable element in OOP. Mark this case as todo
+      todo(false, "paste command works" + stateMeaning);
+    } else {
+      ok(SpecialPowers.wrap(msg).json === pasteData, "paste command works" + stateMeaning);
+    }
+    SimpleTest.executeSoon(function() { testCut1(e); });
+  });
+
+  mm.loadFrameScript(getScriptForGetContent(), false);
+}
+
+function testCut1(e) {
+  // Clean clipboard first
+  copyToClipboard("");
+  let setup = function() {
+    doCommand("selectall");
+    doCommand("cut");
+  };
+
+  let nextTest = function(success) {
+    if (state == 4 && browserElementTestHelpers.getOOPByDefaultPref()) {
+      // Something weird when we doCommand with content editable element in OOP.
+      todo(false, "cut function works" + stateMeaning);
+    } else {
+      ok(success, "cut function works" + stateMeaning);
+    }
+    SimpleTest.executeSoon(function() { testCut2(e); });
+  };
+
+  let success = function() {
+    nextTest(true);
+  }
+
+  let fail = function() {
+    nextTest(false);
+  }
+
+  let compareData = pasteData;
+  if (state == 2) {
+    // In password case, we just check length of text at clipboard is equal
+    // to length of pasteData
+    compareData = function(clipboardText) {
+      return clipboardText.length == pasteData.length;
+    };
+  } else if (state == 4 && browserElementTestHelpers.getOOPByDefaultPref()) {
+    // Something weird when we doCommand with content editable element in OOP.
+    // Always true in this case
+    compareData = function() { return true; }
+  }
+
+  SimpleTest.waitForClipboard(compareData, setup, success, fail);
+}
+
+function testCut2(e) {
+  mm.addMessageListener('content-text', function messageforcut(msg) {
+    mm.removeMessageListener('content-text', messageforcut);
+    // normal div cannot cut
+    if (state == 5) {
+      ok(SpecialPowers.wrap(msg).json !== "", "cut command works" + stateMeaning);
+    } else if (state == 4 && browserElementTestHelpers.getOOPByDefaultPref()) {
+      // Something weird when we doCommand with content editable element in OOP. Mark this case as todo
+      todo(false, "cut command works" + stateMeaning);
+    } else {
+      ok(SpecialPowers.wrap(msg).json === "", "cut command works" + stateMeaning);
+    }
+
+    state++;
+    dispatchTest(e);
+  });
+
+  mm.loadFrameScript(getScriptForGetContent(), false);
+}
+
+addEventListener('testready', runTest);
--- a/dom/browser-element/mochitest/browserElement_Iconchange.js
+++ b/dom/browser-element/mochitest/browserElement_Iconchange.js
@@ -7,19 +7,23 @@
 SimpleTest.waitForExplicitFinish();
 browserElementTestHelpers.setEnabledPref(true);
 browserElementTestHelpers.addPermission();
 
 function createHtml(link) {
   return 'data:text/html,<html><head>' + link + '<body></body></html>';
 }
 
-function createLink(name, sizes) {
+function createLink(name, sizes, rel) {
   var s = sizes ? 'sizes="' + sizes + '"' : '';
-  return '<link rel="icon" type="image/png" ' + s + ' href="http://example.com/' + name + '.png">';
+  if (!rel) {
+    rel = 'icon';
+  }
+  return '<link rel="' + rel + '" type="image/png" ' + s +
+    ' href="http://example.com/' + name + '.png">';
 }
 
 function runTest() {
   var iframe1 = document.createElement('iframe');
   SpecialPowers.wrap(iframe1).mozbrowser = true;
   document.body.appendChild(iframe1);
 
   // iframe2 is a red herring; we modify its favicon but don't listen for
@@ -83,21 +87,25 @@ function runTest() {
 
       // Make sure icon check is case insensitive
       SpecialPowers.getBrowserFrameMessageManager(iframe1)
                    .loadFrameScript("data:,content.document.head.insertAdjacentHTML('beforeend', '<link rel=ICON href=http://example.com/ucaseicon.png>')",
                                     /* allowDelayedLoad = */ false);
     }
     else if (numIconChanges == 6) {
       is(e.detail.href, 'http://example.com/ucaseicon.png');
-      iframe1.src = createHtml(createLink('testsize', '50x50'));
+      iframe1.src = createHtml(createLink('testsize', '50x50', 'icon'));
     }
     else if (numIconChanges == 7) {
       is(e.detail.href, 'http://example.com/testsize.png');
       is(e.detail.sizes, '50x50');
+      iframe1.src = createHtml(createLink('testapple1', '100x100', 'apple-touch-icon'));
+    } else if (numIconChanges == 8) {
+      is(e.detail.href, 'http://example.com/testapple1.png');
+      is(e.detail.sizes, '100x100');
       SimpleTest.finish();
     } else {
       ok(false, 'Too many iconchange events.');
     }
   });
 
   iframe3.addEventListener('mozbrowsericonchange', function(e) {
     ok(false, 'Should not get a iconchange event for iframe3.');
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -23,16 +23,17 @@ skip-if = (toolkit == 'gonk' && !debug)
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BackForward.html]
 [test_browserElement_oop_BadScreenshot.html]
 [test_browserElement_oop_BrowserWindowNamespace.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BrowserWindowResize.html]
 [test_browserElement_oop_Close.html]
 [test_browserElement_oop_CookiesNotThirdParty.html]
+[test_browserElement_oop_CopyPaste.html]
 [test_browserElement_oop_DOMRequestError.html]
 [test_browserElement_oop_DataURI.html]
 [test_browserElement_oop_DocumentFirstPaint.html]
 [test_browserElement_oop_Download.html]
 disabled = bug 1022281
 [test_browserElement_oop_ErrorSecurity.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_FirstPaint.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -14,16 +14,17 @@ support-files =
   browserElement_ThemeColor.js
   browserElement_BrowserWindowNamespace.js
   browserElement_BrowserWindowResize.js
   browserElement_Close.js
   browserElement_CloseApp.js
   browserElement_CloseFromOpener.js
   browserElement_ContextmenuEvents.js
   browserElement_CookiesNotThirdParty.js
+  browserElement_CopyPaste.js
   browserElement_DOMRequestError.js
   browserElement_DataURI.js
   browserElement_DocumentFirstPaint.js
   browserElement_Download.js
   browserElement_ErrorSecurity.js
   browserElement_ExposableURI.js
   browserElement_FirstPaint.js
   browserElement_ForwardName.js
@@ -131,16 +132,17 @@ skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_BrowserWindowResize.html]
 [test_browserElement_inproc_Close.html]
 [test_browserElement_inproc_CloseApp.html]
 skip-if = toolkit == 'android' || buildapp == 'b2g' # android(FAILS, bug 796982) androidx86(FAILS, bug 796982)
 [test_browserElement_inproc_CloseFromOpener.html]
 skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_ContextmenuEvents.html]
 [test_browserElement_inproc_CookiesNotThirdParty.html]
+[test_browserElement_inproc_CopyPaste.html]
 [test_browserElement_inproc_DOMRequestError.html]
 [test_browserElement_inproc_DataURI.html]
 [test_browserElement_inproc_DocumentFirstPaint.html]
 [test_browserElement_inproc_Download.html]
 disabled = bug 1022281
 [test_browserElement_inproc_ExposableURI.html]
 [test_browserElement_inproc_FirstPaint.html]
 [test_browserElement_inproc_ForwardName.html]
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_CopyPaste.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=987040
+-->
+<head>
+  <title>Test for Bug 987040</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=987040">Mozilla Bug 987040</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_CopyPaste.js">
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_CopyPaste.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=987040
+-->
+<head>
+  <title>Test for Bug 987040</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=987040">Mozilla Bug 987040</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_CopyPaste.js">
+</script>
+</body>
+</html>
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -556,17 +556,17 @@ NS_INTERFACE_MAP_END
 uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
 DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
 
 
 
 CanvasRenderingContext2D::CanvasRenderingContext2D()
   : mForceSoftware(false)
   // these are the default values from the Canvas spec
-  , mWidth(300), mHeight(150)
+  , mWidth(0), mHeight(0)
   , mZero(false), mOpaque(false)
   , mResetLayer(true)
   , mIPC(false)
   , mStream(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
   , mInvalidateCount(0)
 {
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-conformance/failing_tests_windows_msbasicrender.txt
@@ -0,0 +1,4 @@
+conformance/context/context-attributes-alpha-depth-stencil-antialias.html
+conformance/renderbuffers/framebuffer-object-attachment.html
+conformance/state/gl-object-get-calls.html
+conformance/more/functions/isTests.html
--- a/dom/canvas/test/webgl-conformance/mochitest.ini
+++ b/dom/canvas/test/webgl-conformance/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   failing_tests_android_nvidia.txt
   failing_tests_android_x86.txt
   failing_tests_linux.txt
   failing_tests_linux_mesa.txt
   failing_tests_linux_nvidia.txt
   failing_tests_mac.txt
   failing_tests_mac_mtnlion.txt
   failing_tests_windows.txt
+  failing_tests_windows_msbasicrender.txt
   skipped_tests_android.txt
   skipped_tests_android_x86.txt
   skipped_tests_linux_mesa.txt
   skipped_tests_win_vista.txt
   skipped_tests_winxp.txt
 
 [test_webgl_conformance_test_suite.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' # bug 865443- separate suite - the non_conf* tests pass except for one on armv6 tests
--- a/dom/canvas/test/webgl-conformance/test_webgl_conformance_test_suite.html
+++ b/dom/canvas/test/webgl-conformance/test_webgl_conformance_test_suite.html
@@ -77,16 +77,17 @@ function start() {
   var OS_WINDOWS = 'windows';
   var OS_MAC     = 'mac';
   var OS_LINUX   = 'linux';
   var OS_ANDROID = 'android';
   
   var GLDRIVER_MESA   = 'mesa';
   var GLDRIVER_NVIDIA = 'nvidia';
   var GLDRIVER_X86EMULATOR = 'android x86 emulator';
+  var GLDRIVER_MSBASICRENDER = 'Microsoft Basic Render Driver';
 
   var kOS = null;
   var kOSVersion = null;
   var kGLDriver = null;
 
   if (navigator.platform.indexOf('Win') == 0) {
     kOS = OS_WINDOWS;
     
@@ -112,18 +113,20 @@ function start() {
   info('GL renderer: ' + glRenderer);
   
   if (glRenderer.contains('Android Emulator')) {
     kGLDriver = GLDRIVER_X86EMULATOR;
   } else if (glRenderer.contains('llvmpipe')) {
     kGLDriver = GLDRIVER_MESA;
   } else if (glVendor.contains('NVIDIA')) {
     kGLDriver = GLDRIVER_NVIDIA;
+  } else if (glRenderer.contains(GLDRIVER_MSBASICRENDER)) {
+    kGLDriver = GLDRIVER_MSBASICRENDER;
   }
-  
+
   if (kOS) {
     info('OS detected as: ' + kOS);
     info('  Version: ' + kOSVersion);
   } else {
     info('OS not detected.');
     info('  `platform`:   ' + navigator.platform);
     info('  `appVersion`: ' + navigator.appVersion);
     info('  `userAgent`:  ' + navigator.userAgent);
@@ -484,18 +487,22 @@ function start() {
   // Windows uses the ANGLE library for rendering. Until everything is perfect, this means a different set of
   // failing tests. It's easier to do a platform check for Windows than for ANGLE itself.
   // Moreover, we currently also have different tests failing on Mac and on Linux,
   // presumably due to differences in the drivers.
   var failingTestsFilename = null;
   var skippedTestsFilename = null;
   switch (kOS) {
     case OS_WINDOWS: {
-      failingTestsFilename = 'failing_tests_windows.txt';
-      
+      if (kGLDriver == GLDRIVER_MSBASICRENDER) {
+        failingTestsFilename = 'failing_tests_windows_msbasicrender.txt';
+      } else {
+        failingTestsFilename = 'failing_tests_windows.txt';
+      }
+
       if (kOSVersion >= 6.0) // 6.0 is Vista
         skippedTestsFilename = 'skipped_tests_win_vista.txt'
       else // XP
         skippedTestsFilename = 'skipped_tests_winxp.txt';
         
       break;
     }
     case OS_MAC: {
--- a/dom/events/test/test_bug603008.html
+++ b/dom/events/test/test_bug603008.html
@@ -422,17 +422,17 @@ function testPreventDefault() {
      { name: "touchend", prevent: true }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false, doPrevent: true },
      { name: "touchend", prevent: false }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false },
-     { name: "touchend", prevent: false, doPrevent: true }]
+     { name: "touchend", prevent: true, doPrevent: true }]
   ];
 
   var dotest = function(aTest) {
     if (aTest.doPrevent) {
       target.addEventListener(aTest.name, preventFunction, false);
     }
 
     if (aTest.name == "touchmove") {
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -18,17 +18,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(c3ff0328-6c47-4e64-a22f-ac221959e258)]
+[scriptable, uuid(ed7cc4e4-cf5b-42af-9c2e-8df074a01470)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -421,17 +421,19 @@ interface nsIDOMWindow : nsISupports
    * nsISupports (nsISupportsPrimitives) types are converted to native
    * JS types when possible.
    */
   [noscript] nsIDOMWindow   openDialog(in DOMString url, in DOMString name,
                                        in DOMString options,
                                        in nsISupports aExtraArgument);
 
   // XXX Should this be in nsIDOMChromeWindow?
-  void                      updateCommands(in DOMString action);
+  void                      updateCommands(in DOMString action,
+                                           [optional] in nsISelection sel,
+                                           [optional] in short reason);
 
   /* Find in page.
    * @param str: the search pattern
    * @param caseSensitive: is the search caseSensitive
    * @param backwards: should we search backwards
    * @param wrapAround: should we wrap the search
    * @param wholeWord: should we search only for whole words
    * @param searchInFrames: should we search through all frames
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -725,16 +725,18 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
+  , mTouchEndCancelled(false)
+  , mEndTouchIsClick(false)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mUniqueId(0)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
@@ -1811,16 +1813,20 @@ TabChild::RecvHandleDoubleTap(const CSSP
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
+  if (mTouchEndCancelled) {
+    return true;
+  }
+
   LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
 
   MessageLoop::current()->PostDelayedTask(
     FROM_HERE,
     NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint),
     sActiveDurationMs);
   return true;
 }
@@ -1903,17 +1909,17 @@ TabChild::RecvNotifyAPZStateChange(const
   }
   case APZStateChange::StartPanning:
   {
     mActiveElementManager->HandlePanStart();
     break;
   }
   case APZStateChange::EndTouch:
   {
-    mActiveElementManager->HandleTouchEnd(aArg);
+    mEndTouchIsClick = aArg;
     break;
   }
   default:
     // APZStateChange has a 'sentinel' value, and the compiler complains
     // if an enumerator is not handled and there is no 'default' case.
     break;
   }
   return true;
@@ -2122,34 +2128,42 @@ TabChild::RecvRealTouchEvent(const Widge
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
+    mTouchEndCancelled = false;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
       SendContentReceivedTouch(aGuid, isTouchPrevented);
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
     }
     break;
   }
 
-  case NS_TOUCH_MOVE:
   case NS_TOUCH_END:
-  case NS_TOUCH_CANCEL: {
+    if (isTouchPrevented) {
+      mTouchEndCancelled = true;
+      mEndTouchIsClick = false;
+    }
+    // fall through
+  case NS_TOUCH_CANCEL:
+    mActiveElementManager->HandleTouchEnd(mEndTouchIsClick);
+    // fall through
+  case NS_TOUCH_MOVE: {
     SendPendingTouchPreventedResponse(isTouchPrevented, aGuid);
     break;
   }
 
   default:
     NS_WARNING("Unknown touch event type");
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -589,16 +589,19 @@ private:
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mPendingTouchPreventedResponse;
     ScrollableLayerGuid mPendingTouchPreventedGuid;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
+    bool mTouchEndCancelled;
+    bool mEndTouchIsClick;
+
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
     uint64_t mUniqueId;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -103,32 +103,22 @@ GlobalPCList.prototype = {
 
     let cleanupWinId = function(list, winID) {
       if (list.hasOwnProperty(winID)) {
         list[winID].forEach(cleanupPcRef);
         delete list[winID];
       }
     };
 
-    let hasPluginId = function(list, winID, pluginID, name, crashReport) {
+    let broadcastPluginCrash = function(list, winID, pluginID, name, crashReportID) {
       if (list.hasOwnProperty(winID)) {
         list[winID].forEach(function(pcref) {
           let pc = pcref.get();
           if (pc) {
-            if (pc._pc.pluginCrash(pluginID)) {
-              // Notify DOM window of the crash
-              let event = new CustomEvent("PluginCrashed",
-                { bubbles: false, cancelable: false,
-                  detail: {
-                    pluginName: name, 
-                    pluginDumpId: crashReport,
-                    submittedCrashReport: false }
-                });
-              pc._win.dispatchEvent(event);
-            }
+            pc._pc.pluginCrash(pluginID, name, crashReportID);
           }
         });
       }
     };
 
     if (topic == "inner-window-destroyed") {
       let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
       cleanupWinId(this._list, winID);
@@ -162,18 +152,18 @@ GlobalPCList.prototype = {
       let sep = data.indexOf(' ');
       let pluginId = data.slice(0, sep);
       let rest = data.slice(sep+1);
       // This presumes no spaces in the name!
       sep = rest.indexOf(' ');
       let name = rest.slice(0, sep);
       let crashId = rest.slice(sep+1);
       for (let winId in this._list) {
-        hasPluginId(this._list, winId, pluginId, name, crashId);
-      }      
+        broadcastPluginCrash(this._list, winId, pluginId, name, crashId);
+      }
     }
   },
 
   _registerPeerConnectionLifecycleCallback: function(winID, cb) {
     this._lifecycleobservers[winID] = cb;
   },
 };
 let _globalPCList = new GlobalPCList();
--- a/dom/network/moz.build
+++ b/dom/network/moz.build
@@ -6,12 +6,12 @@
 
 DIRS += ['interfaces', 'src']
 
 XPCSHELL_TESTS_MANIFESTS += [
     'tests/unit/xpcshell.ini',
     'tests/unit_ipc/xpcshell.ini',
 ]
 
-if CONFIG['tests/MOZ_B2G_RIL']:
+if CONFIG['MOZ_B2G_RIL']:
     XPCSHELL_TESTS_MANIFESTS += ['tests/unit_stats/xpcshell.ini']
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1478,18 +1478,18 @@ bool
   nsIDocument *doc = GetDocumentFromNPP(npp);
   NS_ENSURE_TRUE(doc, false);
 
   nsGlobalWindow* win = static_cast<nsGlobalWindow*>(doc->GetInnerWindow());
   if (NS_WARN_IF(!win || !win->FastGetGlobalJSObject())) {
     return false;
   }
 
-  AutoSafeJSContext cx;
-  JSAutoCompartment ac(cx, win->FastGetGlobalJSObject());
+  dom::AutoEntryScript aes(win);
+  JSContext* cx = aes.cx();
 
   JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
 
   if (!obj) {
     return false;
   }
 
   obj = JS_ObjectToInnerObject(cx, obj);
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -430,35 +430,40 @@ PluginScriptableObjectChild::~PluginScri
     }
     else {
       NS_ASSERTION(mType == LocalObject, "Wrong type!");
       PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
     }
   }
 }
 
-void
+bool
 PluginScriptableObjectChild::InitializeProxy()
 {
   AssertPluginThread();
   NS_ASSERTION(mType == Proxy, "Bad type!");
   NS_ASSERTION(!mObject, "Calling Initialize more than once!");
   NS_ASSERTION(!mInvalidated, "Already invalidated?!");
 
   mInstance = static_cast<PluginInstanceChild*>(Manager());
   NS_ASSERTION(mInstance, "Null manager?!");
 
   NPObject* object = CreateProxyObject();
-  NS_ASSERTION(object, "Failed to create object!");
+  if (!object) {
+    NS_ERROR("Failed to create object!");
+    return false;
+  }
 
   if (!PluginModuleChild::current()->RegisterActorForNPObject(object, this)) {
-    NS_ERROR("Out of memory?");
+    NS_ERROR("RegisterActorForNPObject failed");
+    return false;
   }
 
   mObject = object;
+  return true;
 }
 
 void
 PluginScriptableObjectChild::InitializeLocal(NPObject* aObject)
 {
   AssertPluginThread();
   NS_ASSERTION(mType == LocalObject, "Bad type!");
   NS_ASSERTION(!mObject, "Calling Initialize more than once!");
@@ -468,17 +473,17 @@ PluginScriptableObjectChild::InitializeL
   NS_ASSERTION(mInstance, "Null manager?!");
 
   PluginModuleChild::sBrowserFuncs.retainobject(aObject);
 
   NS_ASSERTION(!mProtectCount, "Should be zero!");
   mProtectCount++;
 
   if (!PluginModuleChild::current()->RegisterActorForNPObject(aObject, this)) {
-      NS_ERROR("Out of memory?");
+    NS_ERROR("RegisterActorForNPObject failed");
   }
 
   mObject = aObject;
 }
 
 NPObject*
 PluginScriptableObjectChild::CreateProxyObject()
 {
@@ -509,25 +514,21 @@ PluginScriptableObjectChild::CreateProxy
 
 bool
 PluginScriptableObjectChild::ResurrectProxyObject()
 {
   NS_ASSERTION(mInstance, "Must have an instance already!");
   NS_ASSERTION(!mObject, "Should not have an object already!");
   NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
 
-  NPObject* object = CreateProxyObject();
-  if (!object) {
-    NS_WARNING("Failed to create object!");
+  if (!InitializeProxy()) {
+    NS_ERROR("Initialize failed!");
     return false;
   }
 
-  InitializeProxy();
-  NS_ASSERTION(mObject, "Initialize failed!");
-
   SendProtect();
   return true;
 }
 
 NPObject*
 PluginScriptableObjectChild::GetObject(bool aCanResurrect)
 {
   if (!mObject && aCanResurrect && !ResurrectProxyObject()) {
--- a/dom/plugins/ipc/PluginScriptableObjectChild.h
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -41,17 +41,17 @@ struct ChildNPObject : NPObject
 class PluginScriptableObjectChild : public PPluginScriptableObjectChild
 {
   friend class PluginInstanceChild;
 
 public:
   PluginScriptableObjectChild(ScriptableObjectType aType);
   virtual ~PluginScriptableObjectChild();
 
-  void
+  bool
   InitializeProxy();
 
   void
   InitializeLocal(NPObject* aObject);
 
 
   virtual bool
   AnswerInvalidate() MOZ_OVERRIDE;
--- a/dom/plugins/test/mochitest/test_npruntime_npnevaluate.html
+++ b/dom/plugins/test/mochitest/test_npruntime_npnevaluate.html
@@ -43,16 +43,17 @@
       ["new Array(1, 2, 3, 4)", [1, 2, 3, 4]],
       ["document.getElementById('display')", 
           document.getElementById("display")],
       ["encodeURI('a = b')", "a%20=%20b"],
       ["document.getElementById('testdiv').innerHTML = 'Hello world!'", 
           "Hello world!"],
       ["function test2() { var x = {a: '1', b: '2'}; return x; } test2();", 
           {a: '1', b: '2'}],
+      ["(function() { var ret; try { win = window.open(); win.document.writeln('wibble'); ret = 'no error' } catch(e) { ret = e.name; } win.close(); return ret; })()", "no error"],
     ];
 
     var plugin = document.getElementById("plugin1");
 
     // Test calling NPN_Evaluate from within plugin code.
     for (var test of tests) {
       var expected = test[1];
       var result = plugin.npnEvaluateTest(test[0]);
deleted file mode 100644
--- a/dom/push/tests/moz.build
+++ /dev/null
@@ -1,4 +0,0 @@
-# vim: set filetype=python:
-# 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/.
deleted file mode 100644
--- a/dom/wappush/interfaces/moz.build
+++ /dev/null
@@ -1,5 +0,0 @@
-# vim: set filetype=python:
-# 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/.
-
--- a/dom/wappush/moz.build
+++ b/dom/wappush/moz.build
@@ -1,9 +1,9 @@
 # vim: set filetype=python:
 # 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/.
 
-DIRS += ['interfaces', 'src']
+DIRS += ['src']
 
 if CONFIG['ENABLE_TESTS']:
     XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -55,18 +55,18 @@ interface PeerConnectionImpl  {
    * into the SDP.
    */
   [Throws]
   void addIceCandidate(DOMString candidate, DOMString mid, unsigned short level);
 
   /* Puts the SIPCC engine back to 'kIdle', shuts down threads, deletes state */
   void close();
 
-  /* Notify DOM window if this plugin crash is ours */
-  boolean pluginCrash(unsigned long pluginId);
+  /* Notify DOM window if this plugin crash is ours. */
+  boolean pluginCrash(unsigned long long pluginId, DOMString name, DOMString pluginDumpID);
 
   /* Attributes */
   readonly attribute DOMString fingerprint;
   readonly attribute DOMString localDescription;
   readonly attribute DOMString remoteDescription;
 
   readonly attribute PCImplIceConnectionState iceConnectionState;
   readonly attribute PCImplIceGatheringState iceGatheringState;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/SelectionChangeEvent.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+dictionary SelectionChangeEventInit : EventInit {
+  DOMString selectedText = "";
+  DOMRectReadOnly? boundingClientRect = null;
+  short reason = 0;
+};
+
+[Constructor(DOMString type, optional SelectionChangeEventInit eventInit),
+ ChromeOnly]
+interface SelectionChangeEvent : Event {
+  readonly attribute DOMString selectedText;
+  readonly attribute DOMRectReadOnly? boundingClientRect;
+  readonly attribute short reason;
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -298,17 +298,19 @@ partial interface Window {
 
   [Throws] attribute boolean                            fullScreen;
 
   [Throws, ChromeOnly] void             back();
   [Throws, ChromeOnly] void             forward();
   [Throws, ChromeOnly] void             home();
 
   // XXX Should this be in nsIDOMChromeWindow?
-  void                      updateCommands(DOMString action);
+  void                      updateCommands(DOMString action,
+                                           optional Selection? sel = null,
+                                           optional short reason = 0);
 
   /* Find in page.
    * @param str: the search pattern
    * @param caseSensitive: is the search caseSensitive
    * @param backwards: should we search backwards
    * @param wrapAround: should we wrap the search
    * @param wholeWord: should we search only for whole words
    * @param searchInFrames: should we search through all frames
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -664,16 +664,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'PopStateEvent.webidl',
     'PopupBlockedEvent.webidl',
     'ProgressEvent.webidl',
     'RecordErrorEvent.webidl',
     'RTCDataChannelEvent.webidl',
     'RTCPeerConnectionIceEvent.webidl',
     'RTCPeerConnectionIdentityErrorEvent.webidl',
     'RTCPeerConnectionIdentityEvent.webidl',
+    'SelectionChangeEvent.webidl',
     'SmartCardEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
     'StyleSheetChangeEvent.webidl',
     'TrackEvent.webidl',
     'UserProximityEvent.webidl',
     'USSDReceivedEvent.webidl',
 ]
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4286,16 +4286,23 @@ WorkerPrivate::IsOnCurrentThread(bool* a
 void
 WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(mChildWorkers.IsEmpty());
   MOZ_ASSERT(mSyncLoopStack.IsEmpty());
 
   ClearMainEventQueue(aRanOrNot);
+#ifdef DEBUG
+  if (WorkerRan == aRanOrNot) {
+    nsIThread* currentThread = NS_GetCurrentThread();
+    MOZ_ASSERT(currentThread);
+    MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
+  }
+#endif
 
   if (WorkerPrivate* parent = GetParent()) {
     nsRefPtr<WorkerFinishedRunnable> runnable =
       new WorkerFinishedRunnable(parent, this);
     if (!runnable->Dispatch(nullptr)) {
       NS_WARNING("Failed to dispatch runnable!");
     }
   }
@@ -4522,17 +4529,16 @@ WorkerPrivate::ClearMainEventQueue(Worke
       nsRefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
       static_cast<nsIRunnable*>(runnable.get())->Run();
     }
   } else {
     nsIThread* currentThread = NS_GetCurrentThread();
     MOZ_ASSERT(currentThread);
 
     NS_ProcessPendingEvents(currentThread);
-    MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
   }
 
   MOZ_ASSERT(mCancelAllPendingRunnables);
   mCancelAllPendingRunnables = false;
 }
 
 uint32_t
 WorkerPrivate::RemainingRunTimeMS() const
--- a/gfx/2d/Point.h
+++ b/gfx/2d/Point.h
@@ -62,17 +62,17 @@ struct PointTyped :
   public units {
   static_assert(IsPixel<units>::value,
                 "'units' must be a coordinate system tag");
 
   typedef BasePoint< Float, PointTyped<units> > Super;
 
   MOZ_CONSTEXPR PointTyped() : Super() {}
   MOZ_CONSTEXPR PointTyped(Float aX, Float aY) : Super(aX, aY) {}
-  MOZ_CONSTEXPR PointTyped(const IntPointTyped<units>& point) : Super(float(point.x), float(point.y)) {}
+  MOZ_CONSTEXPR MOZ_IMPLICIT PointTyped(const IntPointTyped<units>& point) : Super(float(point.x), float(point.y)) {}
 
   // XXX When all of the code is ported, the following functions to convert to and from
   // unknown types should be removed.
 
   static PointTyped<units> FromUnknownPoint(const PointTyped<UnknownUnits>& aPoint) {
     return PointTyped<units>(aPoint.x, aPoint.y);
   }
 
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -98,17 +98,17 @@ struct AlignedArray
   typedef T value_type;
 
   AlignedArray()
     : mPtr(nullptr)
     , mStorage(nullptr)
   {
   }
 
-  MOZ_ALWAYS_INLINE AlignedArray(size_t aCount)
+  explicit MOZ_ALWAYS_INLINE AlignedArray(size_t aCount)
     : mStorage(nullptr)
     , mCount(0)
   {
     Realloc(aCount);
   }
 
   MOZ_ALWAYS_INLINE ~AlignedArray()
   {
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -1051,43 +1051,47 @@ template <>
 struct ParamTraits<mozilla::gfx::FilterPrimitiveDescription>
 {
   typedef mozilla::gfx::FilterPrimitiveDescription paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.Type());
     WriteParam(aMsg, aParam.PrimitiveSubregion());
+    WriteParam(aMsg, aParam.FilterSpaceBounds());
     WriteParam(aMsg, aParam.IsTainted());
     WriteParam(aMsg, aParam.OutputColorSpace());
     WriteParam(aMsg, aParam.NumberOfInputs());
     for (size_t i = 0; i < aParam.NumberOfInputs(); i++) {
       WriteParam(aMsg, aParam.InputPrimitiveIndex(i));
       WriteParam(aMsg, aParam.InputColorSpace(i));
     }
     WriteParam(aMsg, aParam.Attributes());
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     mozilla::gfx::PrimitiveType type;
     mozilla::gfx::IntRect primitiveSubregion;
+    mozilla::gfx::IntRect filterSpaceBounds;
     bool isTainted = false;
     mozilla::gfx::ColorSpace outputColorSpace;
     size_t numberOfInputs = 0;
     if (!ReadParam(aMsg, aIter, &type) ||
         !ReadParam(aMsg, aIter, &primitiveSubregion) ||
+        !ReadParam(aMsg, aIter, &filterSpaceBounds) ||
         !ReadParam(aMsg, aIter, &isTainted) ||
         !ReadParam(aMsg, aIter, &outputColorSpace) ||
         !ReadParam(aMsg, aIter, &numberOfInputs)) {
       return false;
     }
 
     aResult->SetType(type);
     aResult->SetPrimitiveSubregion(primitiveSubregion);
+    aResult->SetFilterSpaceBounds(filterSpaceBounds);
     aResult->SetIsTainted(isTainted);
     aResult->SetOutputColorSpace(outputColorSpace);
 
     for (size_t i = 0; i < numberOfInputs; i++) {
       int32_t inputPrimitiveIndex = 0;
       mozilla::gfx::ColorSpace inputColorSpace;
       if (!ReadParam(aMsg, aIter, &inputPrimitiveIndex) ||
           !ReadParam(aMsg, aIter, &inputColorSpace)) {
@@ -1103,24 +1107,22 @@ struct ParamTraits<mozilla::gfx::FilterP
 
 template <>
 struct ParamTraits<mozilla::gfx::FilterDescription>
 {
   typedef mozilla::gfx::FilterDescription paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.mFilterSpaceBounds);
     WriteParam(aMsg, aParam.mPrimitives);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
-    return (ReadParam(aMsg, aIter, &aResult->mFilterSpaceBounds) &&
-            ReadParam(aMsg, aIter, &aResult->mPrimitives));
+    return (ReadParam(aMsg, aIter, &aResult->mPrimitives));
   }
 };
 
 typedef mozilla::layers::GeckoContentController::APZStateChange APZStateChange;
 
 template <>
 struct ParamTraits<APZStateChange>
   : public ContiguousTypedEnumSerializer<
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -79,19 +79,16 @@ MOZ_BEGIN_ENUM_CLASS(TextureFlags, uint3
   // The contents of the texture must be uploaded or copied immediately
   // during the transaction, because the producer may want to write
   // to it again.
   IMMEDIATE_UPLOAD   = 1 << 15,
   // The texture is going to be used as part of a double
   // buffered pair, and so we can guarantee that the producer/consumer
   // won't be racing to access its contents.
   DOUBLE_BUFFERED    = 1 << 16,
-  // We've previously tried a texture and it didn't work for some reason. If there
-  // is a fallback available, try that.
-  ALLOC_FALLBACK     = 1 << 17,
   // Data in this texture has not been alpha-premultiplied.
   NON_PREMULTIPLIED  = 1 << 18,
 
   // OR union of all valid bits
   ALL_BITS           = (1 << 19) - 1,
   // the default flags
   DEFAULT = FRONT
 MOZ_END_ENUM_CLASS(TextureFlags)
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -228,25 +228,16 @@ void
 ContentClientRemoteBuffer::CreateBackBuffer(const nsIntRect& aBufferRect)
 {
   // gfx::BackendType::NONE means fallback to the content backend
   mTextureClient = CreateTextureClientForDrawing(
     mSurfaceFormat, mSize, gfx::BackendType::NONE,
     mTextureInfo.mTextureFlags,
     TextureAllocationFlags::ALLOC_CLEAR_BUFFER
   );
-  if (!mTextureClient) {
-    // try with ALLOC_FALLBACK
-    mTextureClient = CreateTextureClientForDrawing(
-      mSurfaceFormat, mSize, gfx::BackendType::NONE,
-      mTextureInfo.mTextureFlags | TextureFlags::ALLOC_FALLBACK,
-      TextureAllocationFlags::ALLOC_CLEAR_BUFFER
-    );
-  }
-
   if (!mTextureClient || !AddTextureClient(mTextureClient)) {
     AbortTextureClientCreation();
     return;
   }
 
   if (mTextureInfo.mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
     mTextureClientOnWhite = mTextureClient->CreateSimilar(
       mTextureInfo.mTextureFlags,
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -246,125 +246,112 @@ CreateBufferTextureClient(ISurfaceAlloca
     return result.forget();
   }
   RefPtr<BufferTextureClient> result = new ShmemTextureClient(aAllocator, aFormat,
                                                               aMoz2DBackend,
                                                               aTextureFlags);
   return result.forget();
 }
 
-static
+// static
 TemporaryRef<TextureClient>
-CreateTextureClientForDrawing(ISurfaceAllocator* aAllocator,
-                              SurfaceFormat aFormat,
-                              TextureFlags aTextureFlags,
-                              gfx::BackendType aMoz2DBackend,
-                              const gfx::IntSize& aSizeHint)
+TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator,
+                                gfx::SurfaceFormat aFormat,
+                                gfx::IntSize aSize,
+                                gfx::BackendType aMoz2DBackend,
+                                TextureFlags aTextureFlags,
+                                TextureAllocationFlags aAllocFlags)
 {
   if (aMoz2DBackend == gfx::BackendType::NONE) {
     aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend();
   }
 
-  RefPtr<TextureClient> result;
+  RefPtr<TextureClient> texture;
 
 #if defined(MOZ_WIDGET_GONK) || defined(XP_WIN)
   int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
 #endif
 
 #ifdef XP_WIN
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   if (parentBackend == LayersBackend::LAYERS_D3D11 &&
       (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
         aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) &&
       gfxWindowsPlatform::GetPlatform()->GetD2DDevice() &&
-      aSizeHint.width <= maxTextureSize &&
-      aSizeHint.height <= maxTextureSize &&
-      !(aTextureFlags & TextureFlags::ALLOC_FALLBACK)) {
-    result = new TextureClientD3D11(aFormat, aTextureFlags);
+      aSize.width <= maxTextureSize &&
+      aSize.height <= maxTextureSize) {
+    texture = new TextureClientD3D11(aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
-      aSizeHint.width <= maxTextureSize &&
-      aSizeHint.height <= maxTextureSize &&
-      !(aTextureFlags & TextureFlags::ALLOC_FALLBACK)) {
+      aSize.width <= maxTextureSize &&
+      aSize.height <= maxTextureSize) {
     if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
-      result = new CairoTextureClientD3D9(aFormat, aTextureFlags);
+      texture = new CairoTextureClientD3D9(aFormat, aTextureFlags);
     }
   }
 
-  if (!result && aFormat == SurfaceFormat::B8G8R8X8 &&
+  if (!texture && aFormat == SurfaceFormat::B8G8R8X8 &&
       aAllocator->IsSameProcess()) {
-    result = new DIBTextureClient(aFormat, aTextureFlags);
+    texture = new DIBTextureClient(aFormat, aTextureFlags);
   }
 
 #endif
 
 #ifdef MOZ_X11
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   gfxSurfaceType type =
     gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
 
   if (parentBackend == LayersBackend::LAYERS_BASIC &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
-      type == gfxSurfaceType::Xlib &&
-      !(aTextureFlags & TextureFlags::ALLOC_FALLBACK))
+      type == gfxSurfaceType::Xlib)
   {
-    result = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
+    texture = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
   }
 #ifdef GL_PROVIDER_GLX
   if (parentBackend == LayersBackend::LAYERS_OPENGL &&
       type == gfxSurfaceType::Xlib &&
-      !(aTextureFlags & TextureFlags::ALLOC_FALLBACK) &&
       aFormat != SurfaceFormat::A8 &&
       gl::sGLXLibrary.UseTextureFromPixmap())
   {
-    result = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
+    texture = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
   }
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_GONK
-  if (!DisableGralloc(aFormat, aSizeHint)) {
+  if (!DisableGralloc(aFormat, aSize)) {
     // Don't allow Gralloc texture clients to exceed the maximum texture size.
     // BufferTextureClients have code to handle tiling the surface client-side.
-    if (aSizeHint.width <= maxTextureSize && aSizeHint.height <= maxTextureSize) {
-      result = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend,
+    if (aSize.width <= maxTextureSize && aSize.height <= maxTextureSize) {
+      texture = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend,
                                            aTextureFlags);
     }
   }
 #endif
 
-  // Can't do any better than a buffer texture client.
-  if (!result) {
-    result = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, aMoz2DBackend);
+  MOZ_ASSERT(!texture || texture->CanExposeDrawTarget(), "texture cannot expose a DrawTarget?");
+
+  if (texture && texture->AllocateForSurface(aSize, aAllocFlags)) {
+    return texture;
   }
 
-  MOZ_ASSERT(!result || result->CanExposeDrawTarget(), "texture cannot expose a DrawTarget?");
-  return result;
-}
+  if (texture) {
+    NS_WARNING("Failed to allocate a TextureClient, falling back to BufferTextureClient.");
+  }
 
-// static
-TemporaryRef<TextureClient>
-TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator,
-                                gfx::SurfaceFormat aFormat,
-                                gfx::IntSize aSize,
-                                gfx::BackendType aMoz2DBackend,
-                                TextureFlags aTextureFlags,
-                                TextureAllocationFlags aAllocFlags)
-{
-  RefPtr<TextureClient> texture =
-    CreateTextureClientForDrawing(aAllocator, aFormat,
-                                  aTextureFlags, aMoz2DBackend,
-                                  aSize);
-  if (texture) {
-    if (!texture->AllocateForSurface(aSize, aAllocFlags)) {
-      return nullptr;
-    }
+  // Can't do any better than a buffer texture client.
+  texture = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, aMoz2DBackend);
+
+  if (!texture->AllocateForSurface(aSize, aAllocFlags)) {
+    return nullptr;
   }
+
   return texture;
 }
 
 // static
 TemporaryRef<BufferTextureClient>
 TextureClient::CreateForRawBufferAccess(ISurfaceAllocator* aAllocator,
                                         gfx::SurfaceFormat aFormat,
                                         gfx::IntSize aSize,
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -719,16 +719,19 @@ SharedTextureClientD3D9::SharedTextureCl
   , mHandle(0)
   , mIsLocked(false)
 {
   MOZ_COUNT_CTOR(SharedTextureClientD3D9);
 }
 
 SharedTextureClientD3D9::~SharedTextureClientD3D9()
 {
+  if (mTexture && mActor) {
+    KeepUntilFullDeallocation(new TKeepAlive<IDirect3DTexture9>(mTexture));
+  }
   MOZ_COUNT_DTOR(SharedTextureClientD3D9);
 }
 
 bool
 SharedTextureClientD3D9::Lock(OpenMode)
 {
   MOZ_ASSERT(!mIsLocked);
   if (!IsValid()) {
@@ -913,56 +916,73 @@ DXGITextureHostD3D9::DXGITextureHostD3D9
   const SurfaceDescriptorD3D10& aDescriptor)
   : TextureHost(aFlags)
   , mHandle(aDescriptor.handle())
   , mFormat(aDescriptor.format())
   , mSize(aDescriptor.size())
   , mIsLocked(false)
 {
   MOZ_ASSERT(mHandle);
+  OpenSharedHandle();
+}
+
+IDirect3DDevice9*
+DXGITextureHostD3D9::GetDevice()
+{
+  return mCompositor ? mCompositor->device() : nullptr;
+}
+
+void
+DXGITextureHostD3D9::OpenSharedHandle()
+{
+  MOZ_ASSERT(!mTextureSource);
+
+  if (!GetDevice()) {
+    return;
+  }
+
+  nsRefPtr<IDirect3DTexture9> texture;
+  HRESULT hr = GetDevice()->CreateTexture(mSize.width, mSize.height, 1,
+                                          D3DUSAGE_RENDERTARGET,
+                                          SurfaceFormatToD3D9Format(mFormat),
+                                          D3DPOOL_DEFAULT,
+                                          getter_AddRefs(texture),
+                                          (HANDLE*)&mHandle);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to open shared texture");
+    return;
+  }
+
+  mTextureSource = new DataTextureSourceD3D9(mFormat, mSize, mCompositor, texture);
+
+  return;
 }
 
 NewTextureSource*
 DXGITextureHostD3D9::GetTextureSources()
 {
   MOZ_ASSERT(mIsLocked);
   MOZ_ASSERT(mTextureSource);
   return mTextureSource;
 }
 
 bool
 DXGITextureHostD3D9::Lock()
 {
   MOZ_ASSERT(!mIsLocked);
-  DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
-  if (!deviceManager) {
-    NS_WARNING("trying to lock a TextureHost without a D3D device");
+
+  if (!GetDevice()) {
     return false;
   }
 
   if (!mTextureSource) {
-    nsRefPtr<IDirect3DTexture9> tex;
-    HRESULT hr = deviceManager->device()->CreateTexture(mSize.width,
-                                                        mSize.height,
-                                                        1,
-                                                        D3DUSAGE_RENDERTARGET,
-                                                        SurfaceFormatToD3D9Format(mFormat),
-                                                        D3DPOOL_DEFAULT,
-                                                        getter_AddRefs(tex),
-                                                        (HANDLE*)&mHandle);
-    if (FAILED(hr)) {
-      NS_WARNING("Failed to open shared texture");
-      return false;
-    }
-
-    mTextureSource = new DataTextureSourceD3D9(mFormat, mSize, mCompositor, tex);
+    OpenSharedHandle();
   }
-
-  mIsLocked = true;
-  return true;
+  mIsLocked = !!mTextureSource;
+  return mIsLocked;
 }
 
 void
 DXGITextureHostD3D9::Unlock()
 {
   MOZ_ASSERT(mIsLocked);
   mIsLocked = false;
 }
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -348,16 +348,19 @@ public:
   virtual void Unlock() MOZ_OVERRIDE;
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE
   {
     return nullptr; // TODO: cf bug 872568
   }
 
 protected:
+  void OpenSharedHandle();
+  IDirect3DDevice9* GetDevice();
+
   RefPtr<DataTextureSourceD3D9> mTextureSource;
   RefPtr<CompositorD3D9> mCompositor;
   WindowsHandle mHandle;
   gfx::SurfaceFormat mFormat;
   gfx::IntSize mSize;
   bool mIsLocked;
 };
 
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -1049,17 +1049,16 @@ FilterNodeGraphFromDescription(DrawTarge
                                const IntRect& aSourceGraphicRect,
                                SourceSurface* aFillPaint,
                                const IntRect& aFillPaintRect,
                                SourceSurface* aStrokePaint,
                                const IntRect& aStrokePaintRect,
                                nsTArray<RefPtr<SourceSurface>>& aAdditionalImages)
 {
   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
-  const IntRect& filterSpaceBounds = aFilter.mFilterSpaceBounds;
 
   Rect resultNeededRect(aResultNeededRect);
   resultNeededRect.RoundOut();
 
   RefPtr<FilterCachedColorModels> sourceFilters[4];
   nsTArray<RefPtr<FilterCachedColorModels> > primitiveFilters;
 
   for (size_t i = 0; i < primitives.Length(); ++i) {
@@ -1068,20 +1067,19 @@ FilterNodeGraphFromDescription(DrawTarge
     nsTArray<RefPtr<FilterNode> > inputFilterNodes;
     nsTArray<IntRect> inputSourceRects;
     nsTArray<AlphaModel> inputAlphaModels;
 
     for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
 
       int32_t inputIndex = descr.InputPrimitiveIndex(j);
       if (inputIndex < 0) {
-        inputSourceRects.AppendElement(filterSpaceBounds);
+        inputSourceRects.AppendElement(descr.FilterSpaceBounds());
       } else {
-        inputSourceRects.AppendElement(filterSpaceBounds.Intersect(
-          primitives[inputIndex].PrimitiveSubregion()));
+        inputSourceRects.AppendElement(primitives[inputIndex].PrimitiveSubregion());
       }
 
       RefPtr<FilterCachedColorModels> inputFilter;
       if (inputIndex >= 0) {
         MOZ_ASSERT(inputIndex < (int64_t)primitiveFilters.Length(), "out-of-bounds input index!");
         inputFilter = primitiveFilters[inputIndex];
         MOZ_ASSERT(inputFilter, "Referred to input filter that comes after the current one?");
       } else {
@@ -1123,18 +1121,18 @@ FilterNodeGraphFromDescription(DrawTarge
       inputFilterNodes.AppendElement(inputFilter->ForColorModel(inputColorModel));
     }
 
     RefPtr<FilterNode> primitiveFilterNode =
       FilterNodeFromPrimitiveDescription(descr, aDT, inputFilterNodes,
                                          inputSourceRects, aAdditionalImages);
 
     if (primitiveFilterNode) {
-      IntRect cropRect = filterSpaceBounds.Intersect(descr.PrimitiveSubregion());
-      primitiveFilterNode = FilterWrappers::Crop(aDT, primitiveFilterNode, cropRect);
+      primitiveFilterNode =
+        FilterWrappers::Crop(aDT, primitiveFilterNode, descr.PrimitiveSubregion());
     }
 
     ColorModel outputColorModel(descr.OutputColorSpace(),
       OutputAlphaModelForPrimitive(descr, inputAlphaModels));
     RefPtr<FilterCachedColorModels> primitiveFilter =
       new FilterCachedColorModels(aDT, primitiveFilterNode, outputColorModel);
 
     primitiveFilters.AppendElement(primitiveFilter);
@@ -1293,19 +1291,17 @@ FilterSupport::ComputeResultChangeRegion
       nsIntRegion inputChangeRegion =
         ElementForIndex(inputIndex, resultChangeRegions,
                         aSourceGraphicChange, aFillPaintChange,
                         aStrokePaintChange);
       inputChangeRegions.AppendElement(inputChangeRegion);
     }
     nsIntRegion changeRegion =
       ResultChangeRegionForPrimitive(descr, inputChangeRegions);
-    IntRect cropRect =
-      descr.PrimitiveSubregion().Intersect(aFilter.mFilterSpaceBounds);
-    changeRegion.And(changeRegion, ThebesIntRect(cropRect));
+    changeRegion.And(changeRegion, ThebesIntRect(descr.PrimitiveSubregion()));
     resultChangeRegions.AppendElement(changeRegion);
   }
 
   return resultChangeRegions[resultChangeRegions.Length() - 1];
 }
 
 static nsIntRegion
 PostFilterExtentsForPrimitive(const FilterPrimitiveDescription& aDescription,
@@ -1380,35 +1376,33 @@ PostFilterExtentsForPrimitive(const Filt
   }
 }
 
 /* static */ nsIntRegion
 FilterSupport::ComputePostFilterExtents(const FilterDescription& aFilter,
                                         const nsIntRegion& aSourceGraphicExtents)
 {
   const nsTArray<FilterPrimitiveDescription>& primitives = aFilter.mPrimitives;
-  nsIntRegion filterSpace = ThebesIntRect(aFilter.mFilterSpaceBounds);
   nsTArray<nsIntRegion> postFilterExtents;
 
   for (int32_t i = 0; i < int32_t(primitives.Length()); ++i) {
     const FilterPrimitiveDescription& descr = primitives[i];
+    nsIntRegion filterSpace = ThebesIntRect(descr.FilterSpaceBounds());
 
     nsTArray<nsIntRegion> inputExtents;
     for (size_t j = 0; j < descr.NumberOfInputs(); j++) {
       int32_t inputIndex = descr.InputPrimitiveIndex(j);
       MOZ_ASSERT(inputIndex < i, "bad input index");
       nsIntRegion inputExtent =
         ElementForIndex(inputIndex, postFilterExtents,
                         aSourceGraphicExtents, filterSpace, filterSpace);
       inputExtents.AppendElement(inputExtent);
     }
     nsIntRegion extent = PostFilterExtentsForPrimitive(descr, inputExtents);
-    IntRect cropRect =
-      descr.PrimitiveSubregion().Intersect(aFilter.mFilterSpaceBounds);
-    extent.And(extent, ThebesIntRect(cropRect));
+    extent.And(extent, ThebesIntRect(descr.PrimitiveSubregion()));
     postFilterExtents.AppendElement(extent);
   }
 
   return postFilterExtents[postFilterExtents.Length() - 1];
 }
 
 static nsIntRegion
 SourceNeededRegionForPrimitive(const FilterPrimitiveDescription& aDescription,
@@ -1534,18 +1528,22 @@ FilterSupport::ComputeSourceNeededRegion
         &ElementForIndex(inputIndex, primitiveNeededRegions,
                          aSourceGraphicNeededRegion,
                          aFillPaintNeededRegion, aStrokePaintNeededRegion));
       inputNeededRegion->Or(*inputNeededRegion,
         SourceNeededRegionForPrimitive(descr, neededRegion, j));
     }
   }
 
-  aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
-                                 ThebesIntRect(aFilter.mFilterSpaceBounds));
+  // Clip original SourceGraphic to first filter region.
+  if (primitives.Length() > 0) {
+    const FilterPrimitiveDescription& firstDescr = primitives[0];
+    aSourceGraphicNeededRegion.And(aSourceGraphicNeededRegion,
+                                   ThebesIntRect(firstDescr.FilterSpaceBounds()));
+  }
 }
 
 // FilterPrimitiveDescription
 
 FilterPrimitiveDescription::FilterPrimitiveDescription()
  : mType(PrimitiveType::Empty)
  , mOutputColorSpace(ColorSpace::SRGB)
  , mIsTainted(false)
@@ -1559,56 +1557,58 @@ FilterPrimitiveDescription::FilterPrimit
 {
 }
 
 FilterPrimitiveDescription::FilterPrimitiveDescription(const FilterPrimitiveDescription& aOther)
  : mType(aOther.mType)
  , mAttributes(aOther.mAttributes)
  , mInputPrimitives(aOther.mInputPrimitives)
  , mFilterPrimitiveSubregion(aOther.mFilterPrimitiveSubregion)
+ , mFilterSpaceBounds(aOther.mFilterSpaceBounds)
  , mInputColorSpaces(aOther.mInputColorSpaces)
  , mOutputColorSpace(aOther.mOutputColorSpace)
  , mIsTainted(aOther.mIsTainted)
 {
 }
 
 FilterPrimitiveDescription&
 FilterPrimitiveDescription::operator=(const FilterPrimitiveDescription& aOther)
 {
   if (this != &aOther) {
     mType = aOther.mType;
     mAttributes = aOther.mAttributes;
     mInputPrimitives = aOther.mInputPrimitives;
     mFilterPrimitiveSubregion = aOther.mFilterPrimitiveSubregion;
+    mFilterSpaceBounds = aOther.mFilterSpaceBounds;
     mInputColorSpaces = aOther.mInputColorSpaces;
     mOutputColorSpace = aOther.mOutputColorSpace;
     mIsTainted = aOther.mIsTainted;
   }
   return *this;
 }
 
 bool
 FilterPrimitiveDescription::operator==(const FilterPrimitiveDescription& aOther) const
 {
   return mType == aOther.mType &&
     mFilterPrimitiveSubregion.IsEqualInterior(aOther.mFilterPrimitiveSubregion) &&
+    mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
     mOutputColorSpace == aOther.mOutputColorSpace &&
     mIsTainted == aOther.mIsTainted &&
     mInputPrimitives == aOther.mInputPrimitives &&
     mInputColorSpaces == aOther.mInputColorSpaces &&
     mAttributes == aOther.mAttributes;
 }
 
 // FilterDescription
 
 bool
 FilterDescription::operator==(const FilterDescription& aOther) const
 {
-  return mFilterSpaceBounds.IsEqualInterior(aOther.mFilterSpaceBounds) &&
-    mPrimitives == aOther.mPrimitives;
+  return mPrimitives == aOther.mPrimitives;
 }
 
 // AttributeMap
 
 // A class that wraps different types for easy storage in a hashtable. Only
 // used by AttributeMap.
 struct FilterAttribute {
   FilterAttribute(const FilterAttribute& aOther);
--- a/gfx/src/FilterSupport.h
+++ b/gfx/src/FilterSupport.h
@@ -295,16 +295,17 @@ public:
   FilterPrimitiveDescription& operator=(const FilterPrimitiveDescription& aOther);
 
   PrimitiveType Type() const { return mType; }
   void SetType(PrimitiveType aType) { mType = aType; }
   const AttributeMap& Attributes() const { return mAttributes; }
   AttributeMap& Attributes() { return mAttributes; }
 
   IntRect PrimitiveSubregion() const { return mFilterPrimitiveSubregion; }
+  IntRect FilterSpaceBounds() const { return mFilterSpaceBounds; }
   bool IsTainted() const { return mIsTainted; }
 
   size_t NumberOfInputs() const { return mInputPrimitives.Length(); }
   int32_t InputPrimitiveIndex(size_t aInputIndex) const
   {
     return aInputIndex < mInputPrimitives.Length() ?
       mInputPrimitives[aInputIndex] : 0;
   }
@@ -317,16 +318,21 @@ public:
 
   ColorSpace OutputColorSpace() const { return mOutputColorSpace; }
 
   void SetPrimitiveSubregion(const IntRect& aRect)
   {
     mFilterPrimitiveSubregion = aRect;
   }
 
+  void SetFilterSpaceBounds(const IntRect& aRect)
+  {
+    mFilterSpaceBounds = aRect;
+  }
+
   void SetIsTainted(bool aIsTainted)
   {
     mIsTainted = aIsTainted;
   }
 
   void SetInputPrimitive(size_t aInputIndex, int32_t aInputPrimitiveIndex)
   {
     mInputPrimitives.EnsureLengthAtLeast(aInputIndex + 1);
@@ -350,42 +356,40 @@ public:
     return !(*this == aOther);
   }
 
 private:
   PrimitiveType mType;
   AttributeMap mAttributes;
   nsTArray<int32_t> mInputPrimitives;
   IntRect mFilterPrimitiveSubregion;
+  IntRect mFilterSpaceBounds;
   nsTArray<ColorSpace> mInputColorSpaces;
   ColorSpace mOutputColorSpace;
   bool mIsTainted;
 };
 
 /**
  * A data structure that contains one or more FilterPrimitiveDescriptions.
  * Designed to be serializable via IPDL, so it must not contain complex
  * functionality.
  */
 struct FilterDescription MOZ_FINAL {
   FilterDescription() {}
-  FilterDescription(const nsTArray<FilterPrimitiveDescription>& aPrimitives,
-                    const IntRect& aFilterSpaceBounds)
+  FilterDescription(const nsTArray<FilterPrimitiveDescription>& aPrimitives)
    : mPrimitives(aPrimitives)
-   , mFilterSpaceBounds(aFilterSpaceBounds)
   {}
 
   bool operator==(const FilterDescription& aOther) const;
   bool operator!=(const FilterDescription& aOther) const
   {
     return !(*this == aOther);
   }
 
   nsTArray<FilterPrimitiveDescription> mPrimitives;
-  IntRect mFilterSpaceBounds;
 };
 
 /**
  * The methods of this class are not on FilterDescription because
  * FilterDescription is designed as a simple value holder that can be used
  * on any thread.
  */
 class FilterSupport {
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -1,193 +1,75 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxDrawable.h"
 #include "gfxASurface.h"
 #include "gfxContext.h"
-#include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxColor.h"
+#include "gfx2DGlue.h"
 #ifdef MOZ_X11
 #include "cairo.h"
 #include "gfxXlibSurface.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
-gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface,
-                                       const gfxIntSize aSize,
-                                       const gfxMatrix aTransform)
- : gfxDrawable(aSize)
- , mSurface(aSurface)
- , mTransform(aTransform)
-{
-}
-
-gfxSurfaceDrawable::gfxSurfaceDrawable(DrawTarget* aDrawTarget,
-                                       const gfxIntSize aSize,
-                                       const gfxMatrix aTransform)
- : gfxDrawable(aSize)
- , mDrawTarget(aDrawTarget)
- , mTransform(aTransform)
-{
-}
-
 gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
                                        const gfxIntSize aSize,
                                        const gfxMatrix aTransform)
  : gfxDrawable(aSize)
  , mSourceSurface(aSurface)
  , mTransform(aTransform)
 {
 }
 
-static gfxMatrix
-DeviceToImageTransform(gfxContext* aContext,
-                       const gfxMatrix& aUserSpaceToImageSpace)
-{
-    gfxFloat deviceX, deviceY;
-    nsRefPtr<gfxASurface> currentTarget =
-        aContext->CurrentSurface(&deviceX, &deviceY);
-    gfxMatrix currentMatrix = aContext->CurrentMatrix();
-    gfxMatrix deviceToUser = currentMatrix;
-    if (!deviceToUser.Invert()) {
-        return gfxMatrix(0, 0, 0, 0, 0, 0); // singular
-    }
-    deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
-    return deviceToUser * aUserSpaceToImageSpace;
-}
-
-static void
-PreparePatternForUntiledDrawing(gfxPattern* aPattern,
-                                const gfxMatrix& aDeviceToImage,
-                                gfxASurface *currentTarget,
-                                const GraphicsFilter aDefaultFilter)
-{
-    if (!currentTarget) {
-        // This happens if we're dealing with an Azure target.
-        aPattern->SetExtend(gfxPattern::EXTEND_PAD);
-        aPattern->SetFilter(aDefaultFilter);
-        return;
-    }
-
-    // In theory we can handle this using cairo's EXTEND_PAD,
-    // but implementation limitations mean we have to consult
-    // the surface type.
-    switch (currentTarget->GetType()) {
-
-#ifdef MOZ_X11
-        case gfxSurfaceType::Xlib:
-        {
-            // See bugs 324698, 422179, and 468496.  This is a workaround for
-            // XRender's RepeatPad not being implemented correctly on old X
-            // servers.
-            //
-            // In this situation, cairo avoids XRender and instead reads back
-            // to perform EXTEND_PAD with pixman.  This is too slow so we
-            // avoid EXTEND_PAD and set the filter to CAIRO_FILTER_FAST ---
-            // otherwise, pixman's sampling will sample transparency for the
-            // outside edges and we'll get blurry edges.
-            //
-            // But don't do this for simple downscales because it's horrible.
-            // Downscaling means that device-space coordinates are
-            // scaled *up* to find the image pixel coordinates.
-            //
-            // Cairo, and hence Gecko, can use RepeatPad on Xorg 1.7. We
-            // enable EXTEND_PAD provided that we're running on a recent
-            // enough X server.
-            if (static_cast<gfxXlibSurface*>(currentTarget)->IsPadSlow()) {
-                bool isDownscale =
-                    aDeviceToImage._11 >= 1.0 && aDeviceToImage._22 >= 1.0 &&
-                    aDeviceToImage._21 == 0.0 && aDeviceToImage._12 == 0.0;
-
-                GraphicsFilter filter =
-                    isDownscale ? aDefaultFilter : (const GraphicsFilter)GraphicsFilter::FILTER_FAST;
-                aPattern->SetFilter(filter);
-
-                // Use the default EXTEND_NONE
-                break;
-            }
-            // else fall through to EXTEND_PAD and the default filter.
-        }
-#endif
-
-        default:
-            // turn on EXTEND_PAD.
-            // This is what we really want for all surface types, if the
-            // implementation was universally good.
-            aPattern->SetExtend(gfxPattern::EXTEND_PAD);
-            aPattern->SetFilter(aDefaultFilter);
-            break;
-    }
-}
-
 bool
 gfxSurfaceDrawable::Draw(gfxContext* aContext,
                          const gfxRect& aFillRect,
                          bool aRepeat,
                          const GraphicsFilter& aFilter,
                          const gfxMatrix& aTransform)
 {
-    nsRefPtr<gfxPattern> pattern;
-    if (mDrawTarget) {
-      RefPtr<SourceSurface> source = mDrawTarget->Snapshot();
-      pattern = new gfxPattern(source, Matrix());
-    } else if (mSourceSurface) {
-      pattern = new gfxPattern(mSourceSurface, Matrix());
-    } else {
-      pattern = new gfxPattern(mSurface);
+    ExtendMode extend = ExtendMode::CLAMP;
+
+    if (aRepeat) {
+        extend = ExtendMode::REPEAT;
     }
-    if (aRepeat) {
-        pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
-        pattern->SetFilter(aFilter);
+
+    Matrix patternTransform = ToMatrix(aTransform * mTransform);
+    patternTransform.Invert();
+
+    SurfacePattern pattern(mSourceSurface, extend,
+                           patternTransform, ToFilter(aFilter));
+
+    Rect fillRect = ToRect(aFillRect);
+    DrawTarget* dt = aContext->GetDrawTarget();
+
+    if (aContext->CurrentOperator() == gfxContext::OPERATOR_CLEAR) {
+        dt->ClearRect(fillRect);
+    } else if (aContext->CurrentOperator() == gfxContext::OPERATOR_SOURCE) {
+        // Emulate cairo operator source which is bound by mask!
+        dt->ClearRect(fillRect);
+        dt->FillRect(fillRect, pattern);
     } else {
-        GraphicsFilter filter = aFilter;
-        if (aContext->CurrentMatrix().HasOnlyIntegerTranslation() &&
-            aTransform.HasOnlyIntegerTranslation())
-        {
-          // If we only have integer translation, no special filtering needs to
-          // happen and we explicitly use FILTER_FAST. This is fast for some
-          // backends.
-          filter = GraphicsFilter::FILTER_FAST;
-        }
-        nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
-        gfxMatrix deviceSpaceToImageSpace =
-            DeviceToImageTransform(aContext, aTransform);
-        PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace,
-                                        currentTarget, filter);
+        CompositionOp op = CompositionOpForOp(aContext->CurrentOperator());
+        AntialiasMode aaMode =
+            aContext->CurrentAntialiasMode() == gfxContext::MODE_ALIASED ?
+                AntialiasMode::NONE :
+                AntialiasMode::SUBPIXEL;
+        dt->FillRect(fillRect, pattern, DrawOptions(1.0f, op, aaMode));
     }
-    pattern->SetMatrix(aTransform * mTransform);
-    aContext->NewPath();
-    aContext->SetPattern(pattern);
-    aContext->Rectangle(aFillRect);
-    aContext->Fill();
-    // clear the pattern so that the snapshot is released before the
-    // drawable is destroyed
-    aContext->SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 0.0));
     return true;
 }
 
-already_AddRefed<gfxImageSurface>
-gfxSurfaceDrawable::GetAsImageSurface()
-{
-    if (mDrawTarget || mSourceSurface) {
-      // TODO: Find a way to implement this. The caller really wants a 'sub-image' of
-      // the original, without having to do a copy. GetDataSurface() might just copy,
-      // which isn't useful.
-      return nullptr;
-
-    }
-    return mSurface->GetAsImageSurface();
-}
-
 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
                                          const gfxIntSize aSize)
  : gfxDrawable(aSize)
  , mCallback(aCallback)
 {
 }
 
 already_AddRefed<gfxSurfaceDrawable>
--- a/gfx/thebes/gfxDrawable.h
+++ b/gfx/thebes/gfxDrawable.h
@@ -8,17 +8,16 @@
 
 #include "nsAutoPtr.h"
 #include "gfxRect.h"
 #include "gfxMatrix.h"
 #include "GraphicsFilter.h"
 #include "mozilla/gfx/2D.h"
 
 class gfxASurface;
-class gfxImageSurface;
 class gfxContext;
 class gfxPattern;
 
 /**
  * gfxDrawable
  * An Interface representing something that has an intrinsic size and can draw
  * itself repeatedly.
  */
@@ -35,51 +34,42 @@ public:
      * pattern prior to rendering it.
      *  @return whether drawing was successful
      */
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         bool aRepeat,
                         const GraphicsFilter& aFilter,
                         const gfxMatrix& aTransform = gfxMatrix()) = 0;
-    virtual already_AddRefed<gfxImageSurface> GetAsImageSurface() { return nullptr; }
     virtual gfxIntSize Size() { return mSize; }
 
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxDrawable() {}
 
     const gfxIntSize mSize;
 };
 
 /**
  * gfxSurfaceDrawable
  * A convenience implementation of gfxDrawable for surfaces.
  */
 class gfxSurfaceDrawable : public gfxDrawable {
 public:
-    gfxSurfaceDrawable(gfxASurface* aSurface, const gfxIntSize aSize,
-                       const gfxMatrix aTransform = gfxMatrix());
-    gfxSurfaceDrawable(mozilla::gfx::DrawTarget* aDT, const gfxIntSize aSize,
-                       const gfxMatrix aTransform = gfxMatrix());
     gfxSurfaceDrawable(mozilla::gfx::SourceSurface* aSurface, const gfxIntSize aSize,
                        const gfxMatrix aTransform = gfxMatrix());
     virtual ~gfxSurfaceDrawable() {}
 
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         bool aRepeat,
                         const GraphicsFilter& aFilter,
                         const gfxMatrix& aTransform = gfxMatrix());
     
-    virtual already_AddRefed<gfxImageSurface> GetAsImageSurface();
-
 protected:
-    nsRefPtr<gfxASurface> mSurface;
-    mozilla::RefPtr<mozilla::gfx::DrawTarget> mDrawTarget;
     mozilla::RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;
     const gfxMatrix mTransform;
 };
 
 /**
  * gfxDrawingCallback
  * A simple drawing functor.
  */
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3061,27 +3061,28 @@ static AntialiasMode Get2DAAMode(gfxFont
 // The TextRunDrawParams are set up once per textrun; the FontDrawParams
 // are dependent on the specific font, so they are set per GlyphRun.
 
 struct TextRunDrawParams {
     RefPtr<DrawTarget>             dt;
     gfxContext                    *context;
     gfxFont::Spacing              *spacing;
     gfxTextRunDrawCallbacks       *callbacks;
-    gfxTextContextPaint           *contextPaint;
+    gfxTextContextPaint           *runContextPaint;
     gfxFloat                       direction;
     double                         devPerApp;
     DrawMode                       drawMode;
     bool                           isRTL;
     bool                           paintSVGGlyphs;
 };
 
 struct FontDrawParams {
     RefPtr<ScaledFont>             scaledFont;
     RefPtr<GlyphRenderingOptions>  renderingOptions;
+    gfxTextContextPaint           *contextPaint;
     Matrix                        *passedInvMatrix;
     Matrix                         matInv;
     double                         synBoldOnePixelOffset;
     int32_t                        extraStrikes;
     DrawOptions                    drawOptions;
     bool                           haveSVGGlyphs;
     bool                           haveColorGlyphs;
 };
@@ -3145,22 +3146,22 @@ private:
 
         gfxContext::AzureState state = mRunParams.context->CurrentState();
         if ((int(mRunParams.drawMode) &
             (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
             (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
             FlushStroke(buf, state);
         }
         if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_FILL)) {
-            if (state.pattern || mRunParams.contextPaint) {
+            if (state.pattern || mFontParams.contextPaint) {
                 Pattern *pat;
 
                 nsRefPtr<gfxPattern> fillPattern;
-                if (!mRunParams.contextPaint ||
-                    !(fillPattern = mRunParams.contextPaint->GetFillPattern(
+                if (!mFontParams.contextPaint ||
+                    !(fillPattern = mFontParams.contextPaint->GetFillPattern(
                                         mRunParams.context->CurrentMatrix()))) {
                     if (state.pattern) {
                         pat = state.pattern->GetPattern(mRunParams.dt,
                                       state.patternTransformChanged ?
                                           &state.patternTransform : nullptr);
                     } else {
                         pat = nullptr;
                     }
@@ -3230,19 +3231,19 @@ private:
 
         mNumGlyphs = 0;
     }
 
     void FlushStroke(gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
     {
         RefPtr<Path> path =
             mFontParams.scaledFont->GetPathForGlyphs(aBuf, mRunParams.dt);
-        if (mRunParams.contextPaint) {
+        if (mFontParams.contextPaint) {
             nsRefPtr<gfxPattern> strokePattern =
-                mRunParams.contextPaint->GetStrokePattern(
+                mFontParams.contextPaint->GetStrokePattern(
                     mRunParams.context->CurrentMatrix());
             if (strokePattern) {
                 mRunParams.dt->Stroke(path,
                                       *strokePattern->GetPattern(mRunParams.dt),
                                       aState.strokeOptions);
             }
         }
     }
@@ -3310,17 +3311,17 @@ gfxFont::DrawOneGlyph(uint32_t aGlyphID,
                    ToDeviceUnits(aPt->y, runParams.devPerApp));
 
     if (fontParams.haveSVGGlyphs) {
         if (!runParams.paintSVGGlyphs) {
             return;
         }
         DrawMode mode = ForcePaintingDrawMode(runParams.drawMode);
         if (RenderSVGGlyph(runParams.context, devPt, mode,
-                           aGlyphID, runParams.contextPaint,
+                           aGlyphID, fontParams.contextPaint,
                            runParams.callbacks, *aEmittedGlyphs)) {
             return;
         }
     }
 
     if (fontParams.haveColorGlyphs &&
         RenderColorGlyph(runParams.context, fontParams.scaledFont,
                          fontParams.renderingOptions, fontParams.drawOptions,
@@ -3414,17 +3415,17 @@ gfxFont::DrawGlyphs(gfxShapedText       
         }
     }
 
     return emittedGlyphs;
 }
 
 void
 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
-              gfxPoint *aPt, TextRunDrawParams& aRunParams)
+              gfxPoint *aPt, const TextRunDrawParams& aRunParams)
 {
     NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
                  !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
 
     if (aStart >= aEnd) {
         return;
     }
@@ -3433,27 +3434,28 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
 
     fontParams.scaledFont = GetScaledFont(aRunParams.dt);
     if (!fontParams.scaledFont) {
         return;
     }
 
     fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
     fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
+    fontParams.contextPaint = aRunParams.runContextPaint;
 
     nsAutoPtr<gfxTextContextPaint> contextPaint;
-    if (fontParams.haveSVGGlyphs && !aRunParams.contextPaint) {
+    if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
         // If no pattern is specified for fill, use the current pattern
         NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
                      "no pattern supplied for stroking text");
         nsRefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
         contextPaint =
             new SimpleTextContextPaint(fillPattern, nullptr,
                                        aRunParams.context->CurrentMatrix());
-        aRunParams.contextPaint = contextPaint;
+        fontParams.contextPaint = contextPaint;
     }
 
     // Synthetic-bold strikes are each offset one device pixel in run direction.
     // (these values are only needed if IsSyntheticBold() is true)
     if (IsSyntheticBold()) {
         double xscale = CalcXScale(aRunParams.context);
         fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
         if (xscale != 0.0) {
@@ -7099,17 +7101,17 @@ gfxTextRun::Draw(gfxContext *aContext, g
     // to draw, regardless of the font used.
     TextRunDrawParams params;
     params.context = aContext;
     params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
     params.isRTL = IsRightToLeft();
     params.direction = direction;
     params.drawMode = aDrawMode;
     params.callbacks = aCallbacks;
-    params.contextPaint = aContextPaint;
+    params.runContextPaint = aContextPaint;
     params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
     params.dt = aContext->GetDrawTarget();
 
     gfxPoint pt = aPt;
 
     // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
     // correctly unless first drawn without alpha
     BufferAlphaColor syntheticBoldBuffer(aContext);
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1767,17 +1767,17 @@ public:
      * Draw a series of glyphs to aContext. The direction of aTextRun must
      * be honoured.
      * @param aStart the first character to draw
      * @param aEnd draw characters up to here
      * @param aPt the baseline origin; the left end of the baseline
      * for LTR textruns, the right end for RTL textruns.
      * On return, this will be updated to the other end of the baseline.
      * In application units, really!
-     * @param aParams record with drawing parameters, see TextRunDrawParams.
+     * @param aRunParams record with drawing parameters, see TextRunDrawParams.
      * Particular fields of interest include
      * .spacing  spacing to insert before and after characters (for RTL
      *   glyphs, before-spacing is inserted to the right of characters). There
      *   are aEnd - aStart elements in this array, unless it's null to indicate
      *   that there is no spacing.
      * .drawMode  specifies whether the fill or stroke of the glyph should be
      *   drawn, or if it should be drawn into the current path
      * .contextPaint  information about how to construct the fill and
@@ -1786,17 +1786,17 @@ public:
      * .context  the Thebes graphics context to which we're drawing
      * .dt  Moz2D DrawTarget to which we're drawing
      *
      * Callers guarantee:
      * -- aStart and aEnd are aligned to cluster and ligature boundaries
      * -- all glyphs use this font
      */
     void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
-              gfxPoint *aPt, TextRunDrawParams& aParams);
+              gfxPoint *aPt, const TextRunDrawParams& aRunParams);
 
     /**
      * Measure a run of characters. See gfxTextRun::Metrics.
      * @param aTight if false, then return the union of the glyph extents
      * with the font-box for the characters (the rectangle with x=0,width=
      * the advance width for the character run,y=-(font ascent), and height=
      * font ascent + font descent). Otherwise, we must return as tight as possible
      * an approximation to the area actually painted by glyphs.
--- a/gfx/thebes/gfxPoint.h
+++ b/gfx/thebes/gfxPoint.h
@@ -14,25 +14,25 @@
 
 #include "gfxTypes.h"
 
 struct gfxSize : public mozilla::gfx::BaseSize<gfxFloat, gfxSize> {
     typedef mozilla::gfx::BaseSize<gfxFloat, gfxSize> Super;
 
     gfxSize() : Super() {}
     gfxSize(gfxFloat aWidth, gfxFloat aHeight) : Super(aWidth, aHeight) {}
-    gfxSize(const nsIntSize& aSize) : Super(aSize.width, aSize.height) {}
+    MOZ_IMPLICIT gfxSize(const nsIntSize& aSize) : Super(aSize.width, aSize.height) {}
 };
 
 struct gfxPoint : public mozilla::gfx::BasePoint<gfxFloat, gfxPoint> {
     typedef mozilla::gfx::BasePoint<gfxFloat, gfxPoint> Super;
 
     gfxPoint() : Super() {}
     gfxPoint(gfxFloat aX, gfxFloat aY) : Super(aX, aY) {}
-    gfxPoint(const nsIntPoint& aPoint) : Super(aPoint.x, aPoint.y) {}
+    MOZ_IMPLICIT gfxPoint(const nsIntPoint& aPoint) : Super(aPoint.x, aPoint.y) {}
 
     bool WithinEpsilonOf(const gfxPoint& aPoint, gfxFloat aEpsilon) {
         return fabs(aPoint.x - x) < aEpsilon && fabs(aPoint.y - y) < aEpsilon;
     }
 };
 
 inline gfxPoint
 operator*(const gfxPoint& aPoint, const gfxSize& aSize)
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -393,38 +393,32 @@ CreateSamplingRestrictedDrawable(gfxDraw
 
     // if 'needed' is empty, nothing will be drawn since aFill
     // must be entirely outside the clip region, so it doesn't
     // matter what we do here, but we should avoid trying to
     // create a zero-size surface.
     if (needed.IsEmpty())
         return nullptr;
 
-    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 {
-      RefPtr<DrawTarget> target =
-        gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
-                                                                     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()));
-      drawable = new gfxSurfaceDrawable(target, size, gfxMatrix().Translate(-needed.TopLeft()));
+    RefPtr<DrawTarget> target =
+      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
+                                                                   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()));
+    RefPtr<SourceSurface> surface = target->Snapshot();
+
+    nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix().Translate(-needed.TopLeft()));
     return drawable.forget();
 }
 #endif // !MOZ_GFX_OPTIMIZE_MOBILE
 
 // working around cairo/pixman bug (bug 364968)
 // Our device-space-to-image-space transform may not be acceptable to pixman.
 struct MOZ_STACK_CLASS AutoCairoPixmanBugWorkaround
 {
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -112,46 +112,46 @@ private:
  * surface.
  */
 class CachedSurface
 {
   ~CachedSurface() {}
 public:
   NS_INLINE_DECL_REFCOUNTING(CachedSurface)
 
-  CachedSurface(DrawTarget*       aTarget,
+  CachedSurface(SourceSurface*    aSurface,
                 const IntSize     aTargetSize,
                 const Cost        aCost,
                 const ImageKey    aImageKey,
                 const SurfaceKey& aSurfaceKey)
-    : mTarget(aTarget)
+    : mSurface(aSurface)
     , mTargetSize(aTargetSize)
     , mCost(aCost)
     , mImageKey(aImageKey)
     , mSurfaceKey(aSurfaceKey)
   {
-    MOZ_ASSERT(mTarget, "Must have a valid DrawTarget");
+    MOZ_ASSERT(mSurface, "Must have a valid SourceSurface");
     MOZ_ASSERT(mImageKey, "Must have a valid image key");
   }
 
   already_AddRefed<gfxDrawable> Drawable() const
   {
     nsRefPtr<gfxDrawable> drawable =
-      new gfxSurfaceDrawable(mTarget, ThebesIntSize(mTargetSize));
+      new gfxSurfaceDrawable(mSurface, ThebesIntSize(mTargetSize));
     return drawable.forget();
   }
 
   ImageKey GetImageKey() const { return mImageKey; }
   SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
   CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
   nsExpirationState* GetExpirationState() { return &mExpirationState; }
 
 private:
   nsExpirationState       mExpirationState;
-  nsRefPtr<DrawTarget>    mTarget;
+  nsRefPtr<SourceSurface> mSurface;
   const IntSize           mTargetSize;
   const Cost              mCost;
   const ImageKey          mImageKey;
   const SurfaceKey        mSurfaceKey;
 };
 
 /*
  * An ImageSurfaceCache is a per-image surface cache. For correctness we must be
@@ -235,31 +235,31 @@ private:
     UnregisterWeakMemoryReporter(this);
   }
 
 public:
   void InitMemoryReporter() {
     RegisterWeakMemoryReporter(this);
   }
 
-  void Insert(DrawTarget*       aTarget,
+  void Insert(SourceSurface*    aSurface,
               IntSize           aTargetSize,
               const Cost        aCost,
               const ImageKey    aImageKey,
               const SurfaceKey& aSurfaceKey)
   {
     MOZ_ASSERT(!Lookup(aImageKey, aSurfaceKey).take(),
                "Inserting a duplicate drawable into the SurfaceCache");
 
     // If this is bigger than the maximum cache size, refuse to cache it.
     if (!CanHold(aCost))
       return;
 
     nsRefPtr<CachedSurface> surface =
-      new CachedSurface(aTarget, aTargetSize, aCost, aImageKey, aSurfaceKey);
+      new CachedSurface(aSurface, aTargetSize, aCost, aImageKey, aSurfaceKey);
 
     // Remove elements in order of cost until we can fit this in the cache.
     while (aCost > mAvailableCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and it still won't fit");
       Remove(mCosts.LastElement().GetSurface());
     }
 
     // Locate the appropriate per-image cache. If there's not an existing cache
@@ -503,25 +503,25 @@ SurfaceCache::Lookup(const ImageKey    a
 {
   MOZ_ASSERT(sInstance, "Should be initialized");
   MOZ_ASSERT(NS_IsMainThread());
 
   return sInstance->Lookup(aImageKey, aSurfaceKey);
 }
 
 /* static */ void
-SurfaceCache::Insert(DrawTarget*       aTarget,
+SurfaceCache::Insert(SourceSurface*    aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
 {
   MOZ_ASSERT(sInstance, "Should be initialized");
   MOZ_ASSERT(NS_IsMainThread());
 
   Cost cost = ComputeCost(aSurfaceKey.Size());
-  return sInstance->Insert(aTarget, aSurfaceKey.Size(), cost, aImageKey,
+  return sInstance->Insert(aSurface, aSurfaceKey.Size(), cost, aImageKey,
                            aSurfaceKey);
 }
 
 /* static */ bool
 SurfaceCache::CanHold(const IntSize& aSize)
 {
   MOZ_ASSERT(sInstance, "Should be initialized");
   MOZ_ASSERT(NS_IsMainThread());
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -130,17 +130,17 @@ struct SurfaceCache
    * without first calling Lookup to verify that the surface is not already in
    * the cache.
    *
    * @param aTarget      The new surface (in the form of a DrawTarget) to insert
    *                     into the cache.
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
    */
-  static void Insert(gfx::DrawTarget*  aTarget,
+  static void Insert(gfx::SourceSurface*  aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey);
 
   /*
    * Checks if a surface of a given size could possibly be stored in the cache.
    * If CanHold() returns false, Insert() will always fail to insert the
    * surface, but the inverse is not true: Insert() may take more information
    * into account than just image size when deciding whether to cache the
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -930,28 +930,30 @@ VectorImage::CreateDrawableAndShow(const
   gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              SurfaceFormat::B8G8R8A8,
                              GraphicsFilter::FILTER_NEAREST, aParams.flags);
 
+  RefPtr<SourceSurface> surface = target->Snapshot();
+
   // Attempt to cache the resulting surface.
-  SurfaceCache::Insert(target,
+  SurfaceCache::Insert(surface,
                        ImageKey(this),
                        SurfaceKey(aParams.imageRect.Size(), aParams.scale,
                                   aParams.svgContext, aParams.animationTime,
                                   aParams.flags));
 
   // Draw. Note that if SurfaceCache::Insert failed for whatever reason,
   // then |target| is all that is keeping the pixel data alive, so we have
   // to draw before returning from this function.
   nsRefPtr<gfxDrawable> drawable =
-    new gfxSurfaceDrawable(target, ThebesIntSize(aParams.imageRect.Size()));
+    new gfxSurfaceDrawable(surface, ThebesIntSize(aParams.imageRect.Size()));
   Show(drawable, aParams);
 }
 
 
 void
 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
 {
   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -22,54 +22,50 @@ namespace JS {
 // This iterator can be used to walk the stack of a thread suspended at an
 // arbitrary pc. To provide acurate results, profiling must have been enabled
 // (via EnableRuntimeProfilingStack) before executing the callstack being
 // unwound.
 class JS_PUBLIC_API(ProfilingFrameIterator)
 {
     js::AsmJSActivation *activation_;
 
-    static const unsigned StorageSpace = 5 * sizeof(void*);
+    static const unsigned StorageSpace = 6 * sizeof(void*);
     mozilla::AlignedStorage<StorageSpace> storage_;
     js::AsmJSProfilingFrameIterator &iter() {
         JS_ASSERT(!done());
         return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
     const js::AsmJSProfilingFrameIterator &iter() const {
         JS_ASSERT(!done());
         return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
 
     void settle();
 
   public:
     struct RegisterState
     {
+        RegisterState() : pc(nullptr), sp(nullptr), lr(nullptr) {}
         void *pc;
         void *sp;
-#if defined(JS_CODEGEN_ARM)
         void *lr;
-#endif
     };
 
     ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state);
     ~ProfilingFrameIterator();
     void operator++();
     bool done() const { return !activation_; }
 
-    enum Kind {
-        Function,
-        AsmJSTrampoline,
-        CppFunction
-    };
-    Kind kind() const;
+    // Assuming the stack grows down (we do), the return value:
+    //  - always points into the stack
+    //  - is weakly monotonically increasing (may be equal for successive frames)
+    //  - will compare greater than newer native and psuedo-stack frame addresses
+    //    and less than older native and psuedo-stack frame addresses
+    void *stackAddress() const;
 
-    // Methods available if kind() == Function:
-    JSAtom *functionDisplayAtom() const;
-    const char *functionFilename() const;
-
-    // Methods available if kind() != Function
-    const char *nonFunctionDescription() const;
+    // Return a label suitable for regexp-matching as performed by
+    // browser/devtools/profiler/cleopatra/js/parserWorker.js
+    const char *label() const;
 };
 
 } // namespace JS
 
 #endif  /* js_ProfilingFrameIterator_h */
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -56,30 +56,33 @@ class ProfileEntry
         // a JS frame is assumed by default. You're not allowed to publicly
         // change the frame type. Instead, call `setJsFrame` or `setCppFrame`.
         IS_CPP_ENTRY = 0x01,
 
         // Indicate that copying the frame label is not necessary when taking a
         // sample of the pseudostack.
         FRAME_LABEL_COPY = 0x02,
 
+        // This ProfileEntry was pushed immediately before calling into asm.js.
+        ASMJS = 0x04,
+
         // Mask for removing all flags except the category information.
-        CATEGORY_MASK = ~IS_CPP_ENTRY & ~FRAME_LABEL_COPY
+        CATEGORY_MASK = ~IS_CPP_ENTRY & ~FRAME_LABEL_COPY & ~ASMJS
     };
 
     MOZ_BEGIN_NESTED_ENUM_CLASS(Category, uint32_t)
-        OTHER    = 0x04,
-        CSS      = 0x08,
-        JS       = 0x10,
-        GC       = 0x20,
-        CC       = 0x40,
-        NETWORK  = 0x80,
-        GRAPHICS = 0x100,
-        STORAGE  = 0x200,
-        EVENTS   = 0x400,
+        OTHER    = 0x08,
+        CSS      = 0x10,
+        JS       = 0x20,
+        GC       = 0x40,
+        CC       = 0x80,
+        NETWORK  = 0x100,
+        GRAPHICS = 0x200,
+        STORAGE  = 0x400,
+        EVENTS   = 0x800,
 
         FIRST    = OTHER,
         LAST     = EVENTS
     MOZ_END_NESTED_ENUM_CLASS(Category)
 
     // All of these methods are marked with the 'volatile' keyword because SPS's
     // representation of the stack is stored such that all ProfileEntry
     // instances are volatile. These methods would not be available unless they
--- a/js/src/jit-test/tests/asm.js/testProfiling.js
+++ b/js/src/jit-test/tests/asm.js/testProfiling.js
@@ -1,122 +1,133 @@
 load(libdir + "asm.js");
 
 // Single-step profiling currently only works in the ARM simulator
 if (!getBuildConfiguration()["arm-simulator"])
     quit();
 
+function assertEqualStacks(got, expect)
+{
+    // Strip off the " (script/library info)"
+    got = String(got).replace(/ \([^\)]*\)/g, "");
+
+    // Shorten FFI/entry trampolines
+    got = got.replace(/FFI trampoline/g, "<").replace(/entry trampoline/g, ">");
+
+    assertEq(got, expect);
+}
+
 // Test profiling enablement while asm.js is running.
 var stacks;
 var ffi = function(enable) {
     if (enable == +1)
         enableSPSProfiling();
     if (enable == -1)
         disableSPSProfiling();
     enableSingleStepProfiling();
     stacks = disableSingleStepProfiling();
 }
 var f = asmLink(asmCompile('global','ffis',USE_ASM + "var ffi=ffis.ffi; function g(i) { i=i|0; ffi(i|0) } function f(i) { i=i|0; g(i|0) } return f"), null, {ffi});
 f(0);
-assertEq(String(stacks), "");
+assertEqualStacks(stacks, "");
 f(+1);
-assertEq(String(stacks), "");
+assertEqualStacks(stacks, "");
 f(0);
-assertEq(String(stacks), "*gf*");
+assertEqualStacks(stacks, "<gf>");
 f(-1);
-assertEq(String(stacks), "*gf*");
+assertEqualStacks(stacks, "<gf>");
 f(0);
-assertEq(String(stacks), "");
+assertEqualStacks(stacks, "");
 
 // Enable profiling for the rest of the tests.
 enableSPSProfiling();
 
 var f = asmLink(asmCompile(USE_ASM + "function f() { return 42 } return f"));
 enableSingleStepProfiling();
 assertEq(f(), 42);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f*,*,");
+assertEqualStacks(stacks, ",>,f>,>,");
 
 var f = asmLink(asmCompile(USE_ASM + "function g(i) { i=i|0; return (i+1)|0 } function f() { return g(42)|0 } return f"));
 enableSingleStepProfiling();
 assertEq(f(), 43);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f*,gf*,f*,*,");
+assertEqualStacks(stacks, ",>,f>,gf>,f>,>,");
 
 var f = asmLink(asmCompile(USE_ASM + "function g1() { return 1 } function g2() { return 2 } function f(i) { i=i|0; return TBL[i&1]()|0 } var TBL=[g1,g2]; return f"));
 enableSingleStepProfiling();
 assertEq(f(0), 1);
 assertEq(f(1), 2);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f*,g1f*,f*,*,,*,f*,g2f*,f*,*,");
+assertEqualStacks(stacks, ",>,f>,g1f>,f>,>,,>,f>,g2f>,f>,>,");
 
 function testBuiltinD2D(name) {
     var f = asmLink(asmCompile('g', USE_ASM + "var fun=g.Math." + name + "; function f(d) { d=+d; return +fun(d) } return f"), this);
     enableSingleStepProfiling();
     assertEq(f(.1), eval("Math." + name + "(.1)"));
     var stacks = disableSingleStepProfiling();
-    assertEq(String(stacks), ",*,f*,Math." + name + "f*,f*,*,");
+    assertEqualStacks(stacks, ",>,f>,Math." + name + "f>,f>,>,");
 }
 for (name of ['sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'ceil', 'floor', 'exp', 'log'])
     testBuiltinD2D(name);
 function testBuiltinF2F(name) {
     var f = asmLink(asmCompile('g', USE_ASM + "var tof=g.Math.fround; var fun=g.Math." + name + "; function f(d) { d=tof(d); return tof(fun(d)) } return f"), this);
     enableSingleStepProfiling();
     assertEq(f(.1), eval("Math.fround(Math." + name + "(Math.fround(.1)))"));
     var stacks = disableSingleStepProfiling();
-    assertEq(String(stacks), ",*,f*,Math." + name + "f*,f*,*,");
+    assertEqualStacks(stacks, ",>,f>,Math." + name + "f>,f>,>,");
 }
 for (name of ['ceil', 'floor'])
     testBuiltinF2F(name);
 function testBuiltinDD2D(name) {
     var f = asmLink(asmCompile('g', USE_ASM + "var fun=g.Math." + name + "; function f(d, e) { d=+d; e=+e; return +fun(d,e) } return f"), this);
     enableSingleStepProfiling();
     assertEq(f(.1, .2), eval("Math." + name + "(.1, .2)"));
     var stacks = disableSingleStepProfiling();
-    assertEq(String(stacks), ",*,f*,Math." + name + "f*,f*,*,");
+    assertEqualStacks(stacks, ",>,f>,Math." + name + "f>,f>,>,");
 }
 for (name of ['atan2', 'pow'])
     testBuiltinDD2D(name);
 
 // FFI tests:
 setJitCompilerOption("ion.usecount.trigger", 10);
 setJitCompilerOption("baseline.usecount.trigger", 0);
 setJitCompilerOption("offthread-compilation.enable", 0);
 
 var ffi1 = function() { return 10 }
 var ffi2 = function() { return 73 }
 var f = asmLink(asmCompile('g','ffis', USE_ASM + "var ffi1=ffis.ffi1, ffi2=ffis.ffi2; function f() { return ((ffi1()|0) + (ffi2()|0))|0 } return f"), null, {ffi1,ffi2});
 // Interpreter FFI exit
 enableSingleStepProfiling();
 assertEq(f(), 83);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f*,*f*,f*,*f*,f*,*,");
+assertEqualStacks(stacks, ",>,f>,<f>,f>,<f>,f>,>,");
 // Ion FFI exit
 for (var i = 0; i < 20; i++)
     assertEq(f(), 83);
 enableSingleStepProfiling();
 assertEq(f(), 83);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f*,*f*,f*,*f*,f*,*,");
+assertEqualStacks(stacks, ",>,f>,<f>,f>,<f>,f>,>,");
 
 var ffi1 = function() { return 15 }
 var ffi2 = function() { return f2() + 17 }
 var {f1,f2} = asmLink(asmCompile('g','ffis', USE_ASM + "var ffi1=ffis.ffi1, ffi2=ffis.ffi2; function f2() { return ffi1()|0 } function f1() { return ffi2()|0 } return {f1:f1, f2:f2}"), null, {ffi1, ffi2});
 // Interpreter FFI exit
 enableSingleStepProfiling();
 assertEq(f1(), 32);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f1*,*f1*,**f1*,f2**f1*,*f2**f1*,f2**f1*,**f1*,*f1*,f1*,*,");
+assertEqualStacks(stacks, ",>,f1>,<f1>,><f1>,f2><f1>,<f2><f1>,f2><f1>,><f1>,<f1>,f1>,>,");
 // Ion FFI exit
 for (var i = 0; i < 20; i++)
     assertEq(f1(), 32);
 enableSingleStepProfiling();
 assertEq(f1(), 32);
 var stacks = disableSingleStepProfiling();
-assertEq(String(stacks), ",*,f1*,*f1*,**f1*,f2**f1*,*f2**f1*,f2**f1*,**f1*,*f1*,f1*,*,");
+assertEqualStacks(stacks, ",>,f1>,<f1>,><f1>,f2><f1>,<f2><f1>,f2><f1>,><f1>,<f1>,f1>,>,");
 
 // This takes forever to run.
 // Stack-overflow exit test
 //var limit = -1;
 //var maxct = 0;
 //function ffi(ct) { if (ct == limit) { enableSingleStepProfiling(); print("enabled"); } maxct = ct; }
 //var f = asmLink(asmCompile('g', 'ffis',USE_ASM + "var ffi=ffis.ffi; var ct=0; function rec(){ ct=(ct+1)|0; ffi(ct|0); rec() } function f() { ct=0; rec() } return f"), null, {ffi});
 //// First find the stack limit:
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1013,16 +1013,20 @@ class MOZ_STACK_CLASS ModuleCompiler
         explicit MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
             u.func = func;
         }
     };
 
   private:
     struct SlowFunction
     {
+        SlowFunction(PropertyName *name, unsigned ms, unsigned line, unsigned column)
+         : name(name), ms(ms), line(line), column(column)
+        {}
+
         PropertyName *name;
         unsigned ms;
         unsigned line;
         unsigned column;
     };
 
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, Global*> GlobalMap;
@@ -1418,37 +1422,35 @@ class MOZ_STACK_CLASS ModuleCompiler
     }
 
     void startFunctionBodies() {
         module_->startFunctionBodies();
     }
     bool finishGeneratingFunction(Func &func, CodeGenerator &codegen,
                                   const AsmJSFunctionLabels &labels)
     {
-        if (!module_->addFunctionCodeRange(func.name(), labels))
+        uint32_t line, column;
+        tokenStream().srcCoords.lineNumAndColumnIndex(func.srcBegin(), &line, &column);
+
+        if (!module_->addFunctionCodeRange(func.name(), line, labels))
             return false;
 
         jit::IonScriptCounts *counts = codegen.extractScriptCounts();
         if (counts && !module_->addFunctionCounts(counts)) {
             js_delete(counts);
             return false;
         }
 
         if (func.compileTime() >= 250) {
-            SlowFunction sf;
-            sf.name = func.name();
-            sf.ms = func.compileTime();
-            tokenStream().srcCoords.lineNumAndColumnIndex(func.srcBegin(), &sf.line, &sf.column);
+            SlowFunction sf(func.name(), func.compileTime(), line, column);
             if (!slowFunctions_.append(sf))
                 return false;
         }
 
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-        uint32_t line, column;
-        tokenStream().srcCoords.lineNumAndColumnIndex(func.srcBegin(), &line, &column);
         unsigned begin = labels.begin.offset();
         unsigned end = labels.end.offset();
         if (!module_->addProfiledFunction(func.name(), begin, end, line, column))
             return false;
 # ifdef JS_ION_PERF
         // Per-block profiling info uses significantly more memory so only store
         // this information if it is actively requested.
         if (PerfBlockEnabled()) {
@@ -5942,23 +5944,29 @@ static const unsigned FramePushedAfterSa
 #else
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
                                              NonVolatileRegs.fpus().getPushSizeInBytes();
 #endif
 
 static bool
 GenerateEntry(ModuleCompiler &m, unsigned exportIndex)
 {
-    PropertyName *funcName = m.module().exportedFunction(exportIndex).name();
-    const ModuleCompiler::Func &func = *m.lookupFunction(funcName);
-
     MacroAssembler &masm = m.masm();
 
     Label begin;
-    GenerateAsmJSEntryPrologue(masm, &begin);
+    masm.align(CodeAlignment);
+    masm.bind(&begin);
+
+#if defined(JS_CODEGEN_ARM)
+    masm.push(lr);
+#elif defined(JS_CODEGEN_MIPS)
+    masm.push(ra);
+#endif
+    masm.subPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
+    masm.setFramePushed(0);
 
     // In constrast to the system ABI, the Ion convention is that all registers
     // are clobbered by calls. Thus, we must save the caller's non-volatile
     // registers.
     masm.PushRegsInMask(NonVolatileRegs);
     JS_ASSERT(masm.framePushed() == FramePushedAfterSave);
 
     // ARM and MIPS have a globally-pinned GlobalReg (x64 uses RIP-relative
@@ -5974,31 +5982,33 @@ GenerateEntry(ModuleCompiler &m, unsigne
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     masm.loadPtr(Address(IntArgReg1, AsmJSModule::heapGlobalDataOffset()), HeapReg);
 #endif
 
     // Remember the stack pointer in the current AsmJSActivation. This will be
     // used by error exit paths to set the stack pointer back to what it was
     // right after the (C++) caller's non-volatile registers were saved so that
     // they can be restored.
-    Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register activation = ABIArgGenerator::NonArgReturnReg0;
     masm.loadAsmJSActivation(activation);
     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()));
 
     // Get 'argv' into a non-arg register and save it on the stack.
-    Register argv = ABIArgGenerator::NonArgReturnVolatileReg0;
-    Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
+    Register argv = ABIArgGenerator::NonArgReturnReg0;
+    Register scratch = ABIArgGenerator::NonArgReturnReg1;
 #if defined(JS_CODEGEN_X86)
     masm.loadPtr(Address(StackPointer, sizeof(AsmJSFrame) + masm.framePushed()), argv);
 #else
     masm.movePtr(IntArgReg0, argv);
 #endif
     masm.Push(argv);
 
     // Bump the stack for the call.
+    PropertyName *funcName = m.module().exportedFunction(exportIndex).name();
+    const ModuleCompiler::Func &func = *m.lookupFunction(funcName);
     unsigned stackDec = StackDecrementForCall(masm, func.sig().args());
     masm.reserveStack(stackDec);
 
     // Copy parameters out of argv and into the registers/stack-slots specified by
     // the system ABI.
     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
         unsigned argOffset = iter.index() * sizeof(uint64_t);
         Address src(argv, argOffset);
@@ -6047,18 +6057,19 @@ GenerateEntry(ModuleCompiler &m, unsigne
         break;
     }
 
     // Restore clobbered non-volatile registers of the caller.
     masm.PopRegsInMask(NonVolatileRegs);
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.move32(Imm32(true), ReturnReg);
-
-    GenerateAsmJSEntryEpilogue(masm);
+    masm.addPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
+    masm.ret();
+
     return m.finishGeneratingEntry(exportIndex, &begin) && !masm.oom();
 }
 
 static void
 FillArgumentArray(ModuleCompiler &m, const VarTypeVector &argTypes,
                   unsigned offsetToArgs, unsigned offsetToCallerStackArgs,
                   Register scratch)
 {
@@ -6117,17 +6128,17 @@ GenerateFFIInterpExit(ModuleCompiler &m,
     unsigned argvBytes = Max<size_t>(1, exit.sig().args().length()) * sizeof(Value);
     unsigned framePushed = StackDecrementForCall(masm, offsetToArgv + argvBytes);
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::FFI, &begin);
 
     // Fill the argument array.
     unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
-    Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register scratch = ABIArgGenerator::NonArgReturnReg0;
     FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch);
 
     // Prepare the arguments for the call to InvokeFromAsmJS_*.
     ABIArgMIRTypeIter i(invokeArgTypes);
 
     // argument 0: exitIndex
     if (i->kind() == ABIArg::GPR)
         masm.mov(ImmWord(exitIndex), i->gpr());
@@ -6236,18 +6247,18 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 
     // 1. Descriptor
     size_t argOffset = offsetToIonArgs;
     uint32_t descriptor = MakeFrameDescriptor(framePushed, JitFrame_Entry);
     masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 2. Callee
-    Register callee = ABIArgGenerator::NonArgReturnVolatileReg0;   // live until call
-    Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;  // clobbered
+    Register callee = ABIArgGenerator::NonArgReturnReg0;   // live until call
+    Register scratch = ABIArgGenerator::NonArgReturnReg1;  // repeatedly clobbered
 
     // 2.1. Get ExitDatum
     unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex);
 #if defined(JS_CODEGEN_X64)
     m.masm().append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
 #elif defined(JS_CODEGEN_X86)
     m.masm().append(AsmJSGlobalAccess(masm.movlWithPatch(Imm32(0), callee), globalDataOffset));
 #else
@@ -6456,16 +6467,20 @@ GenerateFFIExits(ModuleCompiler &m, cons
 }
 
 // Generate a thunk that updates fp before calling the given builtin so that
 // both the builtin and the calling function show up in profiler stacks. (This
 // thunk is dynamically patched in when profiling is enabled.) Since the thunk
 // pushes an AsmJSFrame on the stack, that means we must rebuild the stack
 // frame. Fortunately, these are low arity functions and everything is passed in
 // regs on everything but x86 anyhow.
+//
+// NB: Since this thunk is being injected at system ABI callsites, it must
+//     preserve the argument registers (going in) and the return register
+//     (coming out) and preserve non-volatile registers.
 static bool
 GenerateBuiltinThunk(ModuleCompiler &m, AsmJSExit::BuiltinKind builtin)
 {
     MacroAssembler &masm = m.masm();
     JS_ASSERT(masm.framePushed() == 0);
 
     MIRTypeVector argTypes(m.cx());
     switch (builtin) {
@@ -6505,30 +6520,34 @@ GenerateBuiltinThunk(ModuleCompiler &m, 
         MOZ_ASSUME_UNREACHABLE("Bad builtin");
     }
 
     uint32_t framePushed = StackDecrementForCall(masm, argTypes);
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Builtin(builtin), &begin);
 
-    unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
     for (ABIArgMIRTypeIter i(argTypes); !i.done(); i++) {
         if (i->kind() != ABIArg::Stack)
             continue;
+#if !defined(JS_CODEGEN_ARM)
+        unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
         Address srcAddr(StackPointer, offsetToCallerStackArgs + i->offsetFromArgBase());
         Address dstAddr(StackPointer, i->offsetFromArgBase());
         if (i.mirType() == MIRType_Int32 || i.mirType() == MIRType_Float32) {
-            masm.load32(srcAddr, ABIArgGenerator::NonArgReturnVolatileReg0);
-            masm.store32(ABIArgGenerator::NonArgReturnVolatileReg0, dstAddr);
+            masm.load32(srcAddr, ABIArgGenerator::NonArg_VolatileReg);
+            masm.store32(ABIArgGenerator::NonArg_VolatileReg, dstAddr);
         } else {
             JS_ASSERT(i.mirType() == MIRType_Double);
             masm.loadDouble(srcAddr, ScratchDoubleReg);
             masm.storeDouble(ScratchDoubleReg, dstAddr);
         }
+#else
+        MOZ_CRASH("Architecture should have enough registers for all builtin calls");
+#endif
     }
 
     AssertStackAlignment(masm);
     masm.call(BuiltinToImmKind(builtin));
 
     Label profilingReturn;
     GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Builtin(builtin), &profilingReturn);
     return m.finishGeneratingBuiltinThunk(builtin, &begin, &profilingReturn) && !masm.oom();
@@ -6566,17 +6585,17 @@ GenerateAsyncInterruptExit(ModuleCompile
     // Be very careful here not to perturb the machine state before saving it
     // to the stack. In particular, add/sub instructions may set conditions in
     // the flags register.
     masm.push(Imm32(0));            // space for resumePC
     masm.pushFlags();               // after this we are safe to use sub
     masm.setFramePushed(0);         // set to zero so we can use masm.framePushed() below
     masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except SP)
 
-    Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register scratch = ABIArgGenerator::NonArgReturnReg0;
 
     // Store resumePC into the reserved space.
     masm.loadAsmJSActivation(scratch);
     masm.loadPtr(Address(scratch, AsmJSActivation::offsetOfResumePC()), scratch);
     masm.storePtr(scratch, Address(StackPointer, masm.framePushed() + sizeof(void*)));
 
     // We know that StackPointer is word-aligned, but not necessarily
     // stack-aligned, so we need to align it dynamically.
@@ -6721,17 +6740,17 @@ GenerateThrowStub(ModuleCompiler &m, Lab
 {
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
     masm.bind(throwLabel);
 
     // We are about to pop all frames in this AsmJSActivation. Set fp to null to
     // maintain the invariant that fp is either null or pointing to a valid
     // frame.
-    Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register activation = ABIArgGenerator::NonArgReturnReg0;
     masm.loadAsmJSActivation(activation);
     masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfFP()));
 
     masm.setFramePushed(FramePushedAfterSave);
     masm.loadPtr(Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()), StackPointer);
     masm.PopRegsInMask(NonVolatileRegs);
     JS_ASSERT(masm.framePushed() == 0);
 
--- a/js/src/jit/AsmJSFrameIterator.cpp
+++ b/js/src/jit/AsmJSFrameIterator.cpp
@@ -94,22 +94,22 @@ AsmJSFrameIterator::computeLine(uint32_t
 /*****************************************************************************/
 // Prologue/epilogue code generation
 
 // These constants reflect statically-determined offsets in the profiling
 // prologue/epilogue. The offsets are dynamically asserted during code
 // generation.
 #if defined(JS_CODEGEN_X64)
 static const unsigned PushedRetAddr = 0;
-static const unsigned PushedFP = 11;
-static const unsigned StoredFP = 15;
+static const unsigned PushedFP = 10;
+static const unsigned StoredFP = 14;
 #elif defined(JS_CODEGEN_X86)
 static const unsigned PushedRetAddr = 0;
-static const unsigned PushedFP = 9;
-static const unsigned StoredFP = 12;
+static const unsigned PushedFP = 8;
+static const unsigned StoredFP = 11;
 #elif defined(JS_CODEGEN_ARM)
 static const unsigned PushedRetAddr = 4;
 static const unsigned PushedFP = 16;
 static const unsigned StoredFP = 20;
 #elif defined(JS_CODEGEN_NONE)
 static const unsigned PushedRetAddr = 0;
 static const unsigned PushedFP = 1;
 static const unsigned StoredFP = 1;
@@ -131,78 +131,92 @@ PushRetAddr(MacroAssembler &masm)
 
 // Generate a prologue that maintains AsmJSActivation::fp as the virtual frame
 // pointer so that AsmJSProfilingFrameIterator can walk the stack at any pc in
 // generated code.
 static void
 GenerateProfilingPrologue(MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                           Label *begin)
 {
-    Register act = ABIArgGenerator::NonArgReturnVolatileReg0;
+#if !defined (JS_CODEGEN_ARM)
+    Register scratch = ABIArgGenerator::NonArg_VolatileReg;
+#else
+    // Unfortunately, there are no unused non-arg volatile registers on ARM --
+    // the MacroAssembler claims both lr and ip -- so we use the second scratch
+    // register (lr) and be very careful not to call any methods that use it.
+    Register scratch = lr;
+    masm.setSecondScratchReg(InvalidReg);
+#endif
 
     // AsmJSProfilingFrameIterator needs to know the offsets of several key
     // instructions from 'begin'. To save space, we make these offsets static
     // constants and assert that they match the actual codegen below. On ARM,
     // this requires AutoForbidPools to prevent a constant pool from being
     // randomly inserted between two instructions.
     {
 #if defined(JS_CODEGEN_ARM)
         AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
 #endif
         DebugOnly<uint32_t> offsetAtBegin = masm.currentOffset();
         masm.bind(begin);
 
         PushRetAddr(masm);
         JS_ASSERT(PushedRetAddr == masm.currentOffset() - offsetAtBegin);
 
-        masm.loadAsmJSActivation(act);
-        masm.push(Address(act, AsmJSActivation::offsetOfFP()));
+        masm.loadAsmJSActivation(scratch);
+        masm.push(Address(scratch, AsmJSActivation::offsetOfFP()));
         JS_ASSERT(PushedFP == masm.currentOffset() - offsetAtBegin);
 
-        masm.storePtr(StackPointer, Address(act, AsmJSActivation::offsetOfFP()));
+        masm.storePtr(StackPointer, Address(scratch, AsmJSActivation::offsetOfFP()));
         JS_ASSERT(StoredFP == masm.currentOffset() - offsetAtBegin);
     }
 
     if (reason != AsmJSExit::None)
-        masm.store32(Imm32(reason), Address(act, AsmJSActivation::offsetOfExitReason()));
+        masm.store32_NoSecondScratch(Imm32(reason), Address(scratch, AsmJSActivation::offsetOfExitReason()));
+
+#if defined(JS_CODEGEN_ARM)
+    masm.setSecondScratchReg(lr);
+#endif
 
     if (framePushed)
         masm.subPtr(Imm32(framePushed), StackPointer);
 }
 
 // Generate the inverse of GenerateProfilingPrologue.
 static void
 GenerateProfilingEpilogue(MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                           Label *profilingReturn)
 {
-    Register act = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register scratch = ABIArgGenerator::NonReturn_VolatileReg0;
+#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
+    Register scratch2 = ABIArgGenerator::NonReturn_VolatileReg1;
+#endif
 
     if (framePushed)
         masm.addPtr(Imm32(framePushed), StackPointer);
 
-    masm.loadAsmJSActivation(act);
+    masm.loadAsmJSActivation(scratch);
 
     if (reason != AsmJSExit::None)
-        masm.store32(Imm32(AsmJSExit::None), Address(act, AsmJSActivation::offsetOfExitReason()));
+        masm.store32(Imm32(AsmJSExit::None), Address(scratch, AsmJSActivation::offsetOfExitReason()));
 
     // AsmJSProfilingFrameIterator assumes that there is only a single 'ret'
     // instruction (whose offset is recorded by profilingReturn) after the store
     // which sets AsmJSActivation::fp to the caller's fp. Use AutoForbidPools to
     // ensure that a pool is not inserted before the return (a pool inserts a
     // jump instruction).
     {
 #if defined(JS_CODEGEN_ARM)
         AutoForbidPools afp(&masm, /* number of instructions in scope = */ 3);
 #endif
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        masm.pop(Operand(act, AsmJSActivation::offsetOfFP()));
+#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
+        masm.pop(scratch2);
+        masm.storePtr(scratch2, Address(scratch, AsmJSActivation::offsetOfFP()));
 #else
-        Register fp = ABIArgGenerator::NonArgReturnVolatileReg1;
-        masm.pop(fp);
-        masm.storePtr(fp, Address(act, AsmJSActivation::offsetOfFP()));
+        masm.pop(Operand(scratch, AsmJSActivation::offsetOfFP()));
 #endif
         masm.bind(profilingReturn);
         masm.ret();
     }
 }
 
 // In profiling mode, we need to maintain fp so that we can unwind the stack at
 // any pc. In non-profiling mode, the only way to observe AsmJSActivation::fp is
@@ -310,54 +324,31 @@ js::GenerateAsmJSStackOverflowExit(Macro
     masm.bind(overflowExit);
 
     // If we reach here via the non-profiling prologue, AsmJSActivation::fp has
     // not been updated. To enable stack unwinding from C++, store to it now. If
     // we reached here via the profiling prologue, we'll just store the same
     // value again. Do not update AsmJSFrame::callerFP as it is not necessary in
     // the non-profiling case (there is no return path from this point) and, in
     // the profiling case, it is already correct.
-    Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
+    Register activation = ABIArgGenerator::NonArgReturnReg0;
     masm.loadAsmJSActivation(activation);
     masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfFP()));
 
     // Prepare the stack for calling C++.
     if (unsigned stackDec = StackDecrementForCall(sizeof(AsmJSFrame), ShadowStackSpace))
         masm.subPtr(Imm32(stackDec), StackPointer);
 
     // No need to restore the stack; the throw stub pops everything.
     masm.assertStackAlignment();
     masm.call(AsmJSImmPtr(AsmJSImm_ReportOverRecursed));
     masm.jump(throwLabel);
 }
 
 void
-js::GenerateAsmJSEntryPrologue(MacroAssembler &masm, Label *begin)
-{
-    // Stack-unwinding stops at the entry prologue, so there is no need to
-    // update AsmJSActivation::fp. Furthermore, on ARM/MIPS, GlobalReg is not
-    // yet initialized, so we can't even if we wanted to.
-    masm.align(CodeAlignment);
-    masm.bind(begin);
-    PushRetAddr(masm);
-    masm.subPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
-    masm.setFramePushed(0);
-}
-
-void
-js::GenerateAsmJSEntryEpilogue(MacroAssembler &masm)
-{
-    // Inverse of GenerateAsmJSEntryPrologue:
-    JS_ASSERT(masm.framePushed() == 0);
-    masm.addPtr(Imm32(AsmJSFrameBytesAfterReturnAddress), StackPointer);
-    masm.ret();
-    masm.setFramePushed(0);
-}
-
-void
 js::GenerateAsmJSExitPrologue(MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                               Label *begin)
 {
     masm.align(CodeAlignment);
     GenerateProfilingPrologue(masm, framePushed, reason, begin);
     masm.setFramePushed(framePushed);
 }
 
@@ -373,16 +364,17 @@ js::GenerateAsmJSExitEpilogue(MacroAssem
 
 /*****************************************************************************/
 // AsmJSProfilingFrameIterator
 
 AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation &activation)
   : module_(&activation.module()),
     callerFP_(nullptr),
     callerPC_(nullptr),
+    stackAddress_(nullptr),
     exitReason_(AsmJSExit::None),
     codeRange_(nullptr)
 {
     initFromFP(activation);
 }
 
 static inline void
 AssertMatchesCallSite(const AsmJSModule &module, const AsmJSModule::CodeRange *calleeCodeRange,
@@ -431,16 +423,17 @@ AsmJSProfilingFrameIterator::initFromFP(
     // they properly accumulate self-time) and for this we use the exitReason.
 
     exitReason_ = activation.exitReason();
 
     void *pc = ReturnAddressFromFP(fp);
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(pc);
     JS_ASSERT(codeRange);
     codeRange_ = codeRange;
+    stackAddress_ = fp;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
         callerPC_ = nullptr;
         callerFP_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
         fp = CallerFPFromFP(fp);
@@ -554,16 +547,17 @@ AsmJSProfilingFrameIterator::AsmJSProfil
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
         break;
       }
     }
 
     codeRange_ = codeRange;
+    stackAddress_ = state.sp;
     JS_ASSERT(!done());
 }
 
 void
 AsmJSProfilingFrameIterator::operator++()
 {
     if (exitReason_ != AsmJSExit::None) {
         JS_ASSERT(codeRange_);
@@ -581,134 +575,96 @@ AsmJSProfilingFrameIterator::operator++(
 
     JS_ASSERT(callerPC_);
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(callerPC_);
     JS_ASSERT(codeRange);
     codeRange_ = codeRange;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
+        JS_ASSERT(callerFP_ == nullptr);
+        JS_ASSERT(callerPC_ != nullptr);
         callerPC_ = nullptr;
-        callerFP_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
       case AsmJSModule::CodeRange::FFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
+        stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
         callerFP_ = CallerFPFromFP(callerFP_);
         break;
     }
 
     JS_ASSERT(!done());
 }
 
-AsmJSProfilingFrameIterator::Kind
-AsmJSProfilingFrameIterator::kind() const
-{
-    JS_ASSERT(!done());
-
-    switch (AsmJSExit::ExtractReasonKind(exitReason_)) {
-      case AsmJSExit::Reason_None:
-        break;
-      case AsmJSExit::Reason_Interrupt:
-      case AsmJSExit::Reason_FFI:
-        return JS::ProfilingFrameIterator::AsmJSTrampoline;
-      case AsmJSExit::Reason_Builtin:
-        return JS::ProfilingFrameIterator::CppFunction;
-    }
-
-    auto codeRange = reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_);
-    switch (codeRange->kind()) {
-      case AsmJSModule::CodeRange::Function:
-        return JS::ProfilingFrameIterator::Function;
-      case AsmJSModule::CodeRange::Entry:
-      case AsmJSModule::CodeRange::FFI:
-      case AsmJSModule::CodeRange::Interrupt:
-      case AsmJSModule::CodeRange::Inline:
-        return JS::ProfilingFrameIterator::AsmJSTrampoline;
-      case AsmJSModule::CodeRange::Thunk:
-        return JS::ProfilingFrameIterator::CppFunction;
-    }
-
-    MOZ_ASSUME_UNREACHABLE("Bad kind");
-}
-
-JSAtom *
-AsmJSProfilingFrameIterator::functionDisplayAtom() const
-{
-    JS_ASSERT(kind() == JS::ProfilingFrameIterator::Function);
-    return reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_)->functionName(*module_);
-}
-
-const char *
-AsmJSProfilingFrameIterator::functionFilename() const
-{
-    JS_ASSERT(kind() == JS::ProfilingFrameIterator::Function);
-    return module_->scriptSource()->filename();
-}
-
 static const char *
 BuiltinToName(AsmJSExit::BuiltinKind builtin)
 {
+    // Note: this label is regexp-matched by
+    // browser/devtools/profiler/cleopatra/js/parserWorker.js.
+
     switch (builtin) {
-      case AsmJSExit::Builtin_ToInt32:   return "ToInt32";
+      case AsmJSExit::Builtin_ToInt32:   return "ToInt32 (in asm.js)";
 #if defined(JS_CODEGEN_ARM)
-      case AsmJSExit::Builtin_IDivMod:   return "software idivmod";
-      case AsmJSExit::Builtin_UDivMod:   return "software uidivmod";
+      case AsmJSExit::Builtin_IDivMod:   return "software idivmod (in asm.js)";
+      case AsmJSExit::Builtin_UDivMod:   return "software uidivmod (in asm.js)";
 #endif
-      case AsmJSExit::Builtin_ModD:      return "fmod";
-      case AsmJSExit::Builtin_SinD:      return "Math.sin";
-      case AsmJSExit::Builtin_CosD:      return "Math.cos";
-      case AsmJSExit::Builtin_TanD:      return "Math.tan";
-      case AsmJSExit::Builtin_ASinD:     return "Math.asin";
-      case AsmJSExit::Builtin_ACosD:     return "Math.acos";
-      case AsmJSExit::Builtin_ATanD:     return "Math.atan";
+      case AsmJSExit::Builtin_ModD:      return "fmod (in asm.js)";
+      case AsmJSExit::Builtin_SinD:      return "Math.sin (in asm.js)";
+      case AsmJSExit::Builtin_CosD:      return "Math.cos (in asm.js)";
+      case AsmJSExit::Builtin_TanD:      return "Math.tan (in asm.js)";
+      case AsmJSExit::Builtin_ASinD:     return "Math.asin (in asm.js)";
+      case AsmJSExit::Builtin_ACosD:     return "Math.acos (in asm.js)";
+      case AsmJSExit::Builtin_ATanD:     return "Math.atan (in asm.js)";
       case AsmJSExit::Builtin_CeilD:
-      case AsmJSExit::Builtin_CeilF:     return "Math.ceil";
+      case AsmJSExit::Builtin_CeilF:     return "Math.ceil (in asm.js)";
       case AsmJSExit::Builtin_FloorD:
-      case AsmJSExit::Builtin_FloorF:    return "Math.floor";
-      case AsmJSExit::Builtin_ExpD:      return "Math.exp";
-      case AsmJSExit::Builtin_LogD:      return "Math.log";
-      case AsmJSExit::Builtin_PowD:      return "Math.pow";
-      case AsmJSExit::Builtin_ATan2D:    return "Math.atan2";
+      case AsmJSExit::Builtin_FloorF:    return "Math.floor (in asm.js)";
+      case AsmJSExit::Builtin_ExpD:      return "Math.exp (in asm.js)";
+      case AsmJSExit::Builtin_LogD:      return "Math.log (in asm.js)";
+      case AsmJSExit::Builtin_PowD:      return "Math.pow (in asm.js)";
+      case AsmJSExit::Builtin_ATan2D:    return "Math.atan2 (in asm.js)";
       case AsmJSExit::Builtin_Limit:     break;
     }
     MOZ_ASSUME_UNREACHABLE("Bad builtin kind");
 }
 
 const char *
-AsmJSProfilingFrameIterator::nonFunctionDescription() const
+AsmJSProfilingFrameIterator::label() const
 {
     JS_ASSERT(!done());
-    JS_ASSERT(kind() != JS::ProfilingFrameIterator::Function);
+
+    // Note: this label is regexp-matched by
+    // browser/devtools/profiler/cleopatra/js/parserWorker.js.
 
     // Use the same string for both time inside and under so that the two
     // entries will be coalesced by the profiler.
-    const char *ffiDescription = "asm.js FFI trampoline";
-    const char *interruptDescription = "asm.js slow script interrupt";
+    const char *ffiDescription = "FFI trampoline (in asm.js)";
+    const char *interruptDescription = "slow script interrupt trampoline (in asm.js)";
 
     switch (AsmJSExit::ExtractReasonKind(exitReason_)) {
       case AsmJSExit::Reason_None:
         break;
       case AsmJSExit::Reason_FFI:
         return ffiDescription;
       case AsmJSExit::Reason_Interrupt:
         return interruptDescription;
       case AsmJSExit::Reason_Builtin:
         return BuiltinToName(AsmJSExit::ExtractBuiltinKind(exitReason_));
     }
 
     auto codeRange = reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_);
     switch (codeRange->kind()) {
-      case AsmJSModule::CodeRange::Function:  MOZ_ASSUME_UNREACHABLE("non-functions only");
-      case AsmJSModule::CodeRange::Entry:     return "asm.js entry trampoline";
+      case AsmJSModule::CodeRange::Function:  return codeRange->functionProfilingLabel(*module_);
+      case AsmJSModule::CodeRange::Entry:     return "entry trampoline (in asm.js)";
       case AsmJSModule::CodeRange::FFI:       return ffiDescription;
       case AsmJSModule::CodeRange::Interrupt: return interruptDescription;
-      case AsmJSModule::CodeRange::Inline:    return "asm.js inline stub";
+      case AsmJSModule::CodeRange::Inline:    return "inline stub (in asm.js)";
       case AsmJSModule::CodeRange::Thunk:     return BuiltinToName(codeRange->thunkTarget());
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad exit kind");
 }
 
--- a/js/src/jit/AsmJSFrameIterator.h
+++ b/js/src/jit/AsmJSFrameIterator.h
@@ -110,61 +110,51 @@ namespace AsmJSExit
 // Iterates over the frames of a single AsmJSActivation, given an
 // asynchrously-interrupted thread's state. If the activation's
 // module is not in profiling mode, the activation is skipped.
 class AsmJSProfilingFrameIterator
 {
     const AsmJSModule *module_;
     uint8_t *callerFP_;
     void *callerPC_;
+    void *stackAddress_;
     AsmJSExit::Reason exitReason_;
 
     // Really, a const AsmJSModule::CodeRange*, but no forward declarations of
     // nested classes, so use void* to avoid pulling in all of AsmJSModule.h.
     const void *codeRange_;
 
     void initFromFP(const AsmJSActivation &activation);
 
   public:
     AsmJSProfilingFrameIterator() : codeRange_(nullptr) {}
     AsmJSProfilingFrameIterator(const AsmJSActivation &activation);
     AsmJSProfilingFrameIterator(const AsmJSActivation &activation,
                                 const JS::ProfilingFrameIterator::RegisterState &state);
     void operator++();
     bool done() const { return !codeRange_; }
 
-    typedef JS::ProfilingFrameIterator::Kind Kind;
-    Kind kind() const;
-
-    JSAtom *functionDisplayAtom() const;
-    const char *functionFilename() const;
-    unsigned functionLine() const;
-
-    const char *nonFunctionDescription() const;
+    void *stackAddress() const { JS_ASSERT(!done()); return stackAddress_; }
+    const char *label() const;
 };
 
 /******************************************************************************/
 // Prologue/epilogue code generation.
 
 void
 GenerateAsmJSFunctionPrologue(jit::MacroAssembler &masm, unsigned framePushed,
                               AsmJSFunctionLabels *labels);
 void
 GenerateAsmJSFunctionEpilogue(jit::MacroAssembler &masm, unsigned framePushed,
                               AsmJSFunctionLabels *labels);
 void
 GenerateAsmJSStackOverflowExit(jit::MacroAssembler &masm, jit::Label *overflowExit,
                                jit::Label *throwLabel);
 
 void
-GenerateAsmJSEntryPrologue(jit::MacroAssembler &masm, jit::Label *begin);
-void
-GenerateAsmJSEntryEpilogue(jit::MacroAssembler &masm);
-
-void
 GenerateAsmJSExitPrologue(jit::MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                           jit::Label *begin);
 void
 GenerateAsmJSExitEpilogue(jit::MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                           jit::Label *profilingReturn);
 
 
 } // namespace js
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -1168,18 +1168,20 @@ AsmJSModule::ExportedFunction::clone(Exc
 
     if (!ClonePodVector(cx, argCoercions_, &out->argCoercions_))
         return false;
 
     out->pod = pod;
     return true;
 }
 
-AsmJSModule::CodeRange::CodeRange(uint32_t nameIndex, const AsmJSFunctionLabels &l)
+AsmJSModule::CodeRange::CodeRange(uint32_t nameIndex, uint32_t lineNumber,
+                                  const AsmJSFunctionLabels &l)
   : nameIndex_(nameIndex),
+    lineNumber_(lineNumber),
     begin_(l.begin.offset()),
     profilingReturn_(l.profilingReturn.offset()),
     end_(l.end.offset())
 {
     u.kind_ = Function;
     setDeltas(l.entry.offset(), l.profilingJump.offset(), l.profilingEpilogue.offset());
 
     JS_ASSERT(l.begin.offset() < l.entry.offset());
@@ -1530,16 +1532,39 @@ AsmJSModule::clone(JSContext *cx, Scoped
 void
 AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx)
 {
     JS_ASSERT(isDynamicallyLinked());
 
     if (profilingEnabled_ == enabled)
         return;
 
+    // When enabled, generate profiling labels for every name in names_ that is
+    // the name of some Function CodeRange. This involves malloc() so do it now
+    // since, once we start sampling, we'll be in a signal-handing context where
+    // we cannot malloc.
+    if (enabled) {
+        profilingLabels_.resize(names_.length());
+        const char *filename = scriptSource_->filename();
+        JS::AutoCheckCannotGC nogc;
+        for (size_t i = 0; i < codeRanges_.length(); i++) {
+            CodeRange &cr = codeRanges_[i];
+            if (!cr.isFunction())
+                continue;
+            unsigned lineno = cr.functionLineNumber();
+            PropertyName *name = names_[cr.functionNameIndex()].name();
+            profilingLabels_[cr.functionNameIndex()].reset(
+                name->hasLatin1Chars()
+                ? JS_smprintf("%s (%s:%u)", name->latin1Chars(nogc), filename, lineno)
+                : JS_smprintf("%hs (%s:%u)", name->twoByteChars(nogc), filename, lineno));
+        }
+    } else {
+        profilingLabels_.clear();
+    }
+
     // Conservatively flush the icache for the entire module.
     AutoFlushICache afc("AsmJSModule::setProfilingEnabled");
     setAutoFlushICacheRange();
 
     // To enable profiling, we need to patch 3 kinds of things:
     AutoUnprotectCode auc(cx, *this);
 
     // Patch all internal (asm.js->asm.js) callsites to call the profiling
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -330,16 +330,17 @@ class AsmJSModule
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, ExportedFunction *out) const;
     };
 
     class CodeRange
     {
         uint32_t nameIndex_;
+        uint32_t lineNumber_;
         uint32_t begin_;
         uint32_t profilingReturn_;
         uint32_t end_;
         union {
             struct {
                 uint8_t kind_;
                 uint8_t beginToEntry_;
                 uint8_t profilingJumpToProfilingReturn_;
@@ -353,17 +354,17 @@ class AsmJSModule
         } u;
 
         void setDeltas(uint32_t entry, uint32_t profilingJump, uint32_t profilingEpilogue);
 
       public:
         enum Kind { Function, Entry, FFI, Interrupt, Thunk, Inline };
 
         CodeRange() {}
-        CodeRange(uint32_t nameIndex, const AsmJSFunctionLabels &l);
+        CodeRange(uint32_t nameIndex, uint32_t lineNumber, const AsmJSFunctionLabels &l);
         CodeRange(Kind kind, uint32_t begin, uint32_t end);
         CodeRange(Kind kind, uint32_t begin, uint32_t profilingReturn, uint32_t end);
         CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin, uint32_t pret, uint32_t end);
         void updateOffsets(jit::MacroAssembler &masm);
 
         Kind kind() const { return Kind(u.kind_); }
         bool isFunction() const { return kind() == Function; }
         bool isEntry() const { return kind() == Entry; }
@@ -388,20 +389,32 @@ class AsmJSModule
         uint32_t profilingEpilogue() const {
             JS_ASSERT(isFunction());
             return profilingReturn_ - u.func.profilingEpilogueToProfilingReturn_;
         }
         uint32_t profilingReturn() const {
             JS_ASSERT(isFunction() || isFFI() || isInterrupt() || isThunk());
             return profilingReturn_;
         }
+        uint32_t functionNameIndex() const {
+            JS_ASSERT(isFunction());
+            return nameIndex_;
+        }
         PropertyName *functionName(const AsmJSModule &module) const {
             JS_ASSERT(isFunction());
             return module.names_[nameIndex_].name();
         }
+        const char *functionProfilingLabel(const AsmJSModule &module) const {
+            JS_ASSERT(isFunction());
+            return module.profilingLabels_[nameIndex_].get();
+        }
+        uint32_t functionLineNumber() const {
+            JS_ASSERT(isFunction());
+            return lineNumber_;
+        }
         AsmJSExit::BuiltinKind thunkTarget() const {
             JS_ASSERT(isThunk());
             return AsmJSExit::BuiltinKind(u.thunk.target_);
         }
     };
 
     class FuncPtrTable
     {
@@ -425,16 +438,18 @@ class AsmJSModule
         PropertyName *name() const { return name_; }
         PropertyName *&name() { return name_; }
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, Name *out) const;
     };
 
+    typedef mozilla::UniquePtr<char, JS::FreePolicy> ProfilingLabel;
+
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     // Function information to add to the VTune JIT profiler following linking.
     struct ProfiledFunction
     {
         PropertyName *name;
         struct Pod {
             unsigned startCodeOffset;
             unsigned endCodeOffset;
@@ -598,16 +613,17 @@ class AsmJSModule
     Vector<Global,                 0, SystemAllocPolicy> globals_;
     Vector<Exit,                   0, SystemAllocPolicy> exits_;
     Vector<ExportedFunction,       0, SystemAllocPolicy> exports_;
     Vector<jit::CallSite,          0, SystemAllocPolicy> callSites_;
     Vector<CodeRange,              0, SystemAllocPolicy> codeRanges_;
     Vector<FuncPtrTable,           0, SystemAllocPolicy> funcPtrTables_;
     Vector<uint32_t,               0, SystemAllocPolicy> builtinThunkOffsets_;
     Vector<Name,                   0, SystemAllocPolicy> names_;
+    Vector<ProfilingLabel,         0, SystemAllocPolicy> profilingLabels_;
     Vector<jit::AsmJSHeapAccess,   0, SystemAllocPolicy> heapAccesses_;
     Vector<jit::IonScriptCounts*,  0, SystemAllocPolicy> functionCounts_;
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     Vector<ProfiledFunction,       0, SystemAllocPolicy> profiledFunctions_;
 #endif
 #if defined(JS_ION_PERF)
     Vector<ProfiledBlocksFunction, 0, SystemAllocPolicy> perfProfiledBlocksFunctions_;
 #endif
@@ -796,23 +812,25 @@ class AsmJSModule
     /*************************************************************************/
     // These functions are called while parsing/compiling function bodies:
 
     void requireHeapLengthToBeAtLeast(uint32_t len) {
         JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         if (len > pod.minHeapLength_)
             pod.minHeapLength_ = len;
     }
-    bool addFunctionCodeRange(PropertyName *name, const AsmJSFunctionLabels &labels) {
+    bool addFunctionCodeRange(PropertyName *name, uint32_t lineNumber,
+                              const AsmJSFunctionLabels &labels)
+    {
         JS_ASSERT(!isFinished());
         JS_ASSERT(name->isTenured());
         if (names_.length() >= UINT32_MAX)
             return false;
         uint32_t nameIndex = names_.length();
-        return names_.append(name) && codeRanges_.append(CodeRange(nameIndex, labels));
+        return names_.append(name) && codeRanges_.append(CodeRange(nameIndex, lineNumber, labels));
     }
     bool addEntryCodeRange(uint32_t begin, uint32_t end) {
         return codeRanges_.append(CodeRange(CodeRange::Entry, begin, end));
     }
     bool addFFICodeRange(uint32_t begin, uint32_t pret, uint32_t end) {
         return codeRanges_.append(CodeRange(CodeRange::FFI, begin, pret, end));
     }
     bool addInterruptCodeRange(uint32_t begin, uint32_t pret, uint32_t end) {
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1285,37 +1285,37 @@ OptimizeMIR(MIRGenerator *mir)
     }
 
     IonSpewPass("BuildSSA");
     AssertBasicGraphCoherency(graph);
 
     if (mir->shouldCancel("Start"))
         return false;
 
+    if (!mir->compilingAsmJS()) {
+        AutoTraceLog log(logger, TraceLogger::FoldTests);
+        FoldTests(graph);
+        IonSpewPass("Fold Tests");
+        AssertBasicGraphCoherency(graph);
+
+        if (mir->shouldCancel("Fold Tests"))
+            return false;
+    }
+
     {
         AutoTraceLog log(logger, TraceLogger::SplitCriticalEdges);
         if (!SplitCriticalEdges(graph))
             return false;
         IonSpewPass("Split Critical Edges");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Split Critical Edges"))
             return false;
     }
 
-    if (!mir->compilingAsmJS()) {
-        AutoTraceLog log(logger, TraceLogger::FoldTests);
-        FoldTests(graph);
-        IonSpewPass("Fold Tests");
-        AssertBasicGraphCoherency(graph);
-
-        if (mir->shouldCancel("Fold Tests"))
-            return false;
-    }
-
     {
         AutoTraceLog log(logger, TraceLogger::RenumberBlocks);
         if (!RenumberBlocks(graph))
             return false;
         IonSpewPass("Renumber Blocks");
         AssertGraphCoherency(graph);
 
         if (mir->shouldCancel("Renumber Blocks"))
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -199,16 +199,23 @@ MaybeFoldConditionBlock(MIRGraph &graph,
     if (falseBranch->numPredecessors() != 1 || falseBranch->numSuccessors() != 1)
         return;
     MBasicBlock *testBlock = trueBranch->getSuccessor(0);
     if (testBlock != falseBranch->getSuccessor(0))
         return;
     if (testBlock->numPredecessors() != 2)
         return;
 
+    if (initialBlock->isLoopBackedge() || trueBranch->isLoopBackedge() || falseBranch->isLoopBackedge())
+        return;
+
+    // Make sure the test block does not have any outgoing loop backedges.
+    if (!SplitCriticalEdgesForBlock(graph, testBlock))
+        CrashAtUnhandlableOOM("MaybeFoldConditionBlock");
+
     MPhi *phi;
     MTest *finalTest;
     if (!BlockIsSingleTest(testBlock, &phi, &finalTest))
         return;
 
     if (&testBlock->info() != &initialBlock->info() ||
         &trueBranch->info() != &initialBlock->info() ||
         &falseBranch->info() != &initialBlock->info())
@@ -268,24 +275,16 @@ MaybeFoldConditionBlock(MIRGraph &graph,
     // Short circuit the initial test to skip any constant branch eliminated above.
     UpdateTestSuccessors(graph.alloc(), initialBlock, initialTest->input(),
                          trueTarget, falseTarget, testBlock);
 
     // Remove testBlock itself.
     finalTest->ifTrue()->removePredecessor(testBlock);
     finalTest->ifFalse()->removePredecessor(testBlock);
     graph.removeBlock(testBlock);
-
-    // Split any new critical edges which were introduced.
-    if (!SplitCriticalEdgesForBlock(graph, initialBlock) ||
-        (trueTarget == trueBranch && !SplitCriticalEdgesForBlock(graph, trueBranch)) ||
-        (falseTarget == falseBranch && !SplitCriticalEdgesForBlock(graph, falseBranch)))
-    {
-        CrashAtUnhandlableOOM("MaybeFoldConditionBlock");
-    }
 }
 
 static void
 MaybeFoldAndOrBlock(MIRGraph &graph, MBasicBlock *initialBlock)
 {
     // Optimize the MIR graph to improve the code generated for && and ||
     // operations when they are used in tests. This is very similar to the
     // above method for folding condition blocks, though the two are
@@ -318,16 +317,23 @@ MaybeFoldAndOrBlock(MIRGraph &graph, MBa
         testBlock = initialTest->ifTrue();
     }
 
     if (branchBlock->numSuccessors() != 1 || branchBlock->getSuccessor(0) != testBlock)
         return;
     if (branchBlock->numPredecessors() != 1 || testBlock->numPredecessors() != 2)
         return;
 
+    if (initialBlock->isLoopBackedge() || branchBlock->isLoopBackedge())
+        return;
+
+    // Make sure the test block does not have any outgoing loop backedges.
+    if (!SplitCriticalEdgesForBlock(graph, testBlock))
+        CrashAtUnhandlableOOM("MaybeFoldAndOrBlock");
+
     MPhi *phi;
     MTest *finalTest;
     if (!BlockIsSingleTest(testBlock, &phi, &finalTest))
         return;
 
     if (&testBlock->info() != &initialBlock->info() || &branchBlock->info() != &initialBlock->info())
         return;
 
@@ -361,23 +367,16 @@ MaybeFoldAndOrBlock(MIRGraph &graph, MBa
 
     UpdateTestSuccessors(graph.alloc(), branchBlock, branchResult,
                          finalTest->ifTrue(), finalTest->ifFalse(), testBlock);
 
     // Remove testBlock itself.
     finalTest->ifTrue()->removePredecessor(testBlock);
     finalTest->ifFalse()->removePredecessor(testBlock);
     graph.removeBlock(testBlock);
-
-    // Split any new critical edges which were introduced.
-    if (!SplitCriticalEdgesForBlock(graph, initialBlock) ||
-        !SplitCriticalEdgesForBlock(graph, branchBlock))
-    {
-        CrashAtUnhandlableOOM("MaybeFoldConditionBlock");
-    }
 }
 
 void
 jit::FoldTests(MIRGraph &graph)
 {
     for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
         MaybeFoldConditionBlock(graph, *block);
         MaybeFoldAndOrBlock(graph, *block);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1496,16 +1496,20 @@ MBinaryArithInstruction::foldsTo(TempAll
         return rhs; // x op id => x
 
     return this;
 }
 
 void
 MBinaryArithInstruction::trySpecializeFloat32(TempAllocator &alloc)
 {
+    // Do not use Float32 if we can use int32.
+    if (specialization_ == MIRType_Int32)
+        return;
+
     MDefinition *left = lhs();
     MDefinition *right = rhs();
 
     if (!left->canProduceFloat32() || !right->canProduceFloat32()
         || !CheckUsesAreFloat32Consumers(this))
     {
         if (left->type() == MIRType_Float32)
             ConvertDefinitionToDouble<0>(alloc, left, this);
@@ -1522,16 +1526,20 @@ bool
 MAbs::fallible() const
 {
     return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
 }
 
 void
 MAbs::trySpecializeFloat32(TempAllocator &alloc)
 {
+    // Do not use Float32 if we can use int32.
+    if (input()->type() == MIRType_Int32)
+        return;
+
     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
         if (input()->type() == MIRType_Float32)
             ConvertDefinitionToDouble<0>(alloc, input(), this);
         return;
     }
 
     setResultType(MIRType_Float32);
     specialization_ = MIRType_Float32;
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -72,18 +72,21 @@ ABIArgGenerator::next(MIRType type)
         floatRegIndex_+=2;
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
 
     return current_;
 }
-const Register ABIArgGenerator::NonArgReturnVolatileReg0 = r4;
-const Register ABIArgGenerator::NonArgReturnVolatileReg1 = r5;
+
+const Register ABIArgGenerator::NonArgReturnReg0 = r4;
+const Register ABIArgGenerator::NonArgReturnReg1 = r5;
+const Register ABIArgGenerator::NonReturn_VolatileReg0 = r2;
+const Register ABIArgGenerator::NonReturn_VolatileReg1 = r3;
 
 // Encode a standard register when it is being used as src1, the dest, and an
 // extra register. These should never be called with an InvalidReg.
 uint32_t
 js::jit::RT(Register r)
 {
     JS_ASSERT((r.code() & ~0xf) == 0);
     return r.code() << 12;
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -58,30 +58,33 @@ static MOZ_CONSTEXPR_VAR Register IntArg
 static MOZ_CONSTEXPR_VAR Register IntArgReg1 = r1;
 static MOZ_CONSTEXPR_VAR Register IntArgReg2 = r2;
 static MOZ_CONSTEXPR_VAR Register IntArgReg3 = r3;
 static MOZ_CONSTEXPR_VAR Register GlobalReg = r10;
 static MOZ_CONSTEXPR_VAR Register HeapReg = r11;
 static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { r5, r6, r7, r8 };
 static const uint32_t NumCallTempNonArgRegs =
     mozilla::ArrayLength(CallTempNonArgRegs);
+
 class ABIArgGenerator
 {
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
     uint32_t stackOffset_;
     ABIArg current_;
 
   public:
     ABIArgGenerator();
     ABIArg next(MIRType argType);
     ABIArg &current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
-    static const Register NonArgReturnVolatileReg0;
-    static const Register NonArgReturnVolatileReg1;
+    static const Register NonArgReturnReg0;
+    static const Register NonArgReturnReg1;
+    static const Register NonReturn_VolatileReg0;
+    static const Register NonReturn_VolatileReg1;
 };
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = r1;
 
 static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg;
 
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = r3;
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2453,16 +2453,23 @@ MacroAssemblerARMCompat::store32(Registe
     if (dest.offset != 0) {
         ma_add(base, Imm32(dest.offset), ScratchRegister);
         base = ScratchRegister;
     }
     ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
 }
 
 void
+MacroAssemblerARMCompat::store32_NoSecondScratch(Imm32 src, const Address &address)
+{
+    move32(src, ScratchRegister);
+    storePtr(ScratchRegister, address);
+}
+
+void
 MacroAssemblerARMCompat::storePtr(ImmWord imm, const Address &address)
 {
     movePtr(imm, ScratchRegister);
     storePtr(ScratchRegister, address);
 }
 
 void
 MacroAssemblerARMCompat::storePtr(ImmPtr imm, const Address &address)
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1353,16 +1353,18 @@ class MacroAssemblerARMCompat : public M
     void store16(Imm32 imm, const BaseIndex &address);
 
     void store32(Register src, AbsoluteAddress address);
     void store32(Register src, const Address &address);
     void store32(Register src, const BaseIndex &address);
     void store32(Imm32 src, const Address &address);
     void store32(Imm32 src, const BaseIndex &address);
 
+    void store32_NoSecondScratch(Imm32 src, const Address &address);
+
     void storePtr(ImmWord imm, const Address &address);
     void storePtr(ImmPtr imm, const Address &address);
     void storePtr(ImmGCPtr imm, const Address &address);
     void storePtr(Register src, const Address &address);
     void storePtr(Register src, const BaseIndex &address);
     void storePtr(Register src, AbsoluteAddress dest);
     void storeDouble(FloatRegister src, Address addr) {
         ma_vstr(src, Operand(addr));
--- a/js/src/jit/mips/Assembler-mips.cpp
+++ b/js/src/jit/mips/Assembler-mips.cpp
@@ -59,18 +59,21 @@ ABIArgGenerator::next(MIRType type)
         }
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
     return current_;
 
 }
-const Register ABIArgGenerator::NonArgReturnVolatileReg0 = t0;
-const Register ABIArgGenerator::NonArgReturnVolatileReg1 = t1;
+const Register ABIArgGenerator::NonArgReturnReg0 = t0;
+const Register ABIArgGenerator::NonArgReturnReg1 = t1;
+const Register ABIArgGenerator::NonArg_VolatileReg = v0;
+const Register ABIArgGenerator::NonReturn_VolatileReg0 = a0;
+const Register ABIArgGenerator::NonReturn_VolatileReg1 = a1;
 
 // Encode a standard register when it is being used as rd, the rs, and
 // an extra register(rt). These should never be called with an InvalidReg.
 uint32_t
 js::jit::RS(Register r)
 {
     MOZ_ASSERT((r.code() & ~RegMask) == 0);
     return r.code() << RSShift;
--- a/js/src/jit/mips/Assembler-mips.h
+++ b/js/src/jit/mips/Assembler-mips.h
@@ -89,18 +89,21 @@ class ABIArgGenerator
 
     uint32_t stackBytesConsumedSoFar() const {
         if (usedArgSlots_ <= 4)
             return ShadowStackSpace;
 
         return usedArgSlots_ * sizeof(intptr_t);
     }
 
-    static const Register NonArgReturnVolatileReg0;
-    static const Register NonArgReturnVolatileReg1;
+    static const Register NonArgReturnReg0;
+    static const Register NonArgReturnReg1;
+    static const Register NonArg_VolatileReg;
+    static const Register NonReturn_VolatileReg0;
+    static const Register NonReturn_VolatileReg1;
 };
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1;
 
 static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg };
 
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = a3;
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -378,16 +378,20 @@ class MacroAssemblerX86Shared : public A
     }
     void load32(const Operand &src, Register dest) {
         movl(src, dest);
     }
     template <typename S, typename T>
     void store32(const S &src, const T &dest) {
         movl(src, Operand(dest));
     }
+    template <typename S, typename T>
+    void store32_NoSecondScratch(const S &src, const T &dest) {
+        store32(src, dest);
+    }
     void loadDouble(const Address &src, FloatRegister dest) {
         movsd(src, dest);
     }
     void loadDouble(const BaseIndex &src, FloatRegister dest) {
         movsd(src, dest);
     }
     void loadDouble(const Operand &src, FloatRegister dest) {
         switch (src.kind()) {
--- a/js/src/jit/x64/Assembler-x64.cpp
+++ b/js/src/jit/x64/Assembler-x64.cpp
@@ -70,19 +70,21 @@ ABIArgGenerator::next(MIRType type)
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
     return current_;
 #endif
 }
 
 // Avoid r11, which is the MacroAssembler's ScratchReg.
-const Register ABIArgGenerator::NonArgReturnVolatileReg0 = r10;
-const Register ABIArgGenerator::NonArgReturnVolatileReg1 = r12;
+const Register ABIArgGenerator::NonArgReturnReg0 = r10;
+const Register ABIArgGenerator::NonArgReturnReg1 = r12;
 const Register ABIArgGenerator::NonVolatileReg = r13;
+const Register ABIArgGenerator::NonArg_VolatileReg = rax;
+const Register ABIArgGenerator::NonReturn_VolatileReg0 = rcx;
 
 void
 Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc)
 {
     if (!jumpRelocations_.length()) {
         // The jump relocation table starts with a fixed-width integer pointing
         // to the start of the extended jump table. But, we don't know the
         // actual extended jump table offset yet, so write a 0 which we'll
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -165,19 +165,21 @@ class ABIArgGenerator
 
   public:
     ABIArgGenerator();
     ABIArg next(MIRType argType);
     ABIArg &current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
 
     // Note: these registers are all guaranteed to be different
-    static const Register NonArgReturnVolatileReg0;
-    static const Register NonArgReturnVolatileReg1;
+    static const Register NonArgReturnReg0;
+    static const Register NonArgReturnReg1;
     static const Register NonVolatileReg;
+    static const Register NonArg_VolatileReg;
+    static const Register NonReturn_VolatileReg0;
 };
 
 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = IntArgReg3;
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = rdx;
 
 // GCC stack is aligned on 16 bytes, but we don't maintain the invariant in
 // jitted code.
--- a/js/src/jit/x86/Assembler-x86.cpp
+++ b/js/src/jit/x86/Assembler-x86.cpp
@@ -30,19 +30,21 @@ ABIArgGenerator::next(MIRType type)
         stackOffset_ += sizeof(uint64_t);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
     return current_;
 }
 
-const Register ABIArgGenerator::NonArgReturnVolatileReg0 = ecx;
-const Register ABIArgGenerator::NonArgReturnVolatileReg1 = edx;
+const Register ABIArgGenerator::NonArgReturnReg0 = ecx;
+const Register ABIArgGenerator::NonArgReturnReg1 = edx;
 const Register ABIArgGenerator::NonVolatileReg = ebx;
+const Register ABIArgGenerator::NonArg_VolatileReg = eax;
+const Register ABIArgGenerator::NonReturn_VolatileReg0 = ecx;
 
 void
 Assembler::executableCopy(uint8_t *buffer)
 {
     AssemblerX86Shared::executableCopy(buffer);
 
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch &rp = jumps_[i];
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -76,19 +76,21 @@ class ABIArgGenerator
 
   public:
     ABIArgGenerator();
     ABIArg next(MIRType argType);
     ABIArg &current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
 
     // Note: these registers are all guaranteed to be different
-    static const Register NonArgReturnVolatileReg0;
-    static const Register NonArgReturnVolatileReg1;
+    static const Register NonArgReturnReg0;
+    static const Register NonArgReturnReg1;
     static const Register NonVolatileReg;
+    static const Register NonArg_VolatileReg;
+    static const Register NonReturn_VolatileReg0;
 };
 
 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = edx;
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = edx;
 
 // Registers used in the GenerateFFIIonExit Enable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = edi;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4262,38 +4262,24 @@ SingleStepCallback(void *arg, jit::Simul
 {
     JSRuntime *rt = reinterpret_cast<JSRuntime*>(arg);
 
     JS::ProfilingFrameIterator::RegisterState state;
     state.pc = pc;
     state.sp = (void*)sim->get_register(jit::Simulator::sp);
     state.lr = (void*)sim->get_register(jit::Simulator::lr);
 
+    DebugOnly<void*> lastStackAddress = nullptr;
     StackChars stack;
     for (JS::ProfilingFrameIterator i(rt, state); !i.done(); ++i) {
-        switch (i.kind()) {
-          case JS::ProfilingFrameIterator::Function: {
-            JS::AutoCheckCannotGC nogc;
-            JSAtom *atom = i.functionDisplayAtom();
-            if (atom->hasLatin1Chars())
-                stack.append(atom->latin1Chars(nogc), atom->length());
-            else
-                stack.append(atom->twoByteChars(nogc), atom->length());
-            break;
-          }
-          case JS::ProfilingFrameIterator::AsmJSTrampoline: {
-            stack.append('*');
-            break;
-          }
-          case JS::ProfilingFrameIterator::CppFunction: {
-            const char *desc = i.nonFunctionDescription();
-            stack.append(desc, strlen(desc));
-            break;
-          }
-        }
+        JS_ASSERT(i.stackAddress() != nullptr);
+        JS_ASSERT(lastStackAddress <= i.stackAddress());
+        lastStackAddress = i.stackAddress();
+        const char *label = i.label();
+        stack.append(label, strlen(label));
     }
 
     // Only append the stack if it differs from the last stack.
     if (stacks.empty() ||
         stacks.back().length() != stack.length() ||
         !PodEqual(stacks.back().begin(), stack.begin(), stack.length()))
     {
         stacks.append(Move(stack));
--- a/js/src/vm/SPSProfiler.cpp
+++ b/js/src/vm/SPSProfiler.cpp
@@ -196,28 +196,28 @@ SPSProfiler::exit(JSScript *script, JSFu
         JS_ASSERT(strcmp((const char*) stack_[*size_].label(), str) == 0);
         stack_[*size_].setLabel(nullptr);
         stack_[*size_].setPC(nullptr);
     }
 #endif
 }
 
 void
-SPSProfiler::enterNative(const char *string, void *sp)
+SPSProfiler::enterAsmJS(const char *string, void *sp)
 {
     /* these operations cannot be re-ordered, so volatile-ize operations */
     volatile ProfileEntry *stack = stack_;
     volatile uint32_t *size = size_;
     uint32_t current = *size;
 
     JS_ASSERT(enabled());
     if (current < max_) {
         stack[current].setLabel(string);
         stack[current].setCppFrame(sp, 0);
-        JS_ASSERT(stack[current].flags() == js::ProfileEntry::IS_CPP_ENTRY);
+        stack[current].setFlag(ProfileEntry::ASMJS);
     }
     *size = current + 1;
 }
 
 void
 SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc, bool copy)
 {
     JS_ASSERT_IF(sp != nullptr, script == nullptr && pc == nullptr);
@@ -318,17 +318,17 @@ SPSEntryMarker::SPSEntryMarker(JSRuntime
     : profiler(&rt->spsProfiler)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (!profiler->installed()) {
         profiler = nullptr;
         return;
     }
     size_before = *profiler->size_;
-    profiler->push("js::RunScript", nullptr, script, script->code(), /* copy = */ false);
+    profiler->push("js::RunScript", this, nullptr, nullptr, /* copy = */ false);
 }
 
 SPSEntryMarker::~SPSEntryMarker()
 {
     if (profiler != nullptr) {
         profiler->pop();
         JS_ASSERT(size_before == *profiler->size_);
     }
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -174,19 +174,19 @@ class SPSProfiler
     void updatePC(JSScript *script, jsbytecode *pc) {
         if (enabled() && *size_ - 1 < max_) {
             JS_ASSERT(*size_ > 0);
             JS_ASSERT(stack_[*size_ - 1].script() == script);
             stack_[*size_ - 1].setPC(pc);
         }
     }
 
-    /* Enter a C++ function. */
-    void enterNative(const char *string, void *sp);
-    void exitNative() { pop(); }
+    /* Enter asm.js code */
+    void enterAsmJS(const char *string, void *sp);
+    void exitAsmJS() { pop(); }
 
     jsbytecode *ipToPC(JSScript *script, size_t ip) { return nullptr; }
 
     void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
     void setEventMarker(void (*fn)(const char *));
     const char *profileString(JSScript *script, JSFunction *maybeFun);
     void onScriptFinalized(JSScript *script);
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1691,34 +1691,34 @@ AsmJSActivation::AsmJSActivation(JSConte
     exitReason_(AsmJSExit::None)
 {
     if (cx->runtime()->spsProfiler.enabled()) {
         // Use a profiler string that matches jsMatch regex in
         // browser/devtools/profiler/cleopatra/js/parserWorker.js.
         // (For now use a single static string to avoid further slowing down
         // calls into asm.js.)
         profiler_ = &cx->runtime()->spsProfiler;
-        profiler_->enterNative("asm.js code :0", this);
+        profiler_->enterAsmJS("asm.js code :0", this);
     }
 
     prevAsmJSForModule_ = module.activation();
     module.activation() = this;
 
     prevAsmJS_ = cx->mainThread().asmJSActivationStack_;
 
     JSRuntime::AutoLockForInterrupt lock(cx->runtime());
     cx->mainThread().asmJSActivationStack_ = this;
 
     (void) errorRejoinSP_;  // squelch GCC warning
 }
 
 AsmJSActivation::~AsmJSActivation()
 {
     if (profiler_)
-        profiler_->exitNative();
+        profiler_->exitAsmJS();
 
     JS_ASSERT(fp_ == nullptr);
 
     JS_ASSERT(module_.activation() == this);
     module_.activation() = prevAsmJSForModule_;
 
     JSContext *cx = cx_->asJSContext();
     JS_ASSERT(cx->mainThread().asmJSActivationStack_ == this);
@@ -1827,51 +1827,28 @@ JS::ProfilingFrameIterator::settle()
             return;
         new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_);
     }
 #else
     MOZ_CRASH("Shouldn't have any frames");
 #endif
 }
 
-JS::ProfilingFrameIterator::Kind
-JS::ProfilingFrameIterator::kind() const
+void *
+JS::ProfilingFrameIterator::stackAddress() const
 {
 #ifdef JS_ION
-    return iter().kind();
-#else
-    MOZ_CRASH("Shouldn't have any frames");
-#endif
-}
-
-JSAtom *
-JS::ProfilingFrameIterator::functionDisplayAtom() const
-{
-#ifdef JS_ION
-    JS_ASSERT(kind() == Function);
-    return iter().functionDisplayAtom();
+    return iter().stackAddress();
 #else
     MOZ_CRASH("Shouldn't have any frames");
 #endif
 }
 
 const char *
-JS::ProfilingFrameIterator::functionFilename() const
+JS::ProfilingFrameIterator::label() const
 {
 #ifdef JS_ION
-    JS_ASSERT(kind() == Function);
-    return iter().functionFilename();
+    return iter().label();
 #else
     MOZ_CRASH("Shouldn't have any frames");
 #endif
 }
 
-const char *
-JS::ProfilingFrameIterator::nonFunctionDescription() const
-{
-#ifdef JS_ION
-    JS_ASSERT(kind() != Function);
-    return iter().nonFunctionDescription();
-#else
-    MOZ_CRASH("Shouldn't have any frames");
-#endif
-}
-
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -605,16 +605,18 @@ XPCWrappedNativeScope::SweepAllWrappedNa
 
 // static
 void
 XPCWrappedNativeScope::KillDyingScopes()
 {
     XPCWrappedNativeScope* cur = gDyingScopes;
     while (cur) {
         XPCWrappedNativeScope* next = cur->mNext;
+        if (cur->mGlobalJSObject)
+            CompartmentPrivate::Get(cur->mGlobalJSObject)->scope = nullptr;
         delete cur;
         cur = next;
     }
     gDyingScopes = nullptr;
 }
 
 struct ShutdownData
 {
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -293,20 +293,27 @@ xpc_TryUnmarkWrappedGrayObject(nsISuppor
 template<typename T>
 static inline T UnexpectedFailure(T rv)
 {
     NS_ERROR("This is not supposed to fail!");
     return rv;
 }
 
 void
-TraceXPCGlobal(JSTracer *trc, JSObject *obj)
+xpc::TraceXPCGlobal(JSTracer *trc, JSObject *obj)
 {
     if (js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL)
         mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
+
+    // We might be called from a GC during the creation of a global, before we've
+    // been able to set up the compartment private or the XPCWrappedNativeScope,
+    // so we need to null-check those.
+    xpc::CompartmentPrivate* compartmentPrivate = xpc::CompartmentPrivate::Get(obj);
+    if (compartmentPrivate && compartmentPrivate->scope)
+        compartmentPrivate->scope->TraceInside(trc);
 }
 
 namespace xpc {
 
 JSObject*
 CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
                    JS::CompartmentOptions& aOptions)
 {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1057,17 +1057,17 @@ public:
     SetExpandoChain(JSContext *cx, JS::HandleObject target, JS::HandleObject chain);
 
     static void
     SystemIsBeingShutDown();
 
     static void
     TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt);
 
-    void TraceSelf(JSTracer *trc) {
+    void TraceInside(JSTracer *trc) {
         MOZ_ASSERT(mGlobalJSObject);
         mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
         if (mContentXBLScope)
             mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
         for (size_t i = 0; i < mAddonScopes.Length(); i++)
             mAddonScopes[i].trace(trc, "XPCWrappedNativeScope::mAddonScopes");
         if (mXrayExpandos.initialized())
             mXrayExpandos.trace(trc);
@@ -1856,17 +1856,17 @@ public:
 
     void TraceInside(JSTracer *trc) {
         if (JS_IsGCMarkingTracer(trc)) {
             mSet->Mark();
             if (mScriptableInfo)
                 mScriptableInfo->Mark();
         }
 
-        GetScope()->TraceSelf(trc);
+        GetScope()->TraceInside(trc);
     }
 
     void TraceJS(JSTracer *trc) {
         TraceSelf(trc);
         TraceInside(trc);
     }
 
     void WriteBarrierPre(JSRuntime* rt)
@@ -1982,19 +1982,16 @@ private:
 
 private:
     XPCWrappedNativeTearOff mTearOffs[XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK];
     XPCWrappedNativeTearOffChunk* mNextChunk;
 };
 
 void *xpc_GetJSPrivate(JSObject *obj);
 
-void
-TraceXPCGlobal(JSTracer *trc, JSObject *obj);
-
 /***************************************************************************/
 // XPCWrappedNative the wrapper