Merge m-c to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 25 Sep 2014 15:34:04 +0200
changeset 230438 2bdd292ffc816527aed10691c54abe5e0de4dab7
parent 230437 031c3611409480f44765873eb3924027a7937e13 (current diff)
parent 230416 e9e56750ca5b4a9ac8dbcedf253e298545fe9da9 (diff)
child 230439 461788c9f58d2210f211ebdac65e9196353b9fe1
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound
dom/camera/DOMCameraManager.cpp
layout/generic/nsObjectFrame.cpp
layout/generic/nsObjectFrame.h
mobile/android/base/resources/drawable-large-hdpi-v11/favicon_none.png
mobile/android/base/resources/drawable-large-mdpi-v11/favicon_none.png
mobile/android/base/resources/drawable-large-xhdpi-v11/favicon_none.png
mobile/android/base/resources/drawable-large-xxhdpi-v11/favicon_none.png
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -455,17 +455,17 @@ TextAttrsMgr::FontFamilyTextAttr::
 bool
 TextAttrsMgr::FontFamilyTextAttr::
   GetFontFamily(nsIFrame* aFrame, nsString& aFamily)
 {
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
 
   gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
-  gfxFont* font = fontGroup->GetFontAt(0);
+  gfxFont* font = fontGroup->GetFirstValidFont();
   gfxFontEntry* fontEntry = font->GetFontEntry();
   aFamily = fontEntry->FamilyName();
   return true;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // FontSizeTextAttr
@@ -613,17 +613,17 @@ TextAttrsMgr::FontWeightTextAttr::
   GetFontWeight(nsIFrame* aFrame)
 {
   // nsFont::width isn't suitable here because it's necessary to expose real
   // value of font weight (used font might not have some font weight values).
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
 
   gfxFontGroup *fontGroup = fm->GetThebesFontGroup();
-  gfxFont *font = fontGroup->GetFontAt(0);
+  gfxFont *font = fontGroup->GetFirstValidFont();
 
   // When there doesn't exist a bold font in the family and so the rendering of
   // a non-bold font face is changed so that the user sees what looks like a
   // bold font, i.e. synthetic bolding is used. IsSyntheticBold method is only
   // needed on Mac, but it is "safe" to use on all platforms.  (For non-Mac
   // platforms it always return false.)
   if (font->IsSyntheticBold())
     return 700;
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -52,17 +52,17 @@
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 #include "nsImageFrame.h"
 #include "nsIObserverService.h"
 #include "nsLayoutUtils.h"
-#include "nsObjectFrame.h"
+#include "nsPluginFrame.h"
 #include "nsSVGPathGeometryFrame.h"
 #include "nsTreeBodyFrame.h"
 #include "nsTreeColumns.h"
 #include "nsTreeUtils.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsXBLBinding.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/DOMStringList.h"
@@ -258,21 +258,21 @@ public:
 private:
   nsCOMPtr<nsIContent> mContent;
 };
 
 NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback)
 #endif
 
 already_AddRefed<Accessible>
-nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame,
+nsAccessibilityService::CreatePluginAccessible(nsPluginFrame* aFrame,
                                                nsIContent* aContent,
                                                Accessible* aContext)
 {
-  // nsObjectFrame means a plugin, so we need to use the accessibility support
+  // nsPluginFrame means a plugin, so we need to use the accessibility support
   // of the plugin.
   if (aFrame->GetRect().IsEmpty())
     return nullptr;
 
 #if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
   nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
   if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) &&
       pluginInstance) {
@@ -1614,18 +1614,18 @@ nsAccessibilityService::CreateAccessible
 
     case eImageType:
       newAcc = new ImageAccessibleWrap(aContent, document);
       break;
     case eOuterDocType:
       newAcc = new OuterDocAccessible(aContent, document);
       break;
     case ePluginType: {
-      nsObjectFrame* objectFrame = do_QueryFrame(aFrame);
-      newAcc = CreatePluginAccessible(objectFrame, aContent, aContext);
+      nsPluginFrame* pluginFrame = do_QueryFrame(aFrame);
+      newAcc = CreatePluginAccessible(pluginFrame, aContent, aContext);
       break;
     }
     case eTextLeafType:
       newAcc = new TextLeafAccessibleWrap(aContent, document);
       break;
     default:
       MOZ_ASSERT(false);
       break;
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -10,17 +10,17 @@
 
 #include "mozilla/a11y/DocManager.h"
 #include "mozilla/a11y/FocusManager.h"
 #include "mozilla/a11y/SelectionManager.h"
 
 #include "nsIObserver.h"
 
 class nsImageFrame;
-class nsObjectFrame;
+class nsPluginFrame;
 class nsITreeView;
 
 namespace mozilla {
 namespace a11y {
 
 class ApplicationAccessible;
 
 /**
@@ -58,17 +58,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLERETRIEVAL
   NS_DECL_NSIOBSERVER
 
   // nsIAccessibilityService
   virtual Accessible* GetRootDocumentAccessible(nsIPresShell* aPresShell,
                                                 bool aCanCreate);
   already_AddRefed<Accessible>
-    CreatePluginAccessible(nsObjectFrame* aFrame, nsIContent* aContent,
+    CreatePluginAccessible(nsPluginFrame* aFrame, nsIContent* aContent,
                            Accessible* aContext);
 
   /**
    * Adds/remove ATK root accessible for gtk+ native window to/from children
    * of the application accessible.
    */
   virtual Accessible* AddNativeRootAccessible(void* aAtkAccessible);
   virtual void RemoveNativeRootAccessible(Accessible* aRootAccessible);
--- a/accessible/interfaces/nsIAccessibilityService.h
+++ b/accessible/interfaces/nsIAccessibilityService.h
@@ -19,17 +19,17 @@ class Accessible;
 
 } // namespace a11y
 } // namespace mozilla
 
 class nsINode;
 class nsIContent;
 class nsIFrame;
 class nsIPresShell;
-class nsObjectFrame;
+class nsPluginFrame;
 
 // 10ff6dca-b219-4b64-9a4c-67a62b86edce
 #define NS_IACCESSIBILITYSERVICE_IID \
 { 0x84dd9182, 0x6639, 0x4377, \
  { 0xa4, 0x13, 0xad, 0xe1, 0xae, 0x4e, 0x52, 0xdd } }
 
 class nsIAccessibilityService : public nsIAccessibleRetrieval
 {
--- a/accessible/windows/sdn/sdnTextAccessible.cpp
+++ b/accessible/windows/sdn/sdnTextAccessible.cpp
@@ -177,17 +177,18 @@ sdnTextAccessible::get_fontFamily(BSTR _
 
   nsIFrame* frame = mAccessible->GetFrame();
   if (!frame)
     return E_FAIL;
 
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(frame, getter_AddRefs(fm));
 
-  const nsString& name = fm->GetThebesFontGroup()->GetFontAt(0)->GetName();
+  const nsString& name =
+    fm->GetThebesFontGroup()->GetFirstValidFont()->GetName();
   if (name.IsEmpty())
     return S_FALSE;
 
   *aFontFamily = ::SysAllocStringLen(name.get(), name.Length());
   return *aFontFamily ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
--- a/addon-sdk/mach_commands.py
+++ b/addon-sdk/mach_commands.py
@@ -43,24 +43,24 @@ class MachCommands(MachCommandBase):
     @Command('generate-addon-sdk-moz-build', category='misc',
         description='Generates the moz.build file for the addon-sdk/ directory.')
     def run_addon_sdk_moz_build(self, **params):
         addon_sdk_dir = mozpath.join(self.topsrcdir, 'addon-sdk')
         js_src_dir = mozpath.join(addon_sdk_dir, 'source/lib')
         dirs_to_files = {}
 
         for path, dirs, files in os.walk(js_src_dir):
-            js_files = [f for f in files if f.endswith(('.js', '.jsm'))]
+            js_files = [f for f in files if f.endswith(('.js', '.jsm', '.html'))]
             if not js_files:
                 continue
 
             relative = mozpath.relpath(path, js_src_dir)
             dirs_to_files[relative] = js_files
 
-        moz_build = """# AUTOMATICALLY GENERATED FROM moz.build.in AND mach.  DO NOT EDIT.
+        moz_build = """# AUTOMATICALLY GENERATED FROM mozbuild.template AND mach.  DO NOT EDIT.
 # 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/.
 
 %(moz-build-template)s
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk":
 %(non-b2g-modules)s
 %(always-on-modules)s"""
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -394,16 +394,17 @@ EXTRA_JS_MODULES.commonjs.sdk.tab += [
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.button.view += [
     'source/lib/sdk/ui/button/view/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.frame += [
     'source/lib/sdk/ui/frame/model.js',
+    'source/lib/sdk/ui/frame/view.html',
     'source/lib/sdk/ui/frame/view.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.state += [
     'source/lib/sdk/ui/state/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.ui.toolbar += [
--- a/addon-sdk/mozbuild.template
+++ b/addon-sdk/mozbuild.template
@@ -3,13 +3,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 JETPACK_PACKAGE_MANIFESTS += ['source/test/jetpack-package.ini']
 JETPACK_ADDON_MANIFESTS += ['source/test/addons/jetpack-addon.ini']
 
-DIRS += ["source/modules/system"]
-
 EXTRA_JS_MODULES.sdk += [
     'source/app-extension/bootstrap.js',
 ]
+
+EXTRA_JS_MODULES.sdk.system += [
+    'source/modules/system/Startup.js',
+    'source/modules/system/XulApp.js',
+]
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -739,24 +739,24 @@ pref("hal.processPriorityManager.gonk.BA
 
 // Processes get this niceness when they have low CPU priority.
 pref("hal.processPriorityManager.gonk.LowCPUNice", 18);
 
 // By default the compositor thread on gonk runs without real-time priority.  RT
 // priority can be enabled by setting this pref to a value between 1 and 99.
 // Note that audio processing currently runs at RT priority 2 or 3 at most.
 //
-// If RT priority is disabled, then the compositor nice value is used.  The
-// code will default to ANDROID_PRIORITY_URGENT_DISPLAY which is -8.  Per gfx
-// request we are keeping the compositor at nice level 0 until we can complete
-// the investigation in bug 982972.
+// If RT priority is disabled, then the compositor nice value is used. We prefer
+// to use a nice value of -4, which matches Android's preferences. Setting a preference
+// of RT priority 1 would mean it is higher than audio, which is -16. The compositor
+// priority must be below the audio thread.
 //
 // Do not change these values without gfx team review.
 pref("hal.gonk.COMPOSITOR.rt_priority", 0);
-pref("hal.gonk.COMPOSITOR.nice", 0);
+pref("hal.gonk.COMPOSITOR.nice", -4);
 
 // Fire a memory pressure event when the system has less than Xmb of memory
 // remaining.  You should probably set this just above Y.KillUnderKB for
 // the highest priority class Y that you want to make an effort to keep alive.
 // (For example, we want BACKGROUND_PERCEIVABLE to stay alive.)  If you set
 // this too high, then we'll send out a memory pressure event every Z seconds
 // (see below), even while we have processes that we would happily kill in
 // order to free up memory.
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -301,16 +301,25 @@ var gPluginHandler = {
   hideNotificationBar: function (browser, name) {
     let notificationBox = gBrowser.getNotificationBox(browser);
     let notification = notificationBox.getNotificationWithValue(name);
     if (notification)
       notificationBox.removeNotification(notification, true);
   },
 
   updateHiddenPluginUI: function (browser, haveInsecure, actions, principal, host) {
+    // It is possible that we've received a message from the frame script to show
+    // the hidden plugin notification for a principal that no longer matches the one
+    // that the browser's content now has assigned (ie, the browser has browsed away
+    // after the message was sent, but before the message was received). In that case,
+    // we should just ignore the message.
+    if (!principal.equals(browser.contentPrincipal)) {
+      return;
+    }
+
     // Set up the icon
     document.getElementById("plugins-notification-icon").classList.
       toggle("plugin-blocked", haveInsecure);
 
     // Now configure the notification bar
     let notificationBox = gBrowser.getNotificationBox(browser);
 
     function hideNotification() {
--- a/browser/base/content/newtab/intro.js
+++ b/browser/base/content/newtab/intro.js
@@ -36,17 +36,17 @@ let gIntro = {
       // Point the panel at the 'what' menu item
       this._nodes.panel.openPopup(nodes.what);
     });
   },
 
   _setUpPanel: function() {
     // Build the panel if necessary
     if (this._nodes.panel.childNodes.length == 1) {
-      ['<a href="' + TILES_EXPLAIN_LINK + '">' + newTabString("learn.link") + "</a>",
+      ['<a href="' + TILES_INTRO_LINK + '">' + newTabString("learn.link") + "</a>",
        '<a href="' + TILES_PRIVACY_LINK + '">' + newTabString("privacy.link") + "</a>",
        '<input type="button" class="newtab-customize"/>',
       ].forEach((arg, index) => {
         let paragraph = document.createElementNS(HTML_NAMESPACE, "p");
         this._nodes.panel.appendChild(paragraph);
         paragraph.innerHTML = newTabString("intro.paragraph" + (index + 1), [arg]);
       });
     }
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -181,33 +181,32 @@ input[type=button] {
 .newtab-title {
   font-size: 13px;
   left: 0;
   padding-top: 14px;
   text-overflow: ellipsis;
 }
 
 .newtab-sponsored {
-  background-color: #f9f9f9;
   border: 1px solid #dcdcdc;
   border-radius: 2px;
-  color: #9b9b9b;
   cursor: pointer;
   display: none;
   font-family: Arial;
   font-size: 10px;
   height: 17px;
   line-height: 17px;
   margin-bottom: -1px;
   padding: 0 4px;
 }
 
 .newtab-sponsored:-moz-any(:hover, [active]) {
-  background-color: #dcdcdc;
-  color: #666666;
+  background-color: #3a72b1;
+  border: 0;
+  color: white;
 }
 
 .newtab-sponsored:-moz-locale-dir(rtl) {
   left: 0;
   right: auto;
 }
 
 .newtab-site:-moz-any([type=enhanced], [type=sponsored]) .newtab-sponsored {
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -45,17 +45,18 @@ function newTabString(name, args) {
 
 function inPrivateBrowsingMode() {
   return PrivateBrowsingUtils.isWindowPrivate(window);
 }
 
 const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
 const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
-const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-sponsored-tiles-work";
+const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-tiles-work-firefox";
+const TILES_INTRO_LINK = "https://www.mozilla.org/firefox/tiles/";
 const TILES_PRIVACY_LINK = "https://www.mozilla.org/privacy/";
 
 #include transformations.js
 #include page.js
 #include grid.js
 #include cells.js
 #include sites.js
 #include drag.js
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -575,19 +575,16 @@
               Array.unshift(arguments, this.mBrowser);
               return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
             },
 
             _shouldShowProgress: function (aRequest) {
               if (this.mBlank)
                 return false;
 
-              if (gMultiProcessBrowser)
-                return true;
-
               // Don't show progress indicators in tabs for about: URIs
               // pointing to local resources.
               try {
                 let channel = aRequest.QueryInterface(Ci.nsIChannel);
                 if (channel.originalURI.schemeIs("about") &&
                     (channel.URI.schemeIs("jar") || channel.URI.schemeIs("file")))
                   return false;
               } catch (e) {}
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -283,16 +283,17 @@ skip-if = e10s # Bug 916974 - Session hi
 [browser_bug902156.js]
 skip-if = e10s
 [browser_bug906190.js]
 skip-if = buildapp == "mulet" || e10s # Bug ?????? - test directly manipulates content (strange - gets an element from a child which it tries to treat as a string, but that fails)
 [browser_bug970746.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content (directly gets elements from the content)
 [browser_bug1015721.js]
 skip-if = os == 'win' || e10s # Bug 1056146 - FullZoomHelper uses promiseTabLoadEvent() which isn't e10s friendly
+[browser_bug1064280_changeUrlInPinnedTab.js]
 [browser_canonizeURL.js]
 skip-if = e10s # Bug ?????? - [JavaScript Error: "Error in AboutHome.sendAboutHomeData TypeError: target.messageManager is undefined" {file: "resource:///modules/AboutHome.jsm" line: 208}]
 [browser_contentAreaClick.js]
 skip-if = e10s
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" || e10s # bug 967013, bug 926729
 [browser_ctrlTab.js]
 skip-if = e10s # Bug ????? - thumbnail captures need e10s love (tabPreviews_capture fails with Argument 1 of CanvasRenderingContext2D.drawWindow does not implement interface Window.)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_bug1064280_changeUrlInPinnedTab.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function(){
+  // Test that changing the URL in a pinned tab works correctly
+
+  let TEST_LINK_INITIAL = "about:";
+  let TEST_LINK_CHANGED = "about:support";
+
+  let appTab = gBrowser.addTab(TEST_LINK_INITIAL);
+  gBrowser.pinTab(appTab);
+  is(appTab.pinned, true, "Tab was successfully pinned");
+
+  let initialTabsNo = gBrowser.tabs.length;
+
+  let goButton = document.getElementById("urlbar-go-button");
+  gBrowser.selectedTab = appTab;
+  gURLBar.focus();
+  gURLBar.value = TEST_LINK_CHANGED;
+
+  let promisePageload = promiseTabLoadEvent(appTab);
+  goButton.click();
+  yield promisePageload;
+
+  is(appTab.linkedBrowser.currentURI.spec, TEST_LINK_CHANGED,
+     "New page loaded in the app tab");
+  is(gBrowser.tabs.length, initialTabsNo, "No additional tabs were opened");
+});
+
+registerCleanupFunction(function () {
+  gBrowser.removeTab(gBrowser.selectedTab);
+});
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -28,16 +28,17 @@ support-files =
   plugin_clickToPlayDeny.html
   plugin_data_url.html
   plugin_hidden_to_visible.html
   plugin_iframe.html
   plugin_outsideScrollArea.html
   plugin_overlayed.html
   plugin_positioned.html
   plugin_small.html
+  plugin_small_2.html
   plugin_syncRemoved.html
   plugin_test.html
   plugin_test2.html
   plugin_test3.html
   plugin_two_types.html
   plugin_unknown.html
   plugin_crashCommentAndURL.html
   plugin_zoom.html
@@ -57,16 +58,17 @@ run-if = crashreporter
 [browser_CTP_data_urls.js]
 [browser_CTP_drag_drop.js]
 [browser_CTP_hide_overlay.js]
 [browser_CTP_iframe.js]
 [browser_CTP_multi_allow.js]
 [browser_CTP_nonplugins.js]
 [browser_CTP_notificationBar.js]
 [browser_CTP_outsideScrollArea.js]
+[browser_CTP_remove_navigate.js]
 [browser_CTP_resize.js]
 [browser_CTP_zoom.js]
 [browser_globalplugin_crashinfobar.js]
 [browser_pageInfo_plugins.js]
 [browser_pluginnotification.js]
 [browser_pluginplaypreview.js]
 [browser_pluginplaypreview2.js]
 [browser_pluginCrashCommentAndURL.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugins/browser_CTP_remove_navigate.js
@@ -0,0 +1,82 @@
+/* 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/. */
+
+const gTestRoot = getRootDirectory(gTestPath);
+const gHttpTestRoot = gTestRoot.replace("chrome://mochitests/content/",
+                                        "http://127.0.0.1:8888/");
+
+add_task(function* () {
+  Services.prefs.setBoolPref("plugins.click_to_play", true);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("plugins.click_to_play");
+  });
+})
+
+/**
+ * Tests that if a plugin is removed just as we transition to
+ * a different page, that we don't show the hidden plugin
+ * notification bar on the new page.
+ */
+add_task(function* () {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = gBrowser.selectedBrowser;
+
+  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+
+  // Load up a page with a plugin...
+  let notificationPromise =
+    waitForNotificationBar("plugin-hidden", gBrowser.selectedBrowser);
+  yield loadPage(browser, gHttpTestRoot + "plugin_small.html")
+  yield forcePluginBindingAttached(browser);
+  yield notificationPromise;
+
+  // Trigger the PluginRemoved event to be fired, and then immediately
+  // browse to a new page.
+  let plugin = browser.contentDocument.getElementById("test");
+  plugin.remove();
+  yield loadPage(browser, "about:mozilla");
+
+  // There should be no hidden plugin notification bar at about:mozilla.
+  let notificationBox = gBrowser.getNotificationBox(browser);
+  is(notificationBox.getNotificationWithValue("plugin-hidden"), null,
+     "Expected no notification box");
+  gBrowser.removeTab(newTab);
+});
+
+/**
+ * Tests that if a plugin is removed just as we transition to
+ * a different page with a plugin, that we show the right notification
+ * for the new page.
+ */
+add_task(function* () {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = gBrowser.selectedBrowser;
+
+  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY,
+                            "Second Test Plug-in");
+
+  // Load up a page with a plugin...
+  let notificationPromise =
+    waitForNotificationBar("plugin-hidden", browser);
+  yield loadPage(browser, gHttpTestRoot + "plugin_small.html")
+  yield forcePluginBindingAttached(browser);
+  yield notificationPromise;
+
+  // Trigger the PluginRemoved event to be fired, and then immediately
+  // browse to a new page.
+  let plugin = browser.contentDocument.getElementById("test");
+  plugin.remove();
+  yield loadPage(browser, gTestRoot + "plugin_small_2.html");
+  let notification = yield waitForNotificationBar("plugin-hidden", browser);
+  ok(notification, "There should be a notification shown for the new page.");
+
+  // Ensure that the notification is showing information about
+  // the x-second-test plugin.
+  ok(notification.label.contains("Second Test"), "Should mention the second plugin");
+  ok(!notification.label.contains("127.0.0.1"), "Should not refer to old principal");
+  ok(notification.label.contains("null"), "Should refer to the new principal");
+  gBrowser.removeTab(newTab);
+});
--- a/browser/base/content/test/plugins/head.js
+++ b/browser/base/content/test/plugins/head.js
@@ -116,20 +116,83 @@ function waitForNotificationPopup(notifi
     () => {
       ok(notification, `Successfully got the ${notificationID} notification popup`);
       callback(notification);
     },
     `Waited too long for the ${notificationID} notification popup`
   );
 }
 
+/**
+ * Returns a Promise that resolves when a notification bar
+ * for a browser is shown. Alternatively, for old-style callers,
+ * can automatically call a callback before it resolves.
+ *
+ * @param notificationID
+ *        The ID of the notification to look for.
+ * @param browser
+ *        The browser to check for the notification bar.
+ * @param callback (optional)
+ *        A function to be called just before the Promise resolves.
+ *
+ * @return Promise
+ */
 function waitForNotificationBar(notificationID, browser, callback) {
-  let notification;
-  let notificationBox = gBrowser.getNotificationBox(browser);
-  waitForCondition(
-    () => (notification = notificationBox.getNotificationWithValue(notificationID)),
-    () => {
-      ok(notification, `Successfully got the ${notificationID} notification bar`);
-      callback(notification);
-    },
-    `Waited too long for the ${notificationID} notification bar`
-  );
+  return new Promise((resolve, reject) => {
+    let notification;
+    let notificationBox = gBrowser.getNotificationBox(browser);
+    waitForCondition(
+      () => (notification = notificationBox.getNotificationWithValue(notificationID)),
+      () => {
+        ok(notification, `Successfully got the ${notificationID} notification bar`);
+        if (callback) {
+          callback(notification);
+        }
+        resolve(notification);
+      },
+      `Waited too long for the ${notificationID} notification bar`
+    );
+  });
 }
+
+/**
+ * Due to layout being async, "PluginBindAttached" may trigger later.
+ * This returns a Promise that resolves once we've forced a layout
+ * flush, which triggers the PluginBindAttached event to fire.
+ *
+ * @param browser
+ *        The browser to force plugin bindings in.
+ *
+ * @return Promise
+ */
+function forcePluginBindingAttached(browser) {
+  return new Promise((resolve, reject) => {
+    let doc = browser.contentDocument;
+    let elems = doc.getElementsByTagName('embed');
+    if (elems.length < 1) {
+      elems = doc.getElementsByTagName('object');
+    }
+    elems[0].clientTop;
+    executeSoon(resolve);
+  });
+}
+
+/**
+ * Loads a page in a browser, and returns a Promise that
+ * resolves once the "load" event has been fired for that
+ * browser.
+ *
+ * @param browser
+ *        The browser to load the page in.
+ * @param uri
+ *        The URI to load.
+ *
+ * @return Promise
+ */
+function loadPage(browser, uri) {
+  return new Promise((resolve, reject) => {
+    browser.addEventListener("load", function onLoad(event) {
+      browser.removeEventListener("load", onLoad, true);
+      resolve();
+    }, true);
+    browser.loadURI(uri);
+  });
+}
copy from browser/base/content/test/plugins/plugin_small.html
copy to browser/base/content/test/plugins/plugin_small_2.html
--- a/browser/base/content/test/plugins/plugin_small.html
+++ b/browser/base/content/test/plugins/plugin_small_2.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 </head>
 <body>
-<embed id="test" style="width: 10px; height: 10px" type="application/x-test">
+<embed id="test" style="width: 10px; height: 10px" type="application/x-second-test">
 </body>
 </html>
--- a/browser/devtools/canvasdebugger/panel.js
+++ b/browser/devtools/canvasdebugger/panel.js
@@ -61,12 +61,14 @@ CanvasDebuggerPanel.prototype = {
 
   destroy: function() {
     // Make sure this panel is not already destroyed.
     if (this._destroyer) {
       return this._destroyer;
     }
 
     return this._destroyer = this.panelWin.shutdownCanvasDebugger().then(() => {
+      // Destroy front to ensure packet handler is removed from client
+      this.panelWin.gFront.destroy();
       this.emit("destroyed");
     });
   }
 };
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -27,16 +27,17 @@ skip-if = e10s # Bug 1070837 - devtools/
 # [browser_toolbox_raise.js] # Bug 962258
 # skip-if = os == "win"
 [browser_toolbox_ready.js]
 [browser_toolbox_select_event.js]
 skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
 [browser_toolbox_sidebar.js]
 [browser_toolbox_tabsswitch_shortcuts.js]
 [browser_toolbox_tool_ready.js]
+[browser_toolbox_tool_remote_reopen.js]
 [browser_toolbox_window_reload_target.js]
 [browser_toolbox_window_shortcuts.js]
 [browser_toolbox_window_title_changes.js]
 [browser_toolbox_zoom.js]
 [browser_toolbox_custom_host.js]
 [browser_toolbox_theme_registration.js]
 
 # We want this test to run for mochitest-dt as well, so we include it here:
--- a/browser/devtools/framework/test/browser_toolbox_tool_ready.js
+++ b/browser/devtools/framework/test/browser_toolbox_tool_ready.js
@@ -1,41 +1,38 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function test() {
-  addTab("about:blank").then(function(tab) {
-    let target = TargetFactory.forTab(tab);
-    target.makeRemote().then(performChecks.bind(null, target));
-  }).then(null, console.error);
+function performChecks(target) {
+  return Task.spawn(function() {
+    let toolIds = gDevTools.getToolDefinitionArray()
+                           .filter(def => def.isTargetSupported(target))
+                           .map(def => def.id);
 
-  function performChecks(target) {
-    let toolIds = gDevTools.getToolDefinitionArray()
-                    .filter(def => def.isTargetSupported(target))
-                    .map(def => def.id);
-
-    let open = function(index) {
+    let toolbox;
+    for (let index = 0; index < toolIds.length; index++) {
       let toolId = toolIds[index];
 
       info("About to open " + index + "/" + toolId);
-      gDevTools.showToolbox(target, toolId).then(function(toolbox) {
-        ok(toolbox, "toolbox exists for " + toolId);
-        is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
+      toolbox = yield gDevTools.showToolbox(target, toolId);
+      ok(toolbox, "toolbox exists for " + toolId);
+      is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
 
-        let panel = toolbox.getCurrentPanel();
-        ok(panel.isReady, toolId + " panel should be ready");
+      let panel = toolbox.getCurrentPanel();
+      ok(panel.isReady, toolId + " panel should be ready");
+    }
+
+    yield toolbox.destroy();
+  });
+}
 
-        let nextIndex = index + 1;
-        if (nextIndex >= toolIds.length) {
-          toolbox.destroy().then(function() {
-            gBrowser.removeCurrentTab();
-            finish();
-          });
-        }
-        else {
-          open(nextIndex);
-        }
-      }, console.error);
-    };
-
-    open(0);
-  }
+function test() {
+  Task.spawn(function() {
+    toggleAllTools(true);
+    let tab = yield addTab("about:blank");
+    let target = TargetFactory.forTab(tab);
+    yield target.makeRemote();
+    yield performChecks(target);
+    gBrowser.removeCurrentTab();
+    toggleAllTools(false);
+    finish();
+  }, console.error);
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_tool_remote_reopen.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { DebuggerServer } =
+  Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
+const { DebuggerClient } =
+  Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
+
+/**
+ * Bug 979536: Ensure fronts are destroyed after toolbox close.
+ *
+ * The fronts need to be destroyed manually to unbind their onPacket handlers.
+ *
+ * When you initialize a front and call |this.manage|, it adds a client actor
+ * pool that the DebuggerClient uses to route packet replies to that actor.
+ *
+ * Most (all?) tools create a new front when they are opened.  When the destroy
+ * step is skipped and the tool is reopened, a second front is created and also
+ * added to the client actor pool.  When a packet reply is received, is ends up
+ * being routed to the first (now unwanted) front that is still in the client
+ * actor pool.  Since this is not the same front that was used to make the
+ * request, an error occurs.
+ *
+ * This problem does not occur with the toolbox for a local tab because the
+ * toolbox target creates its own DebuggerClient for the local tab, and the
+ * client is destroyed when the toolbox is closed, which removes the client
+ * actor pools, and avoids this issue.
+ *
+ * In WebIDE, we do not destroy the DebuggerClient on toolbox close because it
+ * is still used for other purposes like managing apps, etc. that aren't part of
+ * a toolbox.  Thus, the same client gets reused across multiple toolboxes,
+ * which leads to the tools failing if they don't destroy their fronts.
+ */
+
+function runTools(target) {
+  return Task.spawn(function() {
+    let toolIds = gDevTools.getToolDefinitionArray()
+                           .filter(def => def.isTargetSupported(target))
+                           .map(def => def.id);
+
+    let toolbox;
+    for (let index = 0; index < toolIds.length; index++) {
+      let toolId = toolIds[index];
+
+      info("About to open " + index + "/" + toolId);
+      toolbox = yield gDevTools.showToolbox(target, toolId, "window");
+      ok(toolbox, "toolbox exists for " + toolId);
+      is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
+
+      let panel = toolbox.getCurrentPanel();
+      ok(panel.isReady, toolId + " panel should be ready");
+    }
+
+    yield toolbox.destroy();
+  });
+}
+
+function getClient() {
+  let deferred = promise.defer();
+
+  if (!DebuggerServer.initialized) {
+    DebuggerServer.init(() => true);
+    DebuggerServer.addBrowserActors();
+  }
+
+  let transport = DebuggerServer.connectPipe();
+  let client = new DebuggerClient(transport);
+
+  client.connect(() => {
+    deferred.resolve(client);
+  });
+
+  return deferred.promise;
+}
+
+function getTarget(client) {
+  let deferred = promise.defer();
+
+  let tabList = client.listTabs(tabList => {
+    let target = TargetFactory.forRemoteTab({
+      client: client,
+      form: tabList.tabs[tabList.selected],
+      chrome: false
+    });
+    deferred.resolve(target);
+  });
+
+  return deferred.promise;
+}
+
+function test() {
+  Task.spawn(function() {
+    toggleAllTools(true);
+    yield addTab("about:blank");
+
+    let client = yield getClient();
+    let target = yield getTarget(client);
+    yield runTools(target);
+
+    // Actor fronts should be destroyed now that the toolbox has closed, but
+    // look for any that remain.
+    for (let pool of client.__pools) {
+      if (!pool.__poolMap) {
+        continue;
+      }
+      for (let actor of pool.__poolMap.keys()) {
+        // Bug 1056342: Profiler fails today because of framerate actor, but
+        // this appears more complex to rework, so leave it for that bug to
+        // resolve.
+        if (actor.contains("framerateActor")) {
+          todo(false, "Front for " + actor + " still held in pool!");
+          continue;
+        }
+        ok(false, "Front for " + actor + " still held in pool!");
+      }
+    }
+
+    gBrowser.removeCurrentTab();
+    DebuggerServer.destroy();
+    toggleAllTools(false);
+    finish();
+  }, console.error);
+}
--- a/browser/devtools/framework/test/head.js
+++ b/browser/devtools/framework/test/head.js
@@ -122,8 +122,21 @@ function once(target, eventName, useCapt
   return deferred.promise;
 }
 
 function waitForTick() {
   let deferred = promise.defer();
   executeSoon(deferred.resolve);
   return deferred.promise;
 }
+
+function toggleAllTools(state) {
+  for (let [, tool] of gDevTools._tools) {
+    if (!tool.visibilityswitch) {
+      continue;
+    }
+    if (state) {
+      Services.prefs.setBoolPref(tool.visibilityswitch, true);
+    } else {
+      Services.prefs.clearUserPref(tool.visibilityswitch);
+    }
+  }
+}
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1537,16 +1537,19 @@ Toolbox.prototype = {
     outstanding.push(this.destroyInspector().then(() => {
       // Removing buttons
       if (this._pickerButton) {
         this._pickerButton.removeEventListener("command", this._togglePicker, false);
         this._pickerButton = null;
       }
     }));
 
+    // We need to grab a reference to win before this._host is destroyed.
+    let win = this.frame.ownerGlobal;
+
     // Remove the host UI
     outstanding.push(this.destroyHost());
 
     if (this._requisition) {
       this._requisition.destroy();
     }
     this._telemetry.toolClosed("toolbox");
     this._telemetry.destroy();
@@ -1564,19 +1567,16 @@ Toolbox.prototype = {
       let target = this._target;
       this._target = null;
       this.highlighterUtils.release();
       target.off("close", this.destroy);
       return target.destroy();
     }, console.error).then(() => {
       this.emit("destroyed");
 
-      // We need to grab a reference to win before this._host is destroyed.
-      let win = this.frame.ownerGlobal;
-
       // Free _host after the call to destroyed in order to let a chance
       // to destroyed listeners to still query toolbox attributes
       this._host = null;
       this._toolPanels.clear();
 
       // Force GC to prevent long GC pauses when running tests and to free up
       // memory in general when the toolbox is closed.
       if (gDevTools.testing) {
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -31,16 +31,17 @@ const MINIMUM_FONT_SIZE = 6;
 const NORMAL_FONT_SIZE = 12;
 
 const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
 const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
 const SHOW_TRAILING_SPACE = "devtools.scratchpad.showTrailingSpace";
 const ENABLE_AUTOCOMPLETION = "devtools.scratchpad.enableAutocompletion";
 const TAB_SIZE = "devtools.editor.tabsize";
+const FALLBACK_CHARSET_LIST = "intl.fallbackCharsetList.ISO-8859-1";
 
 const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesView.xul";
 
 const require   = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 
 const Telemetry = require("devtools/shared/telemetry");
 const Editor    = require("devtools/sourceeditor/editor");
 const TargetFactory = require("devtools/framework/target").TargetFactory;
@@ -1048,16 +1049,60 @@ var Scratchpad = {
       if (aCallback) {
         aCallback.call(this, Components.results.NS_ERROR_UNEXPECTED);
       }
     });
 
   },
 
   /**
+   * Get a list of applicable charsets.
+   * The best charset, defaulting to "UTF-8"
+   *
+   * @param string aBestCharset
+   * @return array of strings
+   */
+  _getApplicableCharsets: function SP__getApplicableCharsets(aBestCharset="UTF-8") {
+    let charsets = Services.prefs.getCharPref(
+      FALLBACK_CHARSET_LIST).split(",").filter(function (value) {
+      return value.length;
+    });
+    charsets.unshift(aBestCharset);
+    return charsets;
+  },
+
+  /**
+   * Get content converted to unicode, using a list of input charset to try.
+   *
+   * @param string aContent
+   * @param array of string aCharsetArray
+   * @return string
+   */
+  _getUnicodeContent: function SP__getUnicodeContent(aContent, aCharsetArray) {
+    let content = null,
+        converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter),
+        success = aCharsetArray.some(charset => {
+          try {
+            converter.charset = charset;
+            content = converter.ConvertToUnicode(aContent);
+            return true;
+          } catch (e) {
+            this.notificationBox.appendNotification(
+              this.strings.formatStringFromName("importFromFile.convert.failed",
+                                                [ charset ], 1),
+              "file-import-convert-failed",
+              null,
+              this.notificationBox.PRIORITY_WARNING_HIGH,
+              null);
+          }
+        });
+    return content;
+  },
+
+  /**
    * Read the content of a file and put it into the textbox.
    *
    * @param nsILocalFile aFile
    *        The file you want to save the textbox content into.
    * @param boolean aSilentError
    *        True if you do not want to display an error when file load fails,
    *        false otherwise.
    * @param function aCallback
@@ -1067,27 +1112,42 @@ var Scratchpad = {
    *        2) the data that was read from the file, if any.
    */
   importFromFile: function SP_importFromFile(aFile, aSilentError, aCallback)
   {
     // Prevent file type detection.
     let channel = NetUtil.newChannel(aFile);
     channel.contentType = "application/javascript";
 
+    this.notificationBox.removeAllNotifications(false);
+
     NetUtil.asyncFetch(channel, (aInputStream, aStatus) => {
       let content = null;
 
       if (Components.isSuccessCode(aStatus)) {
-        let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
-                        createInstance(Ci.nsIScriptableUnicodeConverter);
-        converter.charset = "UTF-8";
+        let charsets = this._getApplicableCharsets();
         content = NetUtil.readInputStreamToString(aInputStream,
                                                   aInputStream.available());
-        content = converter.ConvertToUnicode(content);
-
+        content = this._getUnicodeContent(content, charsets);
+        if (!content) {
+          let message = this.strings.formatStringFromName(
+            "importFromFile.convert.failed",
+            [ charsets.join(", ") ],
+            1);
+          this.notificationBox.appendNotification(
+            message,
+            "file-import-convert-failed",
+            null,
+            this.notificationBox.PRIORITY_CRITICAL_MEDIUM,
+            null);
+          if (aCallback) {
+            aCallback.call(this, aStatus, content);
+          }
+          return;
+        }
         // Check to see if the first line is a mode-line comment.
         let line = content.split("\n")[0];
         let modeline = this._scanModeLine(line);
         let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
 
         if (chrome && modeline["-sp-context"] === "browser") {
           this.setBrowserContext();
         }
@@ -1095,17 +1155,18 @@ var Scratchpad = {
         this.editor.setText(content);
         this.editor.clearHistory();
         this.dirty = false;
         document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
       }
       else if (!aSilentError) {
         window.alert(this.strings.GetStringFromName("openFile.failed"));
       }
-
+      this.setFilename(aFile.path);
+      this.setRecentFile(aFile);
       if (aCallback) {
         aCallback.call(this, aStatus, content);
       }
     });
   },
 
   /**
    * Open a file to edit in the Scratchpad.
@@ -1140,19 +1201,17 @@ var Scratchpad = {
               null,
               this.notificationBox.PRIORITY_WARNING_HIGH,
               null);
 
             this.clearFiles(aIndex, 1);
             return;
           }
 
-          this.setFilename(file.path);
           this.importFromFile(file, false);
-          this.setRecentFile(file);
         }
       });
     };
 
     if (aIndex > -1) {
       promptCallback();
     } else {
       let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/NS_ERROR_ILLEGAL_INPUT.txt
@@ -0,0 +1,2 @@
+Typ	Datum	Uhrzeit	Quelle	Kategorie	Ereignis	Benutzer	Computer
+Informationen	10.08.2012	16:07:11	MSDTC	Datenträger 	2444	Nicht zutreffend	
--- a/browser/devtools/scratchpad/test/browser.ini
+++ b/browser/devtools/scratchpad/test/browser.ini
@@ -28,16 +28,18 @@ skip-if = buildapp == 'mulet'
 [browser_scratchpad_contexts.js]
 [browser_scratchpad_execute_print.js]
 [browser_scratchpad_files.js]
 [browser_scratchpad_initialization.js]
 [browser_scratchpad_inspect.js]
 [browser_scratchpad_inspect_primitives.js]
 [browser_scratchpad_long_string.js]
 [browser_scratchpad_open.js]
+# test file:
+[NS_ERROR_ILLEGAL_INPUT.txt]
 [browser_scratchpad_open_error_console.js]
 [browser_scratchpad_throw_output.js]
 [browser_scratchpad_pprint-02.js]
 [browser_scratchpad_pprint.js]
 [browser_scratchpad_pprint_error_goto_line.js]
 [browser_scratchpad_restore.js]
 [browser_scratchpad_tab_switch.js]
 [browser_scratchpad_ui.js]
--- a/browser/devtools/scratchpad/test/browser_scratchpad_open.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_open.js
@@ -1,30 +1,31 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // only finish() when correct number of tests are done
-const expected = 3;
+const expected = 4;
 var count = 0;
 var lastUniqueName = null;
 
 function done()
 {
   if (++count == expected) {
     finish();
   }
 }
 
 function test()
 {
   waitForExplicitFinish();
   testOpen();
   testOpenWithState();
   testOpenInvalidState();
+  testOpenTestFile();
 }
 
 function testUniqueName(name)
 {
   ok(name, "Scratchpad has a uniqueName");
 
   if (lastUniqueName === null) {
     lastUniqueName = name;
@@ -69,8 +70,32 @@ function testOpenWithState()
 }
 
 function testOpenInvalidState()
 {
   let win = openScratchpad(null, {state: 7});
   ok(!win, "no scratchpad opened if state is not an object");
   done();
 }
+
+function testOpenTestFile()
+{
+  let win = openScratchpad(function(win) {
+    ok(win, "scratchpad opened for file open");
+    try {
+      win.Scratchpad.importFromFile(
+        "http://example.com/browser/browser/devtools/scratchpad/test/NS_ERROR_ILLEGAL_INPUT.txt",
+        "silent",
+        function (aStatus, content) {
+          let nb = win.document.querySelector('#scratchpad-notificationbox');
+          is(nb.querySelectorAll('notification').length, 1, "There is just one notification");
+          let cn = nb.currentNotification;
+          is(cn.priority, nb.PRIORITY_WARNING_HIGH, "notification priority is correct");
+          is(cn.value, "file-import-convert-failed", "notification value is corrent");
+          is(cn.type, "warning", "notification type is correct");
+          done();
+        });
+      ok(true, "importFromFile does not cause exception");
+    } catch (exception) {
+      ok(false, "importFromFile causes exception " + DevToolsUtils.safeErrorString(exception));
+    }
+  }, {noFocus: true});
+}
--- a/browser/devtools/shadereditor/panel.js
+++ b/browser/devtools/shadereditor/panel.js
@@ -61,12 +61,14 @@ ShaderEditorPanel.prototype = {
 
   destroy: function() {
     // Make sure this panel is not already destroyed.
     if (this._destroyer) {
       return this._destroyer;
     }
 
     return this._destroyer = this.panelWin.shutdownShaderEditor().then(() => {
+      // Destroy front to ensure packet handler is removed from client
+      this.panelWin.gFront.destroy();
       this.emit("destroyed");
     });
   }
 };
--- a/browser/devtools/storage/panel.js
+++ b/browser/devtools/storage/panel.js
@@ -59,16 +59,18 @@ StoragePanel.prototype = {
   },
 
   /**
    * Destroy the style editor.
    */
   destroy: function() {
     if (!this._destroyed) {
       this.UI.destroy();
+      // Destroy front to ensure packet handler is removed from client
+      this._front.destroy();
       this._destroyed = true;
 
       this._target.off("close", this.destroy);
       this._target = null;
       this._toolbox = null;
       this._panelDoc = null;
     }
 
--- a/browser/devtools/storage/ui.js
+++ b/browser/devtools/storage/ui.js
@@ -91,17 +91,17 @@ exports.StorageUI = StorageUI;
 
 StorageUI.prototype = {
 
   storageTypes: null,
   shouldResetColumns: true,
 
   destroy: function() {
     this.front.off("stores-update", this.onUpdate);
-    this._panelDoc.removeEventListener("keypress", this.handleKeypress)
+    this._panelDoc.removeEventListener("keypress", this.handleKeypress);
   },
 
   /**
    * Empties and hides the object viewer sidebar
    */
   hideSidebar: function() {
     this.view.empty();
     this.sidebar.hidden = true;
--- a/browser/devtools/timeline/panel.js
+++ b/browser/devtools/timeline/panel.js
@@ -52,12 +52,14 @@ TimelinePanel.prototype = {
 
   destroy: Task.async(function*() {
     // Make sure this panel is not already destroyed.
     if (this._destroyed) {
       return;
     }
 
     yield this.panelWin.shutdownTimeline();
+    // Destroy front to ensure packet handler is removed from client
+    this.panelWin.gFront.destroy();
     this.emit("destroyed");
     this._destroyed = true;
   })
 };
--- a/browser/devtools/webaudioeditor/panel.js
+++ b/browser/devtools/webaudioeditor/panel.js
@@ -56,12 +56,14 @@ WebAudioEditorPanel.prototype = {
 
   destroy: function() {
     // Make sure this panel is not already destroyed.
     if (this._destroyer) {
       return this._destroyer;
     }
 
     return this._destroyer = this.panelWin.shutdownWebAudioEditor().then(() => {
+      // Destroy front to ensure packet handler is removed from client
+      this.panelWin.gFront.destroy();
       this.emit("destroyed");
     });
   }
 };
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-bypass.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#bypass(), AudioNode#isBypassed()
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL);
+  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   is((yield gainNode.isBypassed()), false, "Nodes start off unbypassed.");
 
   info("Calling node#bypass(true)");
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParamFlags()
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
+  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
   let nodeTypes = [
     "AudioDestinationNode",
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParams()
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
+  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let allNodeParams = yield Promise.all(nodes.map(node => node.getParams()));
   let nodeTypes = [
     "AudioDestinationNode",
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-params-02.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that default properties are returned with the correct type
  * from the AudioNode actors.
  */
 
 function spawnTest() {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
+  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let allParams = yield Promise.all(nodes.map(node => node.getParams()));
   let types = [
     "AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-set-param.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParam() / AudioNode#setParam()
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL);
+  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = yield Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   let freq = yield oscNode.getParam("frequency");
   info(typeof freq);
   ise(freq, 440, "AudioNode:getParam correctly fetches AudioParam");
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-get-type.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getType()
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
+  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualTypes = yield Promise.all(nodes.map(node => node.getType()));
   let expectedTypes = [
     "AudioDestinationNode",
--- a/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-is-source.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#isSource()
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
+  let { target, front } = yield initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = yield Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualTypes = yield Promise.all(nodes.map(node => node.getType()));
   let isSourceResult = yield Promise.all(nodes.map(node => node.isSource()));
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_destroy-node-01.js
@@ -5,17 +5,17 @@
  * Tests that the destruction node event is fired and that the nodes are no
  * longer stored internally in the tool, that the graph is updated properly, and
  * that selecting a soon-to-be dead node clears the inspector.
  *
  * All done in one test since this test takes a few seconds to clear GC.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(DESTROY_NODES_URL);
+  let { target, panel } = yield initWebAudioEditor(DESTROY_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
   let destroyed = getN(gAudioNodes, "remove", 10);
--- a/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_first-run.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the reloading/onContentLoaded hooks work.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
     "The tool's content should initially be hidden.");
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-click.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the clicking on a node in the GraphView opens and sets
  * the correct node in the InspectorView
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let panelWin = panel.panelWin;
   let { gFront, $, $$, InspectorView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
   let [actors, _] = yield Promise.all([
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-markers.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the SVG marker styling is updated when devtools theme changes.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, MARKER_STYLING } = panelWin;
 
   let currentTheme = Services.prefs.getCharPref("devtools.theme");
 
   ok(MARKER_STYLING.light, "Marker styling exists for light theme.");
   ok(MARKER_STYLING.dark, "Marker styling exists for dark theme.");
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-01.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that SVG nodes and edges were created for the Graph View.
  */
 
 let connectCount = 0;
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
   gAudioNodes.on("connect", onConnectNode);
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests more edge rendering for complex graphs.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$ } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
   let [actors] = yield Promise.all([
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-03.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that selected nodes stay selected on graph redraw.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(CONNECT_TOGGLE_URL);
+  let { target, panel } = yield initWebAudioEditor(CONNECT_TOGGLE_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 2)
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-04.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests audio param connection rendering.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
+  let { target, panel } = yield initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
   let [actors] = yield Promise.all([
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-render-05.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that param connections trigger graph redraws
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(CONNECT_TOGGLE_PARAM_URL);
+  let { target, panel } = yield initWebAudioEditor(CONNECT_TOGGLE_PARAM_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 1, 0)
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-selected.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that SVG nodes and edges were created for the Graph View.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
   let [actors] = yield Promise.all([
--- a/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_graph-zoom.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the graph's scale and position is reset on a page reload.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, ContextView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   yield Promise.all([
     reload(target),
     waitForGraphRendered(panelWin, 3, 2)
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector-toggle.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the inspector toggle button shows and hides
  * the inspector panel as intended.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_inspector.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that inspector view opens on graph node click, and
  * loads the correct node inside the inspector.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_navigate.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests naviating from a page to another will repopulate
  * the audio graph if both pages have an AudioContext.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
   reload(target);
 
   var [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that properties are updated when modifying the VariablesView.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-edit-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that properties are not updated when modifying the VariablesView.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
@@ -30,17 +30,17 @@ function waitForDeviceClosed() {
       deferred.resolve();
     }
   });
 
   return deferred.promise;
 }
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(MEDIA_NODES_URL);
+  let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   // Auto enable getUserMedia
   let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
   Services.prefs.setBoolPref(MEDIA_PERMISSION, true);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params-objects.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view correctly displays non-primitive properties
  * like AudioBuffer and Float32Array in properties of AudioNodes.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
+  let { target, panel } = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view-params.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view correctly displays all properties for nodes
  * correctly, with default values and correct types.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_NODES_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_properties-view.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view shows params when they exist, and are hidden otherwise.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   reload(target);
 
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-01.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that reloading a tab will properly listen for the `start-context`
  * event and reshow the tools after reloading.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
     "The tool's content should initially be hidden.");
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-02.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests reloading a tab with the tools open properly cleans up
  * the graph.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-03.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests reloading a tab with the tools open properly cleans up
  * the inspector and selected node.
  */
 
 function spawnTest() {
-  let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, InspectorView } = panelWin;
 
   reload(target);
 
   let [actors] = yield Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
--- a/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
+++ b/browser/devtools/webaudioeditor/test/browser_wa_reset-04.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that switching to an iframe works fine.
  */
 
 function spawnTest() {
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 
-  let [target, debuggee, panel, toolbox] = yield initWebAudioEditor(IFRAME_CONTEXT_URL);
+  let { target, panel, toolbox } = yield initWebAudioEditor(IFRAME_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
     "The tool's content should initially be hidden.");
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-connect-param.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test the `connect-param` event on the web audio actor.
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(CONNECT_PARAM_URL);
+  let { target, front } = yield initBackend(CONNECT_PARAM_URL);
   let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = yield Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     getN(front, "create-node", 4),
     get2(front, "connect-node"),
     once(front, "connect-param")
   ]);
 
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test `destroy-node` event on WebAudioActor.
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(DESTROY_NODES_URL);
+  let { target, front } = yield initBackend(DESTROY_NODES_URL);
 
   let waitUntilDestroyed = getN(front, "destroy-node", 10);
   let [, , created] = yield Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     // Should create 1 destination node and 10 disposable buffer nodes
     getN(front, "create-node", 13)
   ]);
--- a/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
+++ b/browser/devtools/webaudioeditor/test/browser_webaudio-actor-simple.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test basic communication of Web Audio actor
  */
 
 function spawnTest () {
-  let [target, debuggee, front] = yield initBackend(SIMPLE_CONTEXT_URL);
+  let { target, front } = yield initBackend(SIMPLE_CONTEXT_URL);
   let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = yield Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     get3(front, "create-node"),
     get2(front, "connect-node")
   ]);
 
   let destType = yield destNode.getType();
--- a/browser/devtools/webaudioeditor/test/head.js
+++ b/browser/devtools/webaudioeditor/test/head.js
@@ -133,39 +133,37 @@ function initBackend(aUrl) {
   if (!DebuggerServer.initialized) {
     DebuggerServer.init(() => true);
     DebuggerServer.addBrowserActors();
   }
 
   return Task.spawn(function*() {
     let tab = yield addTab(aUrl);
     let target = TargetFactory.forTab(tab);
-    let debuggee = target.window.wrappedJSObject;
 
     yield target.makeRemote();
 
     let front = new WebAudioFront(target.client, target.form);
-    return [target, debuggee, front];
+    return { target, front };
   });
 }
 
 function initWebAudioEditor(aUrl) {
   info("Initializing a web audio editor pane.");
 
   return Task.spawn(function*() {
     let tab = yield addTab(aUrl);
     let target = TargetFactory.forTab(tab);
-    let debuggee = target.window.wrappedJSObject;
 
     yield target.makeRemote();
 
     Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
     let toolbox = yield gDevTools.showToolbox(target, "webaudioeditor");
     let panel = toolbox.getCurrentPanel();
-    return [target, debuggee, panel, toolbox];
+    return { target, panel, toolbox };
   });
 }
 
 function teardown(aPanel) {
   info("Destroying the web audio editor.");
 
   return Promise.all([
     once(aPanel, "destroyed"),
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -57,17 +57,17 @@ let UI = {
 
     this.updateCommands();
     this.updateRuntimeList();
 
     this.onfocus = this.onfocus.bind(this);
     window.addEventListener("focus", this.onfocus, true);
 
     AppProjects.load().then(() => {
-      this.openLastProject();
+      this.autoSelectProject();
     });
 
     // Auto install the ADB Addon Helper. Only once.
     // If the user decides to uninstall the addon, we won't install it again.
     let autoInstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
     if (autoInstallADBHelper && !Devices.helperAddonInstalled) {
       GetAvailableAddons().then(addons => {
         addons.adb.install();
@@ -75,31 +75,16 @@ let UI = {
     }
     Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
 
     this.lastConnectedRuntime = Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
 
     this.setupDeck();
   },
 
-  openLastProject: function() {
-    let lastProjectLocation = Services.prefs.getCharPref("devtools.webide.lastprojectlocation");
-    let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
-    if (lastProjectLocation && shouldRestore) {
-      let lastProject = AppProjects.get(lastProjectLocation);
-      if (lastProject) {
-        AppManager.selectedProject = lastProject;
-      } else {
-        AppManager.selectedProject = null;
-      }
-    } else {
-      AppManager.selectedProject = null;
-    }
-  },
-
   uninit: function() {
     window.removeEventListener("focus", this.onfocus, true);
     AppManager.off("app-manager-update", this.appManagerUpdate);
     AppManager.uninit();
     window.removeEventListener("message", this.onMessage);
   },
 
   canWindowClose: function() {
@@ -136,16 +121,17 @@ let UI = {
       case "project":
         this._updatePromise = Task.spawn(function() {
           UI.updateTitle();
           yield UI.destroyToolbox();
           UI.updateCommands();
           UI.updateProjectButton();
           UI.openProject();
           UI.autoStartProject();
+          UI.saveLastSelectedProject();
         });
         return;
       case "project-is-not-running":
       case "project-is-running":
       case "list-tabs-response":
         this.updateCommands();
         break;
       case "runtime":
@@ -155,16 +141,20 @@ let UI = {
       case "project-validated":
         this.updateTitle();
         this.updateCommands();
         this.updateProjectButton();
         this.updateProjectEditorHeader();
         break;
       case "install-progress":
         this.updateProgress(Math.round(100 * details.bytesSent / details.totalBytes));
+        break;
+      case "runtime-apps-found":
+        this.autoSelectProject();
+        break;
     };
     this._updatePromise = promise.resolve();
   },
 
   openInBrowser: function(url) {
     // Open a URL in a Firefox window
     let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
     if (browserWin) {
@@ -492,22 +482,16 @@ let UI = {
 
     // Nothing to show
 
     if (!project) {
       this.resetDeck();
       return;
     }
 
-    // Save last project location
-
-    if (project.location) {
-      Services.prefs.setCharPref("devtools.webide.lastprojectlocation", project.location);
-    }
-
     // Make sure the directory exist before we show Project Editor
 
     let forceDetailsOnly = false;
     if (project.type == "packaged") {
       forceDetailsOnly = !utils.doesFileExist(project.location);
     }
 
     // Show only the details screen
@@ -565,16 +549,99 @@ let UI = {
 
     // Validate project
     yield AppManager.validateProject(project);
 
     // Select project
     AppManager.selectedProject = project;
   }),
 
+  // Remember the last selected project on the runtime
+  saveLastSelectedProject: function() {
+    let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
+    if (!shouldRestore) {
+      return;
+    }
+
+    // Ignore unselection of project on runtime disconnection
+    if (AppManager.connection.status != Connection.Status.CONNECTED) {
+      return;
+    }
+
+    let project = "", type = "";
+    let selected = AppManager.selectedProject;
+    if (selected) {
+      if (selected.type == "runtimeApp") {
+        type = "runtimeApp";
+        project = selected.app.manifestURL;
+      } else if (selected.type == "mainProcess") {
+        type = "mainProcess";
+      } else if (selected.type == "packaged" ||
+                 selected.type == "hosted") {
+        type = "local";
+        project = selected.location;
+      }
+    }
+    if (type) {
+      Services.prefs.setCharPref("devtools.webide.lastSelectedProject",
+                                 type + ":" + project);
+    } else {
+      Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
+    }
+  },
+
+  autoSelectProject: function() {
+    if (AppManager.selectedProject) {
+      return;
+    }
+    let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
+    if (!shouldRestore) {
+      return;
+    }
+    let pref = Services.prefs.getCharPref("devtools.webide.lastSelectedProject");
+    if (!pref) {
+      return;
+    }
+    let m = pref.match(/^(\w+):(.*)$/);
+    if (!m) {
+      return;
+    }
+    let [_, type, project] = m;
+
+    if (type == "local") {
+      let lastProject = AppProjects.get(project);
+      if (lastProject) {
+        AppManager.selectedProject = lastProject;
+      }
+    }
+
+    // For other project types, we need to be connected to the runtime
+    if (AppManager.connection.status != Connection.Status.CONNECTED) {
+      return;
+    }
+
+    if (type == "mainProcess" && AppManager.isMainProcessDebuggable()) {
+      AppManager.selectedProject = {
+        type: "mainProcess",
+        name: Strings.GetStringFromName("mainProcess_label"),
+        icon: AppManager.DEFAULT_PROJECT_ICON
+      }
+    } else if (type == "runtimeApp") {
+      let app = AppManager.apps.get(project);
+      if (app) {
+        AppManager.selectedProject = {
+          type: "runtimeApp",
+          app: app.manifest,
+          icon: app.iconURL,
+          name: app.manifest.name
+        };
+      }
+    }
+  },
+
   /********** DECK **********/
 
   setupDeck: function() {
     let iframes = document.querySelectorAll("#deck > iframe");
     for (let iframe of iframes) {
       iframe.tooltip = "aHTMLTooltip";
     }
   },
@@ -882,19 +949,22 @@ let Cmds = {
         panelNode.addEventListener("popupshown", onPopupShown);
         panelNode.openPopup(anchorNode);
         panelVboxNode.scrollTop = 0;
       }, 0);
     }, deferred.reject);
 
 
     let runtimeappsHeaderNode = document.querySelector("#panel-header-runtimeapps");
-    let sortedApps = AppManager.webAppsStore.object.all;
+    let sortedApps = [];
+    for (let [manifestURL, app] of AppManager.apps) {
+      sortedApps.push(app);
+    }
     sortedApps = sortedApps.sort((a, b) => {
-      return a.name > b.name;
+      return a.manifest.name > b.manifest.name;
     });
     let mainProcess = AppManager.isMainProcessDebuggable();
     if (AppManager.connection.status == Connection.Status.CONNECTED &&
         (sortedApps.length > 0 || mainProcess)) {
       runtimeappsHeaderNode.removeAttribute("hidden");
     } else {
       runtimeappsHeaderNode.setAttribute("hidden", "true");
     }
@@ -919,26 +989,26 @@ let Cmds = {
         };
       }, true);
     }
 
     for (let i = 0; i < sortedApps.length; i++) {
       let app = sortedApps[i];
       let panelItemNode = document.createElement("toolbarbutton");
       panelItemNode.className = "panel-item";
-      panelItemNode.setAttribute("label", app.name);
+      panelItemNode.setAttribute("label", app.manifest.name);
       panelItemNode.setAttribute("image", app.iconURL);
       runtimeAppsNode.appendChild(panelItemNode);
       panelItemNode.addEventListener("click", () => {
         UI.hidePanels();
         AppManager.selectedProject = {
           type: "runtimeApp",
-          app: app,
+          app: app.manifest,
           icon: app.iconURL,
-          name: app.name
+          name: app.manifest.name
         };
       }, true);
     }
 
     // Build the tab list right now, so it's fast...
     this._buildProjectPanelTabs();
 
     // But re-list them and rebuild, in case any tabs navigated since the last
--- a/browser/devtools/webide/modules/app-manager.js
+++ b/browser/devtools/webide/modules/app-manager.js
@@ -9,21 +9,20 @@ let { Promise: promise } = Cu.import("re
 const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
 const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
 const {TextEncoder, OS}  = Cu.import("resource://gre/modules/osfile.jsm", {});
 const {AppProjects} = require("devtools/app-manager/app-projects");
-const WebappsStore = require("devtools/app-manager/webapps-store");
 const TabStore = require("devtools/webide/tab-store");
 const {AppValidator} = require("devtools/app-manager/app-validator");
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
-const AppActorFront = require("devtools/app-actor-front");
+const {AppActorFront} = require("devtools/app-actor-front");
 const {getDeviceFront} = require("devtools/server/actors/device");
 const {getPreferenceFront} = require("devtools/server/actors/preference");
 const {setTimeout} = require("sdk/timers");
 const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 const {USBRuntime, WiFiRuntime, SimulatorRuntime,
        gLocalRuntime, gRemoteRuntime} = require("devtools/webide/runtimes");
 const discovery = require("devtools/toolkit/discovery/discovery");
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
@@ -41,19 +40,16 @@ exports.AppManager = AppManager = {
   init: function() {
     let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
     let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
 
     this.connection = ConnectionManager.createConnection("localhost", port);
     this.onConnectionChanged = this.onConnectionChanged.bind(this);
     this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
 
-    this.onWebAppsStoreready = this.onWebAppsStoreready.bind(this);
-    this.webAppsStore = new WebappsStore(this.connection);
-    this.webAppsStore.on("store-ready", this.onWebAppsStoreready);
     this.tabStore = new TabStore(this.connection);
     this.onTabNavigate = this.onTabNavigate.bind(this);
     this.onTabClosed = this.onTabClosed.bind(this);
     this.tabStore.on("navigate", this.onTabNavigate);
     this.tabStore.on("closed", this.onTabClosed);
 
     this.runtimeList = {
       usb: [],
@@ -64,35 +60,28 @@ exports.AppManager = AppManager = {
     if (Services.prefs.getBoolPref("devtools.webide.enableLocalRuntime")) {
       this.runtimeList.custom.push(gLocalRuntime);
     }
     this.trackUSBRuntimes();
     this.trackWiFiRuntimes();
     this.trackSimulatorRuntimes();
 
     this.onInstallProgress = this.onInstallProgress.bind(this);
-    AppActorFront.on("install-progress", this.onInstallProgress);
 
     this.observe = this.observe.bind(this);
     Services.prefs.addObserver(WIFI_SCANNING_PREF, this, false);
   },
 
   uninit: function() {
-    AppActorFront.off("install-progress", this.onInstallProgress);
-    this._unlistenToApps();
     this.selectedProject = null;
     this.selectedRuntime = null;
     this.untrackUSBRuntimes();
     this.untrackWiFiRuntimes();
     this.untrackSimulatorRuntimes();
-    this._runningApps.clear();
     this.runtimeList = null;
-    this.webAppsStore.off("store-ready", this.onWebAppsStoreready);
-    this.webAppsStore.destroy();
-    this.webAppsStore = null;
     this.tabStore.off("navigate", this.onTabNavigate);
     this.tabStore.off("closed", this.onTabClosed);
     this.tabStore.destroy();
     this.tabStore = null;
     this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
     this._listTabsResponse = null;
     this.connection.disconnect();
     this.connection = null;
@@ -131,89 +120,62 @@ exports.AppManager = AppManager = {
 
   onConnectionChanged: function() {
     if (this.connection.status == Connection.Status.DISCONNECTED) {
       this.selectedRuntime = null;
     }
 
     if (this.connection.status != Connection.Status.CONNECTED) {
       console.log("Connection status changed: " + this.connection.status);
-      this._runningApps.clear();
-      this._unlistenToApps();
+      if (this._appsFront) {
+        this._appsFront.off("install-progress", this.onInstallProgress);
+        this._appsFront.unwatchApps();
+        this._appsFront = null;
+      }
       this._listTabsResponse = null;
     } else {
       this.connection.client.listTabs((response) => {
-        this._listenToApps();
+        let front = new AppActorFront(this.connection.client,
+                                      response);
+        front.on("install-progress", this.onInstallProgress);
+        front.watchApps(() => this.checkIfProjectIsRunning())
+             .then(() => front.fetchIcons())
+             .then(() => {
+               this._appsFront = front;
+               this.checkIfProjectIsRunning();
+               this.update("runtime-apps-found");
+             });
         this._listTabsResponse = response;
-        this._getRunningApps();
         this.update("list-tabs-response");
       });
     }
 
     this.update("connection");
   },
 
-  onInstallProgress: function(event, details) {
-    this.update("install-progress", details);
-  },
-
-  onWebAppsStoreready: function() {
-    this.update("runtime-apps-found");
+  get apps() {
+    if (this._appsFront) {
+      return this._appsFront.apps;
+    } else {
+      return new Map();
+    }
   },
 
-  _runningApps: new Set(),
-  _getRunningApps: function() {
-    let client = this.connection.client;
-    if (!this._listTabsResponse.webappsActor) {
-      return;
-    }
-    let request = {
-      to: this._listTabsResponse.webappsActor,
-      type: "listRunningApps"
-    };
-    client.request(request, (res) => {
-      if (res.error) {
-        this.reportError("error_listRunningApps");
-        console.error("listRunningApps error: " + res.error);
-      }
-      for (let m of res.apps) {
-        this._runningApps.add(m);
-      }
-    });
-    this.checkIfProjectIsRunning();
-  },
-  _listenToApps: function() {
-    let client = this.connection.client;
-    client.addListener("appOpen", (type, { manifestURL }) => {
-      this._runningApps.add(manifestURL);
-      this.checkIfProjectIsRunning();
-    });
-
-    client.addListener("appClose", (type, { manifestURL }) => {
-      this._runningApps.delete(manifestURL);
-      this.checkIfProjectIsRunning();
-    });
-
-    client.addListener("appUninstall", (type, { manifestURL }) => {
-      this._runningApps.delete(manifestURL);
-      this.checkIfProjectIsRunning();
-    });
-  },
-  _unlistenToApps: function() {
-    // Is that even possible?
-    // connection.client is null now.
+  onInstallProgress: function(event, details) {
+    this.update("install-progress", details);
   },
 
   isProjectRunning: function() {
     if (this.selectedProject.type == "mainProcess" ||
         this.selectedProject.type == "tab") {
       return true;
     }
-    let manifest = this.getProjectManifestURL(this.selectedProject);
-    return manifest && this._runningApps.has(manifest);
+
+    let app = this._getProjectFront(this.selectedProject);
+    return app && app.running;
   },
 
   checkIfProjectIsRunning: function() {
     if (this.selectedProject) {
       if (this.isProjectRunning()) {
         this.update("project-is-running");
       } else {
         this.update("project-is-not-running");
@@ -255,63 +217,56 @@ exports.AppManager = AppManager = {
   },
 
   reloadTab: function() {
     if (this.selectedProject && this.selectedProject.type != "tab") {
       return promise.reject("tried to reload non-tab project");
     }
     return this.getTarget().then(target => {
       target.activeTab.reload();
-    });
+    }, console.error.bind(console));
   },
 
   getTarget: function() {
-    let client = this.connection.client;
-
     if (this.selectedProject.type == "mainProcess") {
       return devtools.TargetFactory.forRemoteTab({
         form: this._listTabsResponse,
         client: this.connection.client,
         chrome: true
       });
     }
 
     if (this.selectedProject.type == "tab") {
       return this.tabStore.getTargetForTab();
     }
 
-    let manifest = this.getProjectManifestURL(this.selectedProject);
-    if (!manifest) {
-      console.error("Can't find manifestURL for selected project");
-      return promise.reject();
+    let app = this._getProjectFront(this.selectedProject);
+    if (!app) {
+      return promise.reject("Can't find app front for selected project");
     }
 
-    let actor = this._listTabsResponse.webappsActor;
     return Task.spawn(function* () {
       // Once we asked the app to launch, the app isn't necessary completely loaded.
       // launch request only ask the app to launch and immediatly returns.
       // We have to keep trying to get app tab actors required to create its target.
 
       for (let i = 0; i < 10; i++) {
         try {
-          let target = yield AppActorFront.getTargetForApp(client, actor, manifest);
-          // Success
-          return target;
+          return yield app.getTarget();
         } catch(e) {}
         let deferred = promise.defer();
         setTimeout(deferred.resolve, 500);
         yield deferred.promise;
       }
 
-      AppManager.reportError("error_cantConnectToApp", manifest);
+      AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL);
       throw new Error("can't connect to app");
     });
   },
 
-
   getProjectManifestURL: function(project) {
     let manifest = null;
     if (project.type == "runtimeApp") {
       manifest = project.app.manifestURL;
     }
 
     if (project.type == "hosted") {
       manifest = project.location;
@@ -319,16 +274,24 @@ exports.AppManager = AppManager = {
 
     if (project.type == "packaged" && project.packagedAppOrigin) {
       manifest = "app://" + project.packagedAppOrigin + "/manifest.webapp";
     }
 
     return manifest;
   },
 
+  _getProjectFront: function(project) {
+    let manifest = this.getProjectManifestURL(project);
+    if (manifest && this._appsFront) {
+      return this._appsFront.apps.get(manifest);
+    }
+    return null;
+  },
+
   _selectedProject: null,
   set selectedProject(value) {
     // A regular comparison still sees a difference when equal in some cases
     if (JSON.stringify(this._selectedProject) !==
         JSON.stringify(value)) {
       this._selectedProject = value;
 
       // Clear out tab store's selected state, if any
@@ -440,33 +403,29 @@ exports.AppManager = AppManager = {
     this.connection.disconnect();
     return deferred.promise;
   },
 
   launchRuntimeApp: function() {
     if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
       return promise.reject("attempting to launch a non-runtime app");
     }
-    let client = this.connection.client;
-    let actor = this._listTabsResponse.webappsActor;
-    let manifest = this.getProjectManifestURL(this.selectedProject);
-    return AppActorFront.launchApp(client, actor, manifest);
+    let app = this._getProjectFront(this.selectedProject);
+    return app.launch();
   },
 
   launchOrReloadRuntimeApp: function() {
     if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
       return promise.reject("attempting to launch / reload a non-runtime app");
     }
-    let client = this.connection.client;
-    let actor = this._listTabsResponse.webappsActor;
-    let manifest = this.getProjectManifestURL(this.selectedProject);
-    if (!this.isProjectRunning()) {
-      return AppActorFront.launchApp(client, actor, manifest);
+    let app = this._getProjectFront(this.selectedProject);
+    if (!app.running) {
+      return app.launch();
     } else {
-      return AppActorFront.reloadApp(client, actor, manifest);
+      return app.reload();
     }
   },
 
   installAndRunProject: function() {
     let project = this.selectedProject;
 
     if (!project || (project.type != "packaged" && project.type != "hosted")) {
       console.error("Can't install project. Unknown type of project.");
@@ -483,74 +442,67 @@ exports.AppManager = AppManager = {
 
       yield self.validateProject(project);
 
       if (project.errorsCount > 0) {
         self.reportError("error_cantInstallValidationErrors");
         return;
       }
 
-      let client = self.connection.client;
-      let actor = self._listTabsResponse.webappsActor;
       let installPromise;
 
       if (project.type != "packaged" && project.type != "hosted") {
         return promise.reject("Don't know how to install project");
       }
 
+      let response;
       if (project.type == "packaged") {
-        let {appId} = yield AppActorFront.installPackaged(client,
-                                                          actor,
-                                                          project.location,
-                                                          project.packagedAppOrigin);
+        response = yield self._appsFront.installPackaged(project.location,
+                                                             project.packagedAppOrigin);
+
         // If the packaged app specified a custom origin override,
         // we need to update the local project origin
-        project.packagedAppOrigin = appId;
+        project.packagedAppOrigin = response.appId;
         // And ensure the indexed db on disk is also updated
         AppProjects.update(project);
       }
 
       if (project.type == "hosted") {
         let manifestURLObject = Services.io.newURI(project.location, null, null);
         let origin = Services.io.newURI(manifestURLObject.prePath, null, null);
         let appId = origin.host;
         let metadata = {
           origin: origin.spec,
           manifestURL: project.location
         };
-        yield AppActorFront.installHosted(client,
-                                          actor,
-                                          appId,
-                                          metadata,
-                                          project.manifest);
+        response = yield self._appsFront.installHosted(appId,
+                                            metadata,
+                                            project.manifest);
       }
 
-      let manifest = self.getProjectManifestURL(project);
-      if (!self._runningApps.has(manifest)) {
+      let {app} = response;
+      if (!app.running) {
         let deferred = promise.defer();
         self.on("app-manager-update", function onUpdate(event, what) {
           if (what == "project-is-running") {
             self.off("app-manager-update", onUpdate);
             deferred.resolve();
           }
         });
-        yield AppActorFront.launchApp(client, actor, manifest);
+        yield app.launch();
         yield deferred.promise;
-
       } else {
-        yield AppActorFront.reloadApp(client, actor, manifest);
+        yield app.reload();
       }
     });
   },
 
   stopRunningApp: function() {
-    let client = this.connection.client;
-    let actor = this._listTabsResponse.webappsActor;
-    let manifest = this.getProjectManifestURL(this.selectedProject);
-    return AppActorFront.closeApp(client, actor, manifest);
+    let app = this._getProjectFront(this.selectedProject);
+    return app.close();
   },
 
   /* PROJECT VALIDATION */
 
   validateProject: function(project) {
     if (!project) {
       return promise.reject();
     }
--- a/browser/devtools/webide/test/test_basic.html
+++ b/browser/devtools/webide/test/test_basic.html
@@ -21,17 +21,16 @@
         Task.spawn(function* () {
             let win = yield openWebIDE();
 
             ok(win, "Found a window");
             ok(win.AppManager, "App Manager accessible");
             let appmgr = win.AppManager;
             ok(appmgr.connection, "App Manager connection ready");
             ok(appmgr.runtimeList, "Runtime list ready");
-            ok(appmgr.webAppsStore, "WebApps store ready");
 
             // test error reporting
             let nbox = win.document.querySelector("#notificationbox");
             let notification =  nbox.getNotificationWithValue("webide:errornotification");
             ok(!notification, "No notification yet");
             let deferred = promise.defer();
             nextTick().then(() => {
               deferred.reject("BOOM!");
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -1,18 +1,18 @@
 # -*- indent-tabs-mode: nil; js-indent-level: 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/.
 
 pref("devtools.webide.showProjectEditor", true);
 pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
 pref("devtools.webide.autoinstallADBHelper", true);
-pref("devtools.webide.lastprojectlocation", "");
 pref("devtools.webide.restoreLastProject", true);
 pref("devtools.webide.enableLocalRuntime", false);
 pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
 pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/#VERSION#/#OS#/fxos_#SLASHED_VERSION#_simulator-#OS#-latest.xpi");
 pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
 pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
 pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
 pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
 pref("devtools.webide.lastConnectedRuntime", "");
+pref("devtools.webide.lastSelectedProject", "");
--- a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js
+++ b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js
@@ -19,17 +19,19 @@
 
 'use strict';
 
 /*
  * pdfjschildbootstrap.js loads into the content process to take care of
  * initializing our built-in version of pdfjs when running remote.
  */
 
+Components.utils.import('resource://gre/modules/Services.jsm');
 Components.utils.import('resource://pdf.js/PdfJs.jsm');
 Components.utils.import('resource://pdf.js/PdfjsContentUtils.jsm');
 
 // init content utils shim pdfjs will use to access privileged apis.
 PdfjsContentUtils.init();
 
-// register various pdfjs factories that hook us into content loading.
-PdfJs.updateRegistration();
-
+if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+  // register various pdfjs factories that hook us into content loading.
+  PdfJs.updateRegistration();
+}
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
@@ -27,16 +27,20 @@ scratchpadContext.invalid=Scratchpad can
 # LOCALIZATION NOTE  (openFile.title): This is the file picker title, when you
 # open a file from Scratchpad.
 openFile.title=Open File
 
 # LOCALIZATION NOTE  (openFile.failed): This is the message displayed when file
 # open fails.
 openFile.failed=Failed to read the file.
 
+# LOCALIZATION NOTE  (openFile.failed): This is the message displayed when file
+# open fails.
+importFromFile.convert.failed=Failed to convert file to Unicode from %1$S.
+
 # LOCALIZATION NOTE (clearRecentMenuItems.label): This is the label for the
 # menuitem in the 'Open Recent'-menu which clears all recent files.
 clearRecentMenuItems.label=Clear Items
 
 # LOCALIZATION NOTE  (saveFileAs): This is the file picker title, when you save
 # a file in Scratchpad.
 saveFileAs=Save File As
 
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -280,17 +280,17 @@ PluginContent.prototype = {
     let eventType = event.type;
 
     if (eventType == "unload") {
       this.uninit();
       return;
     }
 
     if (eventType == "PluginRemoved") {
-      this.updateNotificationUI();
+      this.updateNotificationUI(event.target);
       return;
     }
 
     if (eventType == "click") {
       this.onOverlayClick(event);
       return;
     }
 
@@ -693,17 +693,39 @@ PluginContent.prototype = {
 
     this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
       plugins: [... this.pluginData.values()],
       showNow: showNow,
       host: principalHost,
     }, null, principal);
   },
 
-  updateNotificationUI: function () {
+  /**
+   * Updates the "hidden plugin" notification bar UI.
+   *
+   * @param document (optional)
+   *        Specify the document that is causing the update.
+   *        This is useful when the document is possibly no longer
+   *        the current loaded document (for example, if we're
+   *        responding to a PluginRemoved event for an unloading
+   *        document). If this parameter is omitted, it defaults
+   *        to the current top-level document.
+   */
+  updateNotificationUI: function (document) {
+    let principal;
+
+    if (document) {
+      // We're only interested in the top-level document, since that's
+      // the one that provides the Principal that we send back to the
+      // parent.
+      principal = document.defaultView.top.document.nodePrincipal;
+    } else {
+      principal = this.content.document.nodePrincipal;
+    }
+
     // Make a copy of the actions from the last popup notification.
     let haveInsecure = false;
     let actions = new Map();
     for (let action of this.pluginData.values()) {
       switch (action.fallbackType) {
         // haveInsecure will trigger the red flashing icon and the infobar
         // styling below
         case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
@@ -713,19 +735,18 @@ PluginContent.prototype = {
 
         case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
           actions.set(action.permissionString, action);
           continue;
       }
     }
 
     // Remove plugins that are already active, or large enough to show an overlay.
-    let contentWindow = this.global.content;
-    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindowUtils);
+    let cwu = this.content.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindowUtils);
     for (let plugin of cwu.plugins) {
       let info = this._getPluginInfo(plugin);
       if (!actions.has(info.permissionString)) {
         continue;
       }
       let fallbackType = info.fallbackType;
       if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
         actions.delete(info.permissionString);
@@ -750,17 +771,16 @@ PluginContent.prototype = {
         if (actions.size == 0) {
           break;
         }
       }
     }
 
     // If there are any items remaining in `actions` now, they are hidden
     // plugins that need a notification bar.
-    let principal = contentWindow.document.nodePrincipal;
     this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
       haveInsecure: haveInsecure,
       actions: [... actions.values()],
       host: this._getHostFromPrincipal(principal),
     }, null, principal);
   },
 
   removeNotification: function (name) {
--- a/browser/themes/shared/newtab/controls.svg
+++ b/browser/themes/shared/newtab/controls.svg
@@ -1,28 +1,32 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <svg version="1.1"
      id="icons-enhanced-tiles"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      x="0"
      y="0"
-     width="256"
+     width="288"
      height="32"
-     viewBox="0 0 256 32">
+     viewBox="0 0 288 32">
 
   <defs>
     <style type="text/css"><![CDATA[
       /* Glyph Styles */
 
       .glyphShape-style {
         fill: #737373;
       }
 
+      .glyphShape-style-pin {
+        fill: #b4b4b4;
+      }
+
       .glyphShape-style-hover-gear {
         fill: #4a90e2;
       }
 
       .glyphShape-style-hover-pin {
         fill: #4a90e2;
       }
 
@@ -100,9 +104,13 @@
   </g>
 
   <g id="icon-delete-hover-active" transform="translate(224)">
     <use xlink:href="#glyphShape-circle" class="glyphShape-style-circle-dropshadow" />
     <use xlink:href="#glyphShape-circle" class="glyphShape-style-circle" />
     <use xlink:href="#glyphShape-delete" class="glyphShape-style-hover-active" />
   </g>
 
+  <g id="icon-pin-default" transform="translate(256)">
+    <use xlink:href="#glyphShape-pin"    class="glyphShape-style-pin" />
+  </g>
+
 </svg>
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -117,40 +117,55 @@
   background-origin: padding-box;
   background-clip: padding-box;
   background-repeat: no-repeat;
   background-size: cover;
   border-radius: inherit;
   transition: opacity 100ms ease-out;
 }
 
-.newtab-thumbnail.enhanced-content:hover {
+.newtab-site:hover .newtab-thumbnail.enhanced-content {
   opacity: 0;
 }
 
 .newtab-site[type=affiliate] .newtab-thumbnail,
 .newtab-site[type=enhanced] .newtab-thumbnail,
 .newtab-site[type=organic] .newtab-thumbnail,
 .newtab-site[type=sponsored] .newtab-thumbnail {
   background-position: center center;
   background-size: auto;
 }
 
 /* TITLES */
+.newtab-sponsored,
 .newtab-title {
-  color: #737373;
+  color: #5c5c5c;
 }
 
 .newtab-site:hover .newtab-title {
-  color: #4a4a4a;
+  color: #222;
 }
 
 .newtab-site[pinned] .newtab-title {
-  color: #2c72c4;
-  font-weight: bold;
+  padding: 0 15px;
+}
+
+.newtab-site[pinned] .newtab-title::before {
+  background-image: -moz-image-rect(url("chrome://browser/skin/newtab/controls.svg"), 7, 278, 28, 266);
+  background-size: 10px;
+  content: "";
+  height: 17px;
+  left: 0;
+  position: absolute;
+  width: 10px;
+}
+
+.newtab-site[pinned] .newtab-title:-moz-locale-dir(rtl)::before {
+  left: auto;
+  right: 0;
 }
 
 /* CONTROLS */
 .newtab-control {
   background-color: transparent;
   background-size: 24px;
   border: none;
   height: 24px;
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -300,16 +300,18 @@ nsInProcessTabChildGlobal::PreHandleEven
 #endif
 
   return NS_OK;
 }
 
 nsresult
 nsInProcessTabChildGlobal::InitTabChildGlobal()
 {
+  // If you change this, please change GetCompartmentName() in XPCJSRuntime.cpp
+  // accordingly.
   nsAutoCString id;
   id.AssignLiteral("inProcessTabChildGlobal");
   nsIURI* uri = mOwner->OwnerDoc()->GetDocumentURI();
   if (uri) {
     nsAutoCString u;
     uri->GetSpec(u);
     id.AppendLiteral("?ownedBy=");
     id.Append(u);
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -63,17 +63,17 @@
 #include "nsFrameLoader.h"
 
 #include "nsObjectLoadingContent.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 #include "GeckoProfiler.h"
-#include "nsObjectFrame.h"
+#include "nsPluginFrame.h"
 #include "nsDOMClassInfo.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMJSUtils.h"
 
 #include "nsWidgetsCID.h"
 #include "nsContentCID.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/dom/BindingUtils.h"
@@ -855,17 +855,17 @@ nsObjectLoadingContent::InstantiatePlugi
 
   mInstanceOwner = newOwner;
 
   // Ensure the frame did not change during instantiation re-entry (common).
   // HasNewFrame would not have mInstanceOwner yet, so the new frame would be
   // dangling. (Bug 854082)
   nsIFrame* frame = thisContent->GetPrimaryFrame();
   if (frame && mInstanceOwner) {
-    mInstanceOwner->SetFrame(static_cast<nsObjectFrame*>(frame));
+    mInstanceOwner->SetFrame(static_cast<nsPluginFrame*>(frame));
 
     // Bug 870216 - Adobe Reader renders with incorrect dimensions until it gets
     // a second SetWindow call. This is otherwise redundant.
     mInstanceOwner->CallSetWindow();
   }
 
   // Set up scripting interfaces.
   NotifyContentObjectWrapper();
@@ -1291,17 +1291,17 @@ nsObjectLoadingContent::HasNewFrame(nsIO
     // We are successfully setup as type plugin, but have not spawned an
     // instance due to a lack of a frame.
     AsyncStartPluginInstance();
     return NS_OK;
   }
 
   // Otherwise, we're just changing frames
   // Set up relationship between instance owner and frame.
-  nsObjectFrame *objFrame = static_cast<nsObjectFrame*>(aFrame);
+  nsPluginFrame *objFrame = static_cast<nsPluginFrame*>(aFrame);
   mInstanceOwner->SetFrame(objFrame);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetPluginInstance(nsNPAPIPluginInstance** aInstance)
 {
@@ -2713,23 +2713,23 @@ nsObjectLoadingContent::GetTypeOfContent
   if (caps & eSupportPlugins && PluginExistsForType(aMIMEType.get())) {
     // ShouldPlay will handle checking for disabled plugins
     return eType_Plugin;
   }
 
   return eType_Null;
 }
 
-nsObjectFrame*
+nsPluginFrame*
 nsObjectLoadingContent::GetExistingFrame()
 {
   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsIFrame* frame = thisContent->GetPrimaryFrame();
   nsIObjectFrame* objFrame = do_QueryFrame(frame);
-  return static_cast<nsObjectFrame*>(objFrame);
+  return static_cast<nsPluginFrame*>(objFrame);
 }
 
 void
 nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
 {
   nsImageLoadingContent::CreateStaticImageClone(aDest);
 
   aDest->mType = mType;
@@ -3571,17 +3571,17 @@ nsObjectLoadingContent::SetupProtoChain(
 nsresult
 nsObjectLoadingContent::GetPluginJSObject(JSContext *cx,
                                           JS::Handle<JSObject*> obj,
                                           nsNPAPIPluginInstance *plugin_inst,
                                           JS::MutableHandle<JSObject*> plugin_obj,
                                           JS::MutableHandle<JSObject*> plugin_proto)
 {
   // NB: We need an AutoEnterCompartment because we can be called from
-  // nsObjectFrame when the plugin loads after the JS object for our content
+  // nsPluginFrame when the plugin loads after the JS object for our content
   // node has been created.
   JSAutoCompartment ac(cx, obj);
 
   if (plugin_inst) {
     plugin_inst->GetJSObject(cx, plugin_obj.address());
     if (plugin_obj) {
       if (!::JS_GetPrototype(cx, plugin_obj, plugin_proto)) {
         return NS_ERROR_UNEXPECTED;
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -21,17 +21,17 @@
 #include "nsIRunnable.h"
 #include "nsIThreadInternal.h"
 #include "nsIFrame.h"
 #include "nsIFrameLoader.h"
 
 class nsAsyncInstantiateEvent;
 class nsStopPluginRunnable;
 class AutoSetInstantiatingToFalse;
-class nsObjectFrame;
+class nsPluginFrame;
 class nsFrameLoader;
 class nsXULElement;
 class nsPluginInstanceOwner;
 
 namespace mozilla {
 namespace dom {
 template<typename T> class Sequence;
 struct MozPluginParameter;
@@ -497,17 +497,17 @@ class nsObjectLoadingContent : public ns
      *      click-to-play or other content policy checks
      */
     ObjectType GetTypeOfContent(const nsCString& aMIMEType);
 
     /**
      * Gets the frame that's associated with this content node.
      * Does not flush.
      */
-    nsObjectFrame* GetExistingFrame();
+    nsPluginFrame* GetExistingFrame();
 
     // Helper class for SetupProtoChain
     class SetupProtoChainRunner MOZ_FINAL : public nsIRunnable
     {
       ~SetupProtoChainRunner();
     public:
       NS_DECL_ISUPPORTS
 
--- a/content/media/fmp4/moz.build
+++ b/content/media/fmp4/moz.build
@@ -7,21 +7,21 @@
 EXPORTS += [
     'MP4Decoder.h',
     'MP4Reader.h',
     'PlatformDecoderModule.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlankDecoderModule.cpp',
+    'MP4Decoder.cpp',
     'PlatformDecoderModule.cpp',
 ]
 
 SOURCES += [
-    'MP4Decoder.cpp',
     'MP4Reader.cpp',
 ]
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
 
 if CONFIG['MOZ_EME']:
     DIRS += ['eme']
--- a/content/media/omx/AudioOffloadPlayer.cpp
+++ b/content/media/omx/AudioOffloadPlayer.cpp
@@ -17,16 +17,18 @@
  * limitations under the License.
  */
 
 #include "AudioOffloadPlayer.h"
 #include "nsComponentManagerUtils.h"
 #include "nsITimer.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "VideoUtils.h"
+#include "mozilla/dom/power/PowerManagerService.h"
+#include "mozilla/dom/WakeLock.h"
 
 #include <binder/IPCThreadState.h>
 #include <stagefright/foundation/ADebug.h>
 #include <stagefright/foundation/ALooper.h>
 #include <stagefright/MediaDefs.h>
 #include <stagefright/MediaErrors.h>
 #include <stagefright/MediaSource.h>
 #include <stagefright/MetaData.h>
@@ -210,28 +212,31 @@ status_t AudioOffloadPlayer::ChangeState
     default:
       break;
   }
   return OK;
 }
 
 static void ResetCallback(nsITimer* aTimer, void* aClosure)
 {
+  AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __FUNCTION__));
   AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure);
   if (player) {
     player->Reset();
   }
 }
 
 void AudioOffloadPlayer::Pause(bool aPlayPendingSamples)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mStarted) {
     CHECK(mAudioSink.get());
+    WakeLockCreate();
+
     if (aPlayPendingSamples) {
       mAudioSink->Stop();
     } else {
       mAudioSink->Pause();
     }
     mPlaying = false;
   }
 
@@ -247,16 +252,17 @@ void AudioOffloadPlayer::Pause(bool aPla
 
 status_t AudioOffloadPlayer::Play()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mResetTimer) {
     mResetTimer->Cancel();
     mResetTimer = nullptr;
+    WakeLockRelease();
   }
 
   status_t err = OK;
 
   if (!mStarted) {
     // Last pause timed out and offloaded audio sink was reset. Start it again
     err = Start(false);
     if (err != OK) {
@@ -276,16 +282,17 @@ status_t AudioOffloadPlayer::Play()
     }
   }
 
   return err;
 }
 
 void AudioOffloadPlayer::Reset()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   if (!mStarted) {
     return;
   }
 
   CHECK(mAudioSink.get());
 
   AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("reset: mPlaying=%d mReachedEOS=%d",
       mPlaying, mReachedEOS));
@@ -318,16 +325,18 @@ void AudioOffloadPlayer::Reset()
 
   IPCThreadState::self()->flushCommands();
   StopTimeUpdate();
 
   mReachedEOS = false;
   mStarted = false;
   mPlaying = false;
   mStartPosUs = 0;
+
+  WakeLockRelease();
 }
 
 status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
 {
   MOZ_ASSERT(NS_IsMainThread());
   CHECK(mAudioSink.get());
 
   android::Mutex::Autolock autoLock(mLock);
@@ -706,9 +715,35 @@ void AudioOffloadPlayer::SendMetaDataToH
 
 void AudioOffloadPlayer::SetVolume(double aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
   CHECK(mAudioSink.get());
   mAudioSink->SetVolume((float) aVolume);
 }
 
+void AudioOffloadPlayer::WakeLockCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __FUNCTION__));
+  if (!mWakeLock) {
+    nsRefPtr<dom::power::PowerManagerService> pmService =
+      dom::power::PowerManagerService::GetInstance();
+    NS_ENSURE_TRUE_VOID(pmService);
+
+    ErrorResult rv;
+    mWakeLock = pmService->NewWakeLock(NS_LITERAL_STRING("cpu"), nullptr, rv);
+  }
+}
+
+void AudioOffloadPlayer::WakeLockRelease()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __FUNCTION__));
+  if (mWakeLock) {
+    ErrorResult rv;
+    mWakeLock->Unlock(rv);
+    NS_WARN_IF_FALSE(!rv.Failed(), "Failed to unlock the wakelock.");
+    mWakeLock = nullptr;
+  }
+}
+
 } // namespace mozilla
--- a/content/media/omx/AudioOffloadPlayer.h
+++ b/content/media/omx/AudioOffloadPlayer.h
@@ -28,16 +28,20 @@
 
 #include "AudioOutput.h"
 #include "AudioOffloadPlayerBase.h"
 #include "MediaDecoderOwner.h"
 #include "MediaOmxCommonDecoder.h"
 
 namespace mozilla {
 
+namespace dom {
+class WakeLock;
+}
+
 /**
  * AudioOffloadPlayer adds support for audio tunneling to a digital signal
  * processor (DSP) in the device chipset. With tunneling, audio decoding is
  * off-loaded to the DSP, waking the application processor less often and using
  * less battery
  *
  * This depends on offloading capability provided by Android KK AudioTrack class
  *
@@ -188,16 +192,20 @@ private:
   // Timer to trigger position changed events
   nsCOMPtr<nsITimer> mTimeUpdateTimer;
 
   // Timer to reset AudioSink when audio is paused for OFFLOAD_PAUSE_MAX_USECS.
   // It is triggered in Pause() and canceled when there is a Play() within
   // OFFLOAD_PAUSE_MAX_USECS. Used only from main thread so no lock is needed.
   nsCOMPtr<nsITimer> mResetTimer;
 
+  // To avoid device suspend when mResetTimer is going to be triggered.
+  // Used only from main thread so no lock is needed.
+  nsRefPtr<mozilla::dom::WakeLock> mWakeLock;
+
   int64_t GetMediaTimeUs();
 
   // Provide the playback position in microseconds from total number of
   // frames played by audio track
   int64_t GetOutputPlayPositionUs_l() const;
 
   // Fill the buffer given by audio sink with data from compressed audio
   // source. Also handles the seek by seeking audio source and stop the sink in
@@ -235,16 +243,19 @@ private:
   // (>1sec) But when Player UI is visible we need to update progress bar
   // atleast once in 250ms. Start a timer when player UI becomes visible or
   // audio starts playing to send PlaybackPositionChanged events once in 250ms.
   // Stop the timer when UI goes invisible or play state is not playing.
   // Also make sure timer functions are always called from main thread
   nsresult StartTimeUpdate();
   nsresult StopTimeUpdate();
 
+  void WakeLockCreate();
+  void WakeLockRelease();
+
   // Notify end of stream by sending PlaybackEnded event to observer
   // (i.e.MediaDecoder)
   void NotifyAudioEOS();
 
   // Notify position changed event by sending PlaybackPositionChanged event to
   // observer
   void NotifyPositionChanged();
 
--- a/content/media/test/mochitest.ini
+++ b/content/media/test/mochitest.ini
@@ -443,16 +443,17 @@ skip-if = true # bug 1021673
 [test_seekLies.html]
 [test_source.html]
 [test_source_media.html]
 [test_source_null.html]
 [test_source_write.html]
 [test_standalone.html]
 [test_streams_autoplay.html]
 [test_streams_element_capture.html]
+skip-if = e10s && os == 'win' # Bug 1065881 - Crash on child process shutdown in ShadowLayerForwarder::InWorkerThread
 [test_streams_element_capture_createObjectURL.html]
 [test_streams_element_capture_playback.html]
 [test_streams_element_capture_reset.html]
 skip-if = buildapp == 'b2g' # bug 901102
 [test_streams_gc.html]
 skip-if = buildapp == 'b2g' # bug 1021682
 [test_streams_srcObject.html]
 [test_streams_tracks.html]
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -109,22 +109,18 @@ Animation::GetComputedTimingAt(const Nul
   const TimeDuration& localTime = aLocalTime.Value();
 
   // When we finish exactly at the end of an iteration we need to report
   // the end of the final iteration and not the start of the next iteration
   // so we set up a flag for that case.
   bool isEndOfFinalIteration = false;
 
   // Get the normalized time within the active interval.
-  TimeDuration activeTime;
-  // FIXME: The following check that the active duration is not equal to Forever
-  // is a temporary workaround to avoid overflow and should be removed once
-  // bug 1039924 is fixed.
-  if (result.mActiveDuration != TimeDuration::Forever() &&
-      localTime >= aTiming.mDelay + result.mActiveDuration) {
+  StickyTimeDuration activeTime;
+  if (localTime >= aTiming.mDelay + result.mActiveDuration) {
     result.mPhase = ComputedTiming::AnimationPhase_After;
     if (!aTiming.FillsForwards()) {
       // The animation isn't active or filling at this time.
       result.mTimeFraction = ComputedTiming::kNullTimeFraction;
       return result;
     }
     activeTime = result.mActiveDuration;
     // Note that infinity == floor(infinity) so this will also be true when we
@@ -143,20 +139,20 @@ Animation::GetComputedTimingAt(const Nul
   } else {
     MOZ_ASSERT(result.mActiveDuration != zeroDuration,
                "How can we be in the middle of a zero-duration interval?");
     result.mPhase = ComputedTiming::AnimationPhase_Active;
     activeTime = localTime - aTiming.mDelay;
   }
 
   // Get the position within the current iteration.
-  TimeDuration iterationTime;
+  StickyTimeDuration iterationTime;
   if (aTiming.mIterationDuration != zeroDuration) {
     iterationTime = isEndOfFinalIteration
-                    ? aTiming.mIterationDuration
+                    ? StickyTimeDuration(aTiming.mIterationDuration)
                     : activeTime % aTiming.mIterationDuration;
   } /* else, iterationTime is zero */
 
   // Determine the 0-based index of the current iteration.
   if (isEndOfFinalIteration) {
     result.mCurrentIteration =
       aTiming.mIterationCount == NS_IEEEPositiveInfinity()
       ? UINT64_MAX // FIXME: When we return this via the API we'll need
@@ -210,29 +206,30 @@ Animation::GetComputedTimingAt(const Nul
   }
   if (thisIterationReverse) {
     result.mTimeFraction = 1.0 - result.mTimeFraction;
   }
 
   return result;
 }
 
-TimeDuration
+StickyTimeDuration
 Animation::ActiveDuration(const AnimationTiming& aTiming)
 {
   if (aTiming.mIterationCount == mozilla::PositiveInfinity<float>()) {
     // An animation that repeats forever has an infinite active duration
     // unless its iteration duration is zero, in which case it has a zero
     // active duration.
-    const TimeDuration zeroDuration;
+    const StickyTimeDuration zeroDuration;
     return aTiming.mIterationDuration == zeroDuration
            ? zeroDuration
-           : TimeDuration::Forever();
+           : StickyTimeDuration::Forever();
   }
-  return aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount);
+  return StickyTimeDuration(
+    aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount));
 }
 
 bool
 Animation::HasAnimationOfProperty(nsCSSProperty aProperty) const
 {
   for (size_t propIdx = 0, propEnd = mProperties.Length();
        propIdx != propEnd; ++propIdx) {
     if (aProperty == mProperties[propIdx].mProperty) {
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_dom_Animation_h
 #define mozilla_dom_Animation_h
 
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocument.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h" // for nsTimingFunction
 
 struct JSContext;
 
@@ -56,18 +57,19 @@ struct ComputedTiming
     : mTimeFraction(kNullTimeFraction)
     , mCurrentIteration(0)
     , mPhase(AnimationPhase_Null)
   { }
 
   static const double kNullTimeFraction;
 
   // The total duration of the animation including all iterations.
-  // Will equal TimeDuration::Forever() if the animation repeats indefinitely.
-  TimeDuration mActiveDuration;
+  // Will equal StickyTimeDuration::Forever() if the animation repeats
+  // indefinitely.
+  StickyTimeDuration mActiveDuration;
 
   // Will be kNullTimeFraction if the animation is neither animating nor
   // filling at the sampled time.
   double mTimeFraction;
 
   // Zero-based iteration index (meaningless if mTimeFraction is
   // kNullTimeFraction).
   uint64_t mCurrentIteration;
@@ -200,17 +202,18 @@ public:
   // Shortcut for that gets the computed timing using the current local time as
   // calculated from the timeline time.
   ComputedTiming GetComputedTiming(const AnimationTiming* aTiming
                                      = nullptr) const {
     return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming);
   }
 
   // Return the duration of the active interval for the given timing parameters.
-  static TimeDuration ActiveDuration(const AnimationTiming& aTiming);
+  static StickyTimeDuration
+  ActiveDuration(const AnimationTiming& aTiming);
 
   // After transitions finish they need to be retained for one throttle-able
   // cycle (for reasons see explanation in
   // layout/style/nsTransitionManager.cpp).
   // In the meantime, however, they should be ignored.
   bool IsFinishedTransition() const {
     return mIsFinishedTransition;
   }
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -36,16 +36,21 @@ nsPerformanceTiming::nsPerformanceTiming
     mChannel(aChannel),
     mFetchStart(0.0),
     mZeroTime(aZeroTime),
     mTimingAllowed(true),
     mReportCrossOriginRedirect(true)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
   SetIsDOMBinding();
+
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    mZeroTime = 0;
+  }
+
   // The aHttpChannel argument is null if this nsPerformanceTiming object
   // is being used for the navigation timing (document) and has a non-null
   // value for the resource timing (any resources within the page).
   if (aHttpChannel) {
     mTimingAllowed = CheckAllowedOrigin(aHttpChannel);
     bool redirectsPassCheck = false;
     mChannel->GetAllRedirectsPassTimingAllowCheck(&redirectsPassCheck);
     mReportCrossOriginRedirect = mTimingAllowed && redirectsPassCheck;
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -46,17 +46,17 @@ GetCameraLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog) {
     sLog = PR_NewLogModule("Camera");
   }
   return sLog;
 }
 
-WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
+::WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
 
 nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow)
   : mWindowId(aWindow->WindowID())
   , mPermission(nsIPermissionManager::DENY_ACTION)
   , mWindow(aWindow)
 {
   /* member initializers and constructor code */
   DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
@@ -104,17 +104,17 @@ nsDOMCameraManager::CheckPermission(nsPI
 }
 
 /* static */
 already_AddRefed<nsDOMCameraManager>
 nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow)
 {
   // Initialize the shared active window tracker
   if (!sActiveWindows) {
-    sActiveWindows = new WindowTable();
+    sActiveWindows = new ::WindowTable();
   }
 
   nsRefPtr<nsDOMCameraManager> cameraManager =
     new nsDOMCameraManager(aWindow);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     DOM_CAMERA_LOGE("Camera manager failed to get observer service\n");
--- a/dom/camera/moz.build
+++ b/dom/camera/moz.build
@@ -8,17 +8,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
     MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'CameraCommon.h',
     'CameraPreferences.h',
     'DOMCameraManager.h',
 ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'CameraControlImpl.cpp',
     'CameraPreferences.cpp',
     'CameraPreviewMediaStream.cpp',
     'CameraRecorderProfiles.cpp',
     'DOMCameraCapabilities.cpp',
     'DOMCameraControl.cpp',
     'DOMCameraControlListener.cpp',
     'DOMCameraDetectedFace.cpp',
@@ -33,17 +33,17 @@ if CONFIG['MOZ_B2G_CAMERA']:
         'GonkCameraParameters.cpp',
         'GonkCameraSource.cpp',
         'GonkRecorder.cpp',
         'GonkRecorderProfiles.cpp',
         'TestGonkCameraControl.cpp',
         'TestGonkCameraHardware.cpp',
     ]
 else:
-    SOURCES += [
+    UNIFIED_SOURCES += [
         'FallbackCameraControl.cpp',
         'FallbackCameraManager.cpp',
     ]
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '../base',
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3098,17 +3098,17 @@ CanvasRenderingContext2D::GetHitRegionRe
  * Used for nsBidiPresUtils::ProcessText
  */
 struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
 {
   typedef CanvasRenderingContext2D::ContextState ContextState;
 
   virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
   {
-    mFontgrp->UpdateFontList(); // ensure user font generation is current
+    mFontgrp->UpdateUserFonts(); // ensure user font generation is current
     mTextRun = mFontgrp->MakeTextRun(text,
                                      length,
                                      mThebes,
                                      mAppUnitsPerDevPixel,
                                      direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
   }
 
   virtual nscoord GetWidth()
@@ -3450,19 +3450,19 @@ CanvasRenderingContext2D::DrawOrMeasureT
     anchorX = 0;
   } else {
     anchorX = 1;
   }
 
   processor.mPt.x -= anchorX * totalWidth;
 
   // offset pt.y based on text baseline
-  processor.mFontgrp->UpdateFontList(); // ensure user font generation is current
-  NS_ASSERTION(processor.mFontgrp->FontListLength()>0, "font group contains no fonts");
-  const gfxFont::Metrics& fontMetrics = processor.mFontgrp->GetFontAt(0)->GetMetrics();
+  processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
+  const gfxFont::Metrics& fontMetrics =
+    processor.mFontgrp->GetFirstValidFont()->GetMetrics();
 
   gfxFloat anchorY;
 
   switch (state.textBaseline)
   {
   case TextBaseline::HANGING:
       // fall through; best we can do with the information available
   case TextBaseline::TOP:
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 support-files =
   webgl-mochitest/driver-info.js
   webgl-mochitest/webgl-util.js
 
+[webgl-mochitest/test-backbuffer-channels.html]
+[webgl-mochitest/test-hidden-alpha.html]
 [webgl-mochitest/test_depth_readpixels.html]
 [webgl-mochitest/test_draw.html]
 [webgl-mochitest/test_fb_param.html]
 [webgl-mochitest/test_fb_param_crash.html]
 [webgl-mochitest/test_highp_fs.html]
 [webgl-mochitest/test_no_arr_points.html]
 [webgl-mochitest/test_noprog_draw.html]
 [webgl-mochitest/test_privileged_exts.html]
--- a/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py
+++ b/dom/canvas/test/webgl-mochitest/mochi-to-testcase.py
@@ -4,17 +4,17 @@ import re
 
 assert len(sys.argv) == 2
 mochiPath = sys.argv[1]
 
 extDotPos = mochiPath.find('.html')
 assert extDotPos != -1, 'mochitest target must be an html doc.'
 
 testPath = mochiPath[:extDotPos] + '.solo.html'
-    
+
 def ReadLocalFile(include):
     incPath = os.path.dirname(mochiPath)
     filePath = os.path.join(incPath, include)
 
     data = None
     try:
         f = open(filePath, 'r')
         data = f.read()
@@ -26,61 +26,68 @@ def ReadLocalFile(include):
     except:
         pass
 
     return data
 
 kSimpleTestReplacement = '''\n
 <script>
 // SimpleTest.js replacement
+
+function debug(text) {
+  var elem = document.getElementById('mochi-to-testcase-output');
+  elem.innerHTML += '\\n<br/>\\n' + text;
+}
+
 function ok(val, text) {
-  var elem = document.getElementById('mochi-to-testcase-output');
   var status = val ? 'Test <font color=\\'green\\'>passed</font>: '
                    : 'Test <font color=\\'red\\'  >FAILED</font>: ';
-  elem.innerHTML += '\\n<br/>\\n' + status + text;
+  debug(status + text);
 }
 
 function todo(val, text) {
-  ok(!val, 'Todo: ' + text);
+  var status = val ? 'Test <font color=\\'orange\\'>UNEXPECTED PASS</font>: '
+                   : 'Test <font color=\\'blue\\'  >todo</font>: ';
+  debug(status + text);
 }
 </script>
 <div id='mochi-to-testcase-output'></div>
 \n'''
 
-fin = open(mochiPath, 'r')
-fout = open(testPath, 'w')
+fin = open(mochiPath, 'rb')
+fout = open(testPath, 'wb')
 includePattern = re.compile('<script\\s*src=[\'"](.*)\\.js[\'"]>\\s*</script>')
 cssPattern = re.compile('<link\\s*rel=[\'"]stylesheet[\'"]\\s*href=[\'"]([^=>]*)[\'"]>')
 for line in fin:
     skipLine = False
     for css in cssPattern.findall(line):
         skipLine = True
         print('Ignoring stylesheet: ' + css)
-    
+
     for inc in includePattern.findall(line):
         skipLine = True
         if inc == '/MochiKit/MochiKit':
             continue
 
         if inc == '/tests/SimpleTest/SimpleTest':
             print('Injecting SimpleTest replacement')
             fout.write(kSimpleTestReplacement);
             continue
-            
+
         incData = ReadLocalFile(inc + '.js')
         if not incData:
             print('Warning: Unknown JS file ignored: ' + inc + '.js')
             continue
 
         print('Injecting include: ' + inc + '.js')
         fout.write('\n<script>\n// Imported from: ' + inc + '.js\n');
         fout.write(incData);
         fout.write('\n</script>\n');
         continue
 
     if skipLine:
         continue
-    
+
     fout.write(line)
     continue
 
 fin.close()
 fout.close()
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test-backbuffer-channels.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<title>WebGL test: bug 958723</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="driver-info.js"></script>
+<script src="webgl-util.js"></script>
+<body>
+<script>
+
+function TestAttribs(attribs) {
+  debug('Testing attribs: ' + JSON.stringify(attribs));
+  var canvas = document.createElement('canvas');
+  var gl = canvas.getContext('experimental-webgl', attribs);
+  ok(gl, 'No tested attribs should result in failure to create a context');
+  if (!gl)
+    return;
+
+  var actual = gl.getContextAttributes();
+
+  ok(actual.alpha == attribs.alpha,
+     'Resulting `alpha` should match request.');
+  ok(actual.premultipliedAlpha == attribs.premultipliedAlpha,
+     'Resulting `premultipliedAlpha` should match request.');
+  ok(actual.preserveDrawingBuffer == attribs.preserveDrawingBuffer,
+     'Resulting `preserveDrawingBuffer` should match request.');
+
+  // "The depth, stencil and antialias attributes, when set to true, are
+  // requests, not requirements."
+  if (!attribs.antialias) {
+    ok(!actual.antialias, 'No `antialias` if not requested.');
+  }
+  if (!attribs.depth) {
+    ok(!actual.depth, 'No `depth` if not requested.');
+  }
+  if (!attribs.stencil) {
+    ok(!actual.stencil, 'No `stencil` if not requested.');
+  }
+
+  var hasAlpha = !!gl.getParameter(gl.ALPHA_BITS);
+  var hasDepth = !!gl.getParameter(gl.DEPTH_BITS);
+  var hasStencil = !!gl.getParameter(gl.STENCIL_BITS);
+  var hasAntialias = !!gl.getParameter(gl.SAMPLES);
+
+  ok(hasAlpha == actual.alpha, 'Bits should match `alpha` attrib.');
+  ok(hasAntialias == actual.antialias, 'Bits should match `antialias` attrib.');
+  ok(hasDepth == actual.depth, 'Bits should match `depth` attrib.');
+  ok(hasStencil == actual.stencil, 'Bits should match `stencil` attrib.');
+}
+
+function CloneAttribs(attribs) {
+  return {
+    alpha: attribs.alpha,
+    antialias: attribs.antialias,
+    depth: attribs.depth,
+    premultipliedAlpha: attribs.premultipliedAlpha,
+    preserveDrawingBuffer: attribs.preserveDrawingBuffer,
+    stencil: attribs.stencil,
+  };
+}
+
+function SplitForAttrib(list, attrib) {
+  var ret = [];
+
+  for (var i in list) {
+    var cur = list[i];
+    if (cur[attrib])
+      throw 'Attrib is already true.';
+
+    var clone = CloneAttribs(cur);
+    clone[attrib] = true;
+
+    ret.push(cur);
+    ret.push(clone);
+  }
+
+  return ret;
+}
+
+function GenAttribList() {
+  var base = {
+    alpha: false,
+    antialias: false,
+    depth: false,
+    premultipliedAlpha: false,
+    preserveDrawingBuffer: false,
+    stencil: false,
+  };
+  var list = [base];
+  list = SplitForAttrib(list, 'alpha');
+  list = SplitForAttrib(list, 'antialias');
+  list = SplitForAttrib(list, 'depth');
+  list = SplitForAttrib(list, 'premultipliedAlpha');
+  list = SplitForAttrib(list, 'preserveDrawingBuffer');
+  list = SplitForAttrib(list, 'stencil');
+
+  if (list.length != 1<<6)
+    throw 'Attribs list length wrong: ' + list.length;
+
+  return list;
+}
+
+var list = GenAttribList();
+for (var i in list) {
+  var attribs = list[i];
+  TestAttribs(attribs);
+}
+
+ok(true, 'Test complete.');
+
+</script>
+
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test-hidden-alpha.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML>
+<title>WebGL test: Hidden alpha on no-alpha contexts</title>
+<script src='/tests/SimpleTest/SimpleTest.js'></script>
+<link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+<script src='driver-info.js'></script>
+<script src='webgl-util.js'></script>
+<body>
+<script id='vs' type='x-shader/x-vertex'>
+  attribute vec2 aPosCoord;
+
+  void main(void) {
+    gl_Position = vec4(aPosCoord, 0.0, 1.0);
+  }
+</script>
+
+<script id='fs' type='x-shader/x-fragment'>
+  precision mediump float;
+
+  void main(void) {
+    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+  }
+</script>
+<canvas id='canvas' style='border: none;' width='100' height='100'></canvas>
+<script>
+
+var posCoords_arr = new Float32Array(2 * 4);
+var posCoords_buff = null;
+function DrawQuad(gl, prog, x0, y0, x1, y1) {
+  gl.useProgram(prog);
+
+  if (!posCoords_buff) {
+    posCoords_buff = gl.createBuffer();
+  }
+  gl.bindBuffer(gl.ARRAY_BUFFER, posCoords_buff);
+  posCoords_arr[0] = x0;
+  posCoords_arr[1] = y0;
+
+  posCoords_arr[2] = x1;
+  posCoords_arr[3] = y0;
+
+  posCoords_arr[4] = x0;
+  posCoords_arr[5] = y1;
+
+  posCoords_arr[6] = x1;
+  posCoords_arr[7] = y1;
+  gl.bufferData(gl.ARRAY_BUFFER, posCoords_arr, gl.STREAM_DRAW);
+
+  gl.enableVertexAttribArray(prog.aPosCoord);
+  gl.vertexAttribPointer(prog.aPosCoord, 2, gl.FLOAT, false, 0, 0);
+
+  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+}
+
+function DrawSquare(gl, prog, size) {
+  DrawQuad(gl, prog, -size, -size, size, size);
+}
+
+function Reset(gl) {
+  gl.canvas.width += 1;
+  gl.canvas.width -= 1;
+}
+
+function ReadCenterPixel(gl) {
+  var w = gl.drawingbufferWidth;
+  var h = gl.drawingbufferHeight;
+  var ret = new Uint8Array(4);
+  gl.readPixels(w/2, h/2, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, ret);
+  return ret;
+}
+
+function Test(gl, prog) {
+  gl.enable(gl.BLEND);
+  gl.blendFunc(gl.ZERO, gl.DST_ALPHA);
+
+  var iColor = 64;
+  var fColor = iColor / 255.0;
+
+  //////////////////
+
+  debug('clear(R,G,B,0)');
+
+  Reset(gl);
+
+  gl.clearColor(fColor, fColor, fColor, 0.0);
+  gl.clear(gl.COLOR_BUFFER_BIT);
+
+  var dataURL_pre = gl.canvas.toDataURL();
+  //console.log('Before blending: ' + dataURL_pre);
+
+  DrawSquare(gl, prog, 0.7);
+
+  var pixel = ReadCenterPixel(gl);
+  ok(pixel[0] == iColor &&
+     pixel[1] == iColor &&
+     pixel[2] == iColor, 'Color should be the same.');
+  ok(pixel[3] == 255, 'No-alpha should always readback as 1.0 alpha.');
+
+  var dataURL_post = gl.canvas.toDataURL();
+  //console.log('After blending: ' + dataURL_post);
+  ok(dataURL_post == dataURL_pre,
+     'toDataURL should be unchanged after blending.');
+
+  //////////////////
+
+  debug('mask(R,G,B,0), clear(R,G,B,1)');
+
+  Reset(gl);
+
+  gl.colorMask(true, true, true, false);
+  gl.clearColor(fColor, fColor, fColor, 1.0);
+  gl.clear(gl.COLOR_BUFFER_BIT);
+  gl.colorMask(true, true, true, true);
+
+  dataURL_pre = gl.canvas.toDataURL();
+  //console.log('Before blending: ' + dataURL_pre);
+
+  DrawSquare(gl, prog, 0.7);
+
+  var pixel = ReadCenterPixel(gl);
+  ok(pixel[0] == iColor &&
+     pixel[1] == iColor &&
+     pixel[2] == iColor, 'Color should be the same.');
+  ok(pixel[3] == 255, 'No-alpha should always readback as 1.0 alpha.');
+  ok(gl.getError() == 0, 'Should have no errors.');
+
+  dataURL_post = gl.canvas.toDataURL();
+  //console.log('After blending: ' + dataURL_post);
+  ok(dataURL_post == dataURL_pre,
+     'toDataURL should be unchanged after blending.');
+
+  ok(true, 'Test complete.');
+}
+
+(function(){
+  var canvas = document.getElementById('canvas');
+  var attribs = {
+    alpha: false,
+    antialias: false,
+    premultipliedAlpha: false,
+  };
+  var gl = canvas.getContext('experimental-webgl', attribs);
+  ok(gl, 'WebGL should work.');
+  ok(gl.getParameter(gl.ALPHA_BITS) == 0, 'Shouldn\'t have alpha bits.');
+
+  var prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs');
+  ok(prog, 'Program should link.');
+  prog.aPosCoord = gl.getAttribLocation(prog, 'aPosCoord');
+
+  setTimeout(function(){ Test(gl, prog); }, 500);
+})();
+
+</script>
+</body>
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -18,17 +18,17 @@ using mozilla::DefaultXDisplay;
 #include "gfxUtils.h"
 #include "nsIRunnable.h"
 #include "nsContentUtils.h"
 #include "nsRect.h"
 #include "nsSize.h"
 #include "nsDisplayList.h"
 #include "ImageLayers.h"
 #include "GLImages.h"
-#include "nsObjectFrame.h"
+#include "nsPluginFrame.h"
 #include "nsIPluginDocument.h"
 #include "nsIStringStream.h"
 #include "nsNetUtil.h"
 #include "mozilla/Preferences.h"
 #include "nsILinkHandler.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsLayoutUtils.h"
@@ -217,17 +217,17 @@ nsPluginInstanceOwner::GetImageContainer
   // for what we do on other versions.
   if (AndroidBridge::Bridge()->GetAPIVersion() < 11)
     return nullptr;
 
   LayoutDeviceRect r = GetPluginRect();
 
   // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
   // into, set y-flip flags, etc, so we do this at the beginning.
-  gfxSize resolution = mObjectFrame->PresContext()->PresShell()->GetCumulativeResolution();
+  gfxSize resolution = mPluginFrame->PresContext()->PresShell()->GetCumulativeResolution();
   ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution.width, resolution.height)).Size();
   mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
 
   container = LayerManager::CreateImageContainer();
 
   // Try to get it as an EGLImage first.
   nsRefPtr<Image> img;
   AttachToContainerAsEGLImage(container, mInstance, r, &img);
@@ -317,17 +317,17 @@ nsPluginInstanceOwner::nsPluginInstanceO
   // struct and allows to manipulate native window procedure
   nsCOMPtr<nsIPluginHost> pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
   mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
   if (mPluginHost)
     mPluginHost->NewPluginNativeWindow(&mPluginWindow);
   else
     mPluginWindow = nullptr;
 
-  mObjectFrame = nullptr;
+  mPluginFrame = nullptr;
   mContent = nullptr;
   mWidgetCreationComplete = false;
 #ifdef XP_MACOSX
   memset(&mCGPluginPortCopy, 0, sizeof(NP_CGContext));
   mInCGPaintLevel = 0;
   mSentInitialTopLevelWindowEvent = false;
   mColorProfile = nullptr;
   mPluginPortChanged = false;
@@ -360,17 +360,17 @@ nsPluginInstanceOwner::~nsPluginInstance
 {
   if (mWaitingForPaint) {
     // We don't care when the event is dispatched as long as it's "soon",
     // since whoever needs it will be waiting for it.
     nsCOMPtr<nsIRunnable> event = new AsyncPaintWaitEvent(mContent, true);
     NS_DispatchToMainThread(event);
   }
 
-  mObjectFrame = nullptr;
+  mPluginFrame = nullptr;
 
   PLUG_DeletePluginNativeWindow(mPluginWindow);
   mPluginWindow = nullptr;
 
 #ifdef MOZ_WIDGET_ANDROID
   RemovePluginView();
 #endif
 
@@ -538,20 +538,20 @@ NS_IMETHODIMP nsPluginInstanceOwner::Sho
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char16_t *aStatusMsg)
 {
   nsresult  rv = NS_ERROR_FAILURE;
 
-  if (!mObjectFrame) {
+  if (!mPluginFrame) {
     return rv;
   }
-  nsCOMPtr<nsIDocShellTreeItem> docShellItem = mObjectFrame->PresContext()->GetDocShell();
+  nsCOMPtr<nsIDocShellTreeItem> docShellItem = mPluginFrame->PresContext()->GetDocShell();
   if (NS_FAILED(rv) || !docShellItem) {
     return rv;
   }
 
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   rv = docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
   if (NS_FAILED(rv) || !treeOwner) {
     return rv;
@@ -577,25 +577,25 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
   NS_IF_ADDREF(*aDocument = mContent->OwnerDoc());
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect)
 {
   // If our object frame has gone away, we won't be able to determine
   // up-to-date-ness, so just fire off the event.
-  if (mWaitingForPaint && (!mObjectFrame || IsUpToDate())) {
+  if (mWaitingForPaint && (!mPluginFrame || IsUpToDate())) {
     // We don't care when the event is dispatched as long as it's "soon",
     // since whoever needs it will be waiting for it.
     nsCOMPtr<nsIRunnable> event = new AsyncPaintWaitEvent(mContent, true);
     NS_DispatchToMainThread(event);
     mWaitingForPaint = false;
   }
 
-  if (!mObjectFrame || !invalidRect || !mWidgetVisible)
+  if (!mPluginFrame || !invalidRect || !mWidgetVisible)
     return NS_ERROR_FAILURE;
 
 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
   // Each time an asynchronously-drawing plugin sends a new surface to display,
   // the image in the ImageContainer is updated and InvalidateRect is called.
   // There are different side effects for (sync) Android plugins.
   nsRefPtr<ImageContainer> container;
   mInstance->GetImageContainer(getter_AddRefs(container));
@@ -616,44 +616,44 @@ NS_IMETHODIMP nsPluginInstanceOwner::Inv
                  invalidRect->right - invalidRect->left,
                  invalidRect->bottom - invalidRect->top);
   // invalidRect is in "display pixels".  In non-HiDPI modes "display pixels"
   // are device pixels.  But in HiDPI modes each display pixel corresponds
   // to more than one device pixel.
   double scaleFactor = 1.0;
   GetContentsScaleFactor(&scaleFactor);
   rect.ScaleRoundOut(scaleFactor);
-  mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
+  mPluginFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsPluginInstanceOwner::RedrawPlugin()
 {
-  if (mObjectFrame) {
-    mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN);
+  if (mPluginFrame) {
+    mPluginFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
 {
-  if (!mObjectFrame) {
+  if (!mPluginFrame) {
     NS_WARNING("plugin owner has no owner in getting doc's window handle");
     return NS_ERROR_FAILURE;
   }
 
 #if defined(XP_WIN)
   void** pvalue = (void**)value;
-  nsViewManager* vm = mObjectFrame->PresContext()->GetPresShell()->GetViewManager();
+  nsViewManager* vm = mPluginFrame->PresContext()->GetPresShell()->GetViewManager();
   if (!vm)
     return NS_ERROR_FAILURE;
 #if defined(XP_WIN)
   // This property is provided to allow a "windowless" plugin to determine the window it is drawing
   // in, so it can translate mouse coordinates it receives directly from the operating system
   // to coordinates relative to itself.
 
   // The original code (outside this #if) returns the document's window, which is OK if the window the "windowless" plugin
@@ -675,17 +675,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
     // unable to show a caret correctly if we return the enclosing window. Therefore for
     // now we only return the enclosing window when there is an actual offset which
     // would otherwise cause coordinates to be offset incorrectly. (i.e.
     // if the enclosing window if offset from the document window)
     //
     // fixing both the caret and ability to interact issues for a windowless control in a non document aligned windw
     // does not seem to be possible without a change to the flash plugin
 
-    nsIWidget* win = mObjectFrame->GetNearestWidget();
+    nsIWidget* win = mPluginFrame->GetNearestWidget();
     if (win) {
       nsView *view = nsView::GetViewFor(win);
       NS_ASSERTION(view, "No view for widget");
       nsPoint offset = view->GetOffsetTo(nullptr);
 
       if (offset.x || offset.y) {
         // in the case the two windows are offset from eachother, we do go ahead and return the correct enclosing window
         // so that mouse co-ordinates are not messed up.
@@ -703,17 +703,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
     *pvalue = (void*)widget->GetNativeData(NS_NATIVE_WINDOW);
   } else {
     NS_ASSERTION(widget, "couldn't get doc's widget in getting doc's window handle");
   }
 
   return NS_OK;
 #elif (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)) && defined(MOZ_X11)
   // X11 window managers want the toplevel window for WM_TRANSIENT_FOR.
-  nsIWidget* win = mObjectFrame->GetNearestWidget();
+  nsIWidget* win = mPluginFrame->GetNearestWidget();
   if (!win)
     return NS_ERROR_FAILURE;
   *static_cast<Window*>(value) = (long unsigned int)win->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW);
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
@@ -1122,18 +1122,18 @@ GetOffsetRootContent(nsIFrame* aFrame)
   offset += docOffset.ConvertAppUnits(currAPD, apd);
 
   return offset;
 }
 
 LayoutDeviceRect nsPluginInstanceOwner::GetPluginRect()
 {
   // Get the offset of the content relative to the page
-  nsRect bounds = mObjectFrame->GetContentRectRelativeToSelf() + GetOffsetRootContent(mObjectFrame);
-  LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mObjectFrame->PresContext()->AppUnitsPerDevPixel());
+  nsRect bounds = mPluginFrame->GetContentRectRelativeToSelf() + GetOffsetRootContent(mPluginFrame);
+  LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mPluginFrame->PresContext()->AppUnitsPerDevPixel());
   return LayoutDeviceRect(rect);
 }
 
 bool nsPluginInstanceOwner::AddPluginView(const LayoutDeviceRect& aRect /* = LayoutDeviceRect(0, 0, 0, 0) */)
 {
   if (!mJavaView) {
     mJavaView = mInstance->GetJavaSurface();
 
@@ -1345,17 +1345,17 @@ nsPluginInstanceOwner::ProcessMouseDown(
 #if !defined(XP_MACOSX)
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
     return aMouseEvent->PreventDefault(); // consume event
   // continue only for cases without child window
 #endif
 
   // if the plugin is windowless, we need to set focus ourselves
   // otherwise, we might not get key events
-  if (mObjectFrame && mPluginWindow &&
+  if (mPluginFrame && mPluginWindow &&
       mPluginWindow->type == NPWindowTypeDrawable) {
 
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm) {
       nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mContent);
       fm->SetFocus(elem, 0);
     }
   }
@@ -1477,17 +1477,17 @@ static unsigned int XInputEventState(con
   return state;
 }
 #endif
 
 nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
 {
   nsEventStatus rv = nsEventStatus_eIgnore;
 
-  if (!mInstance || !mObjectFrame)   // if mInstance is null, we shouldn't be here
+  if (!mInstance || !mPluginFrame)   // if mInstance is null, we shouldn't be here
     return nsEventStatus_eIgnore;
 
 #ifdef XP_MACOSX
   if (!mWidget)
     return nsEventStatus_eIgnore;
 
   // we never care about synthesized mouse enter
   if (anEvent.message == NS_MOUSE_ENTER_SYNTH)
@@ -1498,37 +1498,37 @@ nsEventStatus nsPluginInstanceOwner::Pro
     return nsEventStatus_eIgnore;
 
   NPEventModel eventModel = GetEventModel();
 
   // If we have to synthesize an event we'll use one of these.
   NPCocoaEvent synthCocoaEvent;
   const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
   nsPoint pt =
-  nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
-  mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
-  nsPresContext* presContext = mObjectFrame->PresContext();
+  nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+  mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
+  nsPresContext* presContext = mPluginFrame->PresContext();
   // Plugin event coordinates need to be translated from device pixels
   // into "display pixels" in HiDPI modes.
   double scaleFactor = 1.0;
   GetContentsScaleFactor(&scaleFactor);
   size_t intScaleFactor = ceil(scaleFactor);
   nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
                   presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
 
   if (!event) {
     InitializeNPCocoaEvent(&synthCocoaEvent);
     switch (anEvent.message) {
       case NS_MOUSE_MOVE:
       {
         // Ignore mouse-moved events that happen as part of a dragging
         // operation that started over another frame.  See bug 525078.
-        nsRefPtr<nsFrameSelection> frameselection = mObjectFrame->GetFrameSelection();
+        nsRefPtr<nsFrameSelection> frameselection = mPluginFrame->GetFrameSelection();
         if (!frameselection->GetDragState() ||
-          (nsIPresShell::GetCapturingContent() == mObjectFrame->GetContent())) {
+          (nsIPresShell::GetCapturingContent() == mPluginFrame->GetContent())) {
           synthCocoaEvent.type = NPCocoaEventMouseMoved;
           synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
           synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
           event = &synthCocoaEvent;
         }
       }
         break;
       case NS_MOUSE_BUTTON_DOWN:
@@ -1537,17 +1537,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
         synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
         event = &synthCocoaEvent;
         break;
       case NS_MOUSE_BUTTON_UP:
         // If we're in a dragging operation that started over another frame,
         // convert it into a mouse-entered event (in the Cocoa Event Model).
         // See bug 525078.
         if (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton &&
-            (nsIPresShell::GetCapturingContent() != mObjectFrame->GetContent())) {
+            (nsIPresShell::GetCapturingContent() != mPluginFrame->GetContent())) {
           synthCocoaEvent.type = NPCocoaEventMouseEntered;
           synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
           synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
           event = &synthCocoaEvent;
         } else {
           synthCocoaEvent.type = NPCocoaEventMouseUp;
           synthCocoaEvent.data.mouse.pluginX = static_cast<double>(ptPx.x);
           synthCocoaEvent.data.mouse.pluginY = static_cast<double>(ptPx.y);
@@ -1647,22 +1647,22 @@ nsEventStatus nsPluginInstanceOwner::Pro
       NS_ASSERTION(anEvent.message == NS_MOUSE_BUTTON_DOWN ||
                    anEvent.message == NS_MOUSE_BUTTON_UP ||
                    anEvent.message == NS_MOUSE_DOUBLECLICK ||
                    anEvent.message == NS_MOUSE_ENTER_SYNTH ||
                    anEvent.message == NS_MOUSE_EXIT_SYNTH ||
                    anEvent.message == NS_MOUSE_MOVE,
                    "Incorrect event type for coordinate translation");
       nsPoint pt =
-        nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
-        mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
-      nsPresContext* presContext = mObjectFrame->PresContext();
+        nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+        mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
+      nsPresContext* presContext = mPluginFrame->PresContext();
       nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
                       presContext->AppUnitsToDevPixels(pt.y));
-      nsIntPoint widgetPtPx = ptPx + mObjectFrame->GetWindowOriginInPixels(true);
+      nsIntPoint widgetPtPx = ptPx + mPluginFrame->GetWindowOriginInPixels(true);
       const_cast<NPEvent*>(pPluginEvent)->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y);
     }
   }
   else if (!pPluginEvent) {
     switch (anEvent.message) {
       case NS_FOCUS_CONTENT:
         pluginEvent.event = WM_SETFOCUS;
         pluginEvent.wParam = 0;
@@ -1675,17 +1675,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
         pluginEvent.lParam = 0;
         pPluginEvent = &pluginEvent;
         break;
     }
   }
 
   if (pPluginEvent && !pPluginEvent->event) {
     // Don't send null events to plugins.
-    NS_WARNING("nsObjectFrame ProcessEvent: trying to send null event to plugin.");
+    NS_WARNING("nsPluginFrame ProcessEvent: trying to send null event to plugin.");
     return rv;
   }
 
   if (pPluginEvent) {
     int16_t response = kNPEventNotHandled;
     mInstance->HandleEvent(const_cast<NPEvent*>(pPluginEvent),
                            &response,
                            NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
@@ -1707,20 +1707,20 @@ nsEventStatus nsPluginInstanceOwner::Pro
           {
           case NS_MOUSE_CLICK:
           case NS_MOUSE_DOUBLECLICK:
             // Button up/down events sent instead.
             return rv;
           }
 
         // Get reference point relative to plugin origin.
-        const nsPresContext* presContext = mObjectFrame->PresContext();
+        const nsPresContext* presContext = mPluginFrame->PresContext();
         nsPoint appPoint =
-          nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
-          mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
+          nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+          mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
         nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x),
                                presContext->AppUnitsToDevPixels(appPoint.y));
         const WidgetMouseEvent& mouseEvent = *anEvent.AsMouseEvent();
         // Get reference point relative to screen:
         LayoutDeviceIntPoint rootPoint(-1, -1);
         if (widget)
           rootPoint = anEvent.refPoint +
             LayoutDeviceIntPoint::FromUntyped(widget->WidgetToScreenOffset());
@@ -1908,20 +1908,20 @@ nsEventStatus nsPluginInstanceOwner::Pro
           {
           case NS_MOUSE_CLICK:
           case NS_MOUSE_DOUBLECLICK:
             // Button up/down events sent instead.
             return rv;
           }
 
         // Get reference point relative to plugin origin.
-        const nsPresContext* presContext = mObjectFrame->PresContext();
+        const nsPresContext* presContext = mPluginFrame->PresContext();
         nsPoint appPoint =
-          nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
-          mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
+          nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
+          mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
         nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x),
                                presContext->AppUnitsToDevPixels(appPoint.y));
 
         switch (anEvent.message)
           {
           case NS_MOUSE_MOVE:
             {
               // are these going to be touch events?
@@ -2040,17 +2040,17 @@ nsPluginInstanceOwner::Destroy()
   return NS_OK;
 }
 
 // Paints are handled differently, so we just simulate an update event.
 
 #ifdef XP_MACOSX
 void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect, CGContextRef cgContext)
 {
-  if (!mInstance || !mObjectFrame)
+  if (!mInstance || !mPluginFrame)
     return;
 
   gfxRect dirtyRectCopy = aDirtyRect;
   double scaleFactor = 1.0;
   GetContentsScaleFactor(&scaleFactor);
   if (scaleFactor != 1.0) {
     ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
     // Convert aDirtyRect from device pixels to "display pixels"
@@ -2062,17 +2062,17 @@ void nsPluginInstanceOwner::Paint(const 
   if (pluginWidget && NS_SUCCEEDED(pluginWidget->StartDrawPlugin())) {
     DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
     pluginWidget->EndDrawPlugin();
   }
 }
 
 void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
 {
-  if (!mInstance || !mObjectFrame)
+  if (!mInstance || !mPluginFrame)
     return;
 
   // The context given here is only valid during the HandleEvent call.
   NPCocoaEvent updateEvent;
   InitializeNPCocoaEvent(&updateEvent);
   updateEvent.type = NPCocoaEventDrawRect;
   updateEvent.data.draw.context = cgContext;
   updateEvent.data.draw.x = aDrawRect.X();
@@ -2082,34 +2082,34 @@ void nsPluginInstanceOwner::DoCocoaEvent
 
   mInstance->HandleEvent(&updateEvent, nullptr);
 }
 #endif
 
 #ifdef XP_WIN
 void nsPluginInstanceOwner::Paint(const RECT& aDirty, HDC aDC)
 {
-  if (!mInstance || !mObjectFrame)
+  if (!mInstance || !mPluginFrame)
     return;
 
   NPEvent pluginEvent;
   pluginEvent.event = WM_PAINT;
   pluginEvent.wParam = WPARAM(aDC);
   pluginEvent.lParam = LPARAM(&aDirty);
   mInstance->HandleEvent(&pluginEvent, nullptr);
 }
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 
 void nsPluginInstanceOwner::Paint(gfxContext* aContext,
                                   const gfxRect& aFrameRect,
                                   const gfxRect& aDirtyRect)
 {
-  if (!mInstance || !mObjectFrame || !mPluginDocumentActiveState || mFullScreen)
+  if (!mInstance || !mPluginFrame || !mPluginDocumentActiveState || mFullScreen)
     return;
 
   int32_t model = mInstance->GetANPDrawingModel();
 
   if (model == kSurface_ANPDrawingModel) {
     if (!AddPluginView(GetPluginRect())) {
       Invalidate();
     }
@@ -2164,17 +2164,17 @@ void nsPluginInstanceOwner::Paint(gfxCon
 }
 #endif
 
 #if defined(MOZ_X11)
 void nsPluginInstanceOwner::Paint(gfxContext* aContext,
                                   const gfxRect& aFrameRect,
                                   const gfxRect& aDirtyRect)
 {
-  if (!mInstance || !mObjectFrame)
+  if (!mInstance || !mPluginFrame)
     return;
 
   // to provide crisper and faster drawing.
   gfxRect pluginRect = aFrameRect;
   if (aContext->UserToDevicePixelSnapped(pluginRect)) {
     pluginRect = aContext->DeviceToUser(pluginRect);
   }
 
@@ -2367,17 +2367,17 @@ nsresult nsPluginInstanceOwner::Init(nsI
   mLastEventloopNestingLevel = GetEventloopNestingLevel();
 
   mContent = aContent;
 
   // Get a frame, don't reflow. If a reflow was necessary it should have been
   // done at a higher level than this (content).
   nsIFrame* frame = aContent->GetPrimaryFrame();
   nsIObjectFrame* iObjFrame = do_QueryFrame(frame);
-  nsObjectFrame* objFrame =  static_cast<nsObjectFrame*>(iObjFrame);
+  nsPluginFrame* objFrame =  static_cast<nsPluginFrame*>(iObjFrame);
   if (objFrame) {
     SetFrame(objFrame);
     // Some plugins require a specific sequence of shutdown and startup when
     // a page is reloaded. Shutdown happens usually when the last instance
     // is destroyed. Here we make sure the plugin instance in the old
     // document is destroyed before we try to create the new one.
     objFrame->PresContext()->EnsureVisible();
   } else {
@@ -2519,19 +2519,19 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
     pluginWidget->SetPluginDrawingModel(GetDrawingModel());
 
     if (GetDrawingModel() == NPDrawingModelCoreAnimation) {
       AddToCARefreshTimer();
     }
 #endif
   }
 
-  if (mObjectFrame) {
+  if (mPluginFrame) {
     // nullptr widget is fine, will result in windowless setup.
-    mObjectFrame->PrepForDrawing(mWidget);
+    mPluginFrame->PrepForDrawing(mWidget);
   }
 
   if (windowless) {
     mPluginWindow->type = NPWindowTypeDrawable;
 
     // this needs to be a HDC according to the spec, but I do
     // not see the right way to release it so let's postpone
     // passing HDC till paint event when it is really
@@ -2568,31 +2568,31 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
   return NS_OK;
 }
 
 // Mac specific code to fix up the port location and clipping region
 #ifdef XP_MACOSX
 
 void* nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState)
 {
-  if (!mWidget || !mPluginWindow || !mInstance || !mObjectFrame)
+  if (!mWidget || !mPluginWindow || !mInstance || !mPluginFrame)
     return nullptr;
 
   nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
   if (!pluginWidget)
     return nullptr;
 
-  // If we've already set up a CGContext in nsObjectFrame::PaintPlugin(), we
+  // If we've already set up a CGContext in nsPluginFrame::PaintPlugin(), we
   // don't want calls to SetPluginPortAndDetectChange() to step on our work.
   if (mInCGPaintLevel < 1) {
     SetPluginPortAndDetectChange();
   }
 
   // We'll need the top-level Cocoa window for the Cocoa event model.
-  nsIWidget* widget = mObjectFrame->GetNearestWidget();
+  nsIWidget* widget = mPluginFrame->GetNearestWidget();
   if (!widget)
     return nullptr;
   void *cocoaTopLevelWindow = widget->GetNativeData(NS_NATIVE_WINDOW);
   // We don't expect to have a top level window in a content process
   if (!cocoaTopLevelWindow && XRE_GetProcessType() == GeckoProcessType_Default) {
     return nullptr;
   }
 
@@ -2717,17 +2717,17 @@ void nsPluginInstanceOwner::UpdateWindow
   // passed to the plugin during paint, an additional update
   // of the the clip rectangle here is not required
   if (aSetWindow && !mWidget && mPluginWindowVisible && !UseAsyncRendering())
     return;
 
   const NPWindow oldWindow = *mPluginWindow;
 
   bool windowless = (mPluginWindow->type == NPWindowTypeDrawable);
-  nsIntPoint origin = mObjectFrame->GetWindowOriginInPixels(windowless);
+  nsIntPoint origin = mPluginFrame->GetWindowOriginInPixels(windowless);
 
   mPluginWindow->x = origin.x;
   mPluginWindow->y = origin.y;
 
   mPluginWindow->clipRect.left = 0;
   mPluginWindow->clipRect.top = 0;
 
   if (mPluginWindowVisible && mPluginDocumentActiveState) {
@@ -2781,18 +2781,18 @@ nsPluginInstanceOwner::UpdateDocumentAct
   }
 #endif
 }
 #endif // XP_MACOSX
 
 NS_IMETHODIMP
 nsPluginInstanceOwner::CallSetWindow()
 {
-  if (mObjectFrame) {
-    mObjectFrame->CallSetWindow(false);
+  if (mPluginFrame) {
+    mPluginFrame->CallSetWindow(false);
   } else if (mInstance) {
     if (UseAsyncRendering()) {
       mInstance->AsyncSetWindow(mPluginWindow);
     } else {
       mInstance->SetWindow(mPluginWindow);
     }
   }
 
@@ -2813,54 +2813,54 @@ nsPluginInstanceOwner::GetContentsScaleF
     scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
       presShell->GetPresContext()->DeviceContext()->UnscaledAppUnitsPerDevPixel();
   }
 #endif
   *result = scaleFactor;
   return NS_OK;
 }
 
-void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
+void nsPluginInstanceOwner::SetFrame(nsPluginFrame *aFrame)
 {
   // Don't do anything if the frame situation hasn't changed.
-  if (mObjectFrame == aFrame) {
+  if (mPluginFrame == aFrame) {
     return;
   }
 
   // If we already have a frame that is changing or going away...
-  if (mObjectFrame) {
+  if (mPluginFrame) {
     // Make sure the old frame isn't holding a reference to us.
-    mObjectFrame->SetInstanceOwner(nullptr);
+    mPluginFrame->SetInstanceOwner(nullptr);
   }
 
   // Swap in the new frame (or no frame)
-  mObjectFrame = aFrame;
+  mPluginFrame = aFrame;
 
   // Set up a new frame
-  if (mObjectFrame) {
-    mObjectFrame->SetInstanceOwner(this);
+  if (mPluginFrame) {
+    mPluginFrame->SetInstanceOwner(this);
     // Can only call PrepForDrawing on an object frame once. Don't do it here unless
     // widget creation is complete. Doesn't matter if we actually have a widget.
     if (mWidgetCreationComplete) {
-      mObjectFrame->PrepForDrawing(mWidget);
+      mPluginFrame->PrepForDrawing(mWidget);
     }
-    mObjectFrame->FixupWindow(mObjectFrame->GetContentRectRelativeToSelf().Size());
-    mObjectFrame->InvalidateFrame();
+    mPluginFrame->FixupWindow(mPluginFrame->GetContentRectRelativeToSelf().Size());
+    mPluginFrame->InvalidateFrame();
 
     nsFocusManager* fm = nsFocusManager::GetFocusManager();
     const nsIContent* content = aFrame->GetContent();
     if (fm && content) {
       mContentFocused = (content == fm->GetFocusedContent());
     }
   }
 }
 
-nsObjectFrame* nsPluginInstanceOwner::GetFrame()
+nsPluginFrame* nsPluginInstanceOwner::GetFrame()
 {
-  return mObjectFrame;
+  return mPluginFrame;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::PrivateModeChanged(bool aEnabled)
 {
   return mInstance ? mInstance->PrivateModeStateChanged(aEnabled) : NS_OK;
 }
 
 already_AddRefed<nsIURI> nsPluginInstanceOwner::GetBaseURI() const
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -21,17 +21,17 @@
 #ifdef XP_MACOSX
 #include "mozilla/gfx/QuartzSupport.h"
 #include <ApplicationServices/ApplicationServices.h>
 #endif
 
 class nsIInputStream;
 struct nsIntRect;
 class nsPluginDOMContextMenuListener;
-class nsObjectFrame;
+class nsPluginFrame;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace dom {
 struct MozPluginParameter;
 }
 }
 
@@ -138,30 +138,30 @@ public:
   // store a copy of plugin port info and to detect when it's been changed.
   void* GetPluginPortCopy();
   // Set plugin port info in the plugin (in the 'window' member of the
   // NPWindow structure passed to the plugin by SetWindow()) and set a
   // flag (mPluginPortChanged) to indicate whether or not this info has
   // changed, and SetWindow() needs to be called again.
   void* SetPluginPortAndDetectChange();
   // Flag when we've set up a Thebes (and CoreGraphics) context in
-  // nsObjectFrame::PaintPlugin().  We need to know this in
+  // nsPluginFrame::PaintPlugin().  We need to know this in
   // FixUpPluginWindow() (i.e. we need to know when FixUpPluginWindow() has
-  // been called from nsObjectFrame::PaintPlugin() when we're using the
+  // been called from nsPluginFrame::PaintPlugin() when we're using the
   // CoreGraphics drawing model).
   void BeginCGPaint();
   void EndCGPaint();
 #else // XP_MACOSX
   void UpdateWindowPositionAndClipRect(bool aSetWindow);
   void UpdateWindowVisibility(bool aVisible);
   void UpdateDocumentActiveState(bool aIsActive);
 #endif // XP_MACOSX
 
-  void SetFrame(nsObjectFrame *aFrame);
-  nsObjectFrame* GetFrame();
+  void SetFrame(nsPluginFrame *aFrame);
+  nsPluginFrame* GetFrame();
 
   uint32_t GetLastEventloopNestingLevel() const {
     return mLastEventloopNestingLevel; 
   }
   
   static uint32_t GetEventloopNestingLevel();
   
   void ConsiderNewEventloopNestingLevel() {
@@ -271,17 +271,17 @@ private:
   void RemovePluginView();
 
   bool mFullScreen;
   void* mJavaView;
 #endif 
  
   nsPluginNativeWindow       *mPluginWindow;
   nsRefPtr<nsNPAPIPluginInstance> mInstance;
-  nsObjectFrame              *mObjectFrame;
+  nsPluginFrame              *mPluginFrame;
   nsIContent                 *mContent; // WEAK, content owns us
   nsCString                   mDocumentBase;
   bool                        mWidgetCreationComplete;
   nsCOMPtr<nsIWidget>         mWidget;
   nsRefPtr<nsPluginHost>      mPluginHost;
   
 #ifdef XP_MACOSX
   NP_CGContext                              mCGPluginPortCopy;
--- a/dom/plugins/base/nsPluginNativeWindowGtk.cpp
+++ b/dom/plugins/base/nsPluginNativeWindowGtk.cpp
@@ -140,17 +140,17 @@ nsresult nsPluginNativeWindowGtk::CallSe
       }
 
       if (!mSocketWidget) {
         return NS_ERROR_FAILURE;
       }
 
       // Make sure to resize and re-place the window if required.
       SetAllocation();
-      // Need to reset "window" each time as nsObjectFrame::DidReflow sets it
+      // Need to reset "window" each time as nsPluginFrame::DidReflow sets it
       // to the ancestor window.
 #if (MOZ_WIDGET_GTK == 2)
       if (GTK_IS_XTBIN(mSocketWidget)) {
         // Point the NPWindow structures window to the actual X window
         SetWindow(GTK_XTBIN(mSocketWidget)->xtwindow);
       }
       else { // XEmbed or OOP&Xt
         SetWindow(gtk_socket_get_id(GTK_SOCKET(mSocketWidget)));
@@ -220,17 +220,17 @@ nsresult nsPluginNativeWindowGtk::Create
   SetAllocation();
 
   gtk_widget_show(mSocketWidget);
 
   gdk_flush();
   SetWindow(gtk_socket_get_id(GTK_SOCKET(mSocketWidget)));
 
   // Fill out the ws_info structure.
-  // (The windowless case is done in nsObjectFrame.cpp.)
+  // (The windowless case is done in nsPluginFrame.cpp.)
   GdkWindow *gdkWindow = gdk_x11_window_lookup_for_display(display, GetWindow());
   if(!gdkWindow)
     return NS_ERROR_FAILURE;
 
   mWsInfo.display = GDK_WINDOW_XDISPLAY(gdkWindow);
 #if (MOZ_WIDGET_GTK == 2)
   mWsInfo.colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(gdkWindow));
   GdkVisual* gdkVisual = gdk_drawable_get_visual(gdkWindow);
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -1943,17 +1943,17 @@ PluginInstanceChild::SharedSurfacePaint(
         case RENDER_NATIVE:
             // pass the internal hdc to the plugin
             UpdatePaintClipRect(pRect);
             evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
             return mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
         break;
         case RENDER_BACK_ONE:
               // Handle a double pass render used in alpha extraction for transparent
-              // plugins. (See nsObjectFrame and gfxWindowsNativeDrawing for details.)
+              // plugins. (See nsPluginFrame and gfxWindowsNativeDrawing for details.)
               // We render twice, once to the shared dib, and once to a cache which
               // we copy back on a second paint. These paints can't be spread across
               // multiple rpc messages as delays cause animation frame changes.
               if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) {
                   mAlphaExtract.doublePass = RENDER_NATIVE;
                   return false;
               }
 
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -362,17 +362,17 @@ PluginInstanceParent::AnswerNPN_SetValue
     const int& drawingModel, OptionalShmem *shmem, CrossProcessMutexHandle *mutex, NPError* result)
 {
     *shmem = null_t();
 
 #ifdef XP_MACOSX
     if (drawingModel == NPDrawingModelCoreAnimation ||
         drawingModel == NPDrawingModelInvalidatingCoreAnimation) {
         // We need to request CoreGraphics otherwise
-        // the nsObjectFrame will try to draw a CALayer
+        // the nsPluginFrame will try to draw a CALayer
         // that can not be shared across process.
         mDrawingModel = drawingModel;
         *result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
                                   (void*)NPDrawingModelCoreGraphics);
     } else
 #endif
     if (drawingModel == NPDrawingModelAsyncBitmapSurface
 #ifdef XP_WIN
@@ -1226,17 +1226,17 @@ PluginInstanceParent::NPP_HandleEvent(vo
                   !wcscmp(szClass, kFlashFullscreenClass)) {
                   return 0;
               }
             }
             break;
 
             case WM_WINDOWPOSCHANGED:
             {
-                // We send this in nsObjectFrame just before painting
+                // We send this in nsPluginFrame just before painting
                 return SendWindowPosChanged(npremoteevent);
             }
             break;
         }
     }
 #endif
 
 #if defined(MOZ_X11)
--- a/dom/plugins/test/testplugin/nptest_macosx.mm
+++ b/dom/plugins/test/testplugin/nptest_macosx.mm
@@ -95,17 +95,17 @@ RectEquals(const NPRect& r1, const NPRec
          r1.right == r2.right && r1.bottom == r2.bottom;
 }
 
 void
 pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow)
 {
   // Ugh. Due to a terrible Gecko bug, we have to ignore position changes
   // when the clip rect doesn't change; the position can be wrong
-  // when set by a path other than nsObjectFrame::FixUpPluginWindow.
+  // when set by a path other than nsPluginFrame::FixUpPluginWindow.
   int32_t oldX = instanceData->window.x;
   int32_t oldY = instanceData->window.y;
   bool clipChanged =
     !RectEquals(instanceData->window.clipRect, newWindow->clipRect);
   instanceData->window = *newWindow;
   if (!clipChanged) {
     instanceData->window.x = oldX;
     instanceData->window.y = oldY;
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -27,16 +27,17 @@ support-files =
   res5.resource
   res5.resource^headers^
   res6.resource
   res6.resource^headers^
   res7.resource
   res7.resource^headers^
   res8.resource
   res8.resource^headers^
+  resource_timing.js
 
 [test_497898.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
 [test_bug504220.html]
 [test_bug628069_1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
new file mode 100644
--- a/dom/tests/mochitest/general/resource_timing_cross_origin.html
+++ b/dom/tests/mochitest/general/resource_timing_cross_origin.html
@@ -81,16 +81,20 @@ function firstCheck() {
   entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res7.resource");
   ok(!!entries[0], "cross origin res7.resource is missing from entries");
   checkCrossOrigin(entries[0]);
 
   entries = window.performance.getEntriesByName("http://test1.example.com/tests/dom/tests/mochitest/general/res8.resource");
   ok(!!entries[0], "redirected res8.resource is missing from entries");
   checkRedirectCrossOriginResourceSameOrigin(entries[0]);
 
+  entries = window.performance.getEntriesByName("http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing.js");
+  ok(!!entries[0], "same origin resource_timing.js is missing from entries");
+  checkSameOrigin(entries[0]);
+
   is(bufferFullCounter, expectedBufferFullEvents, "Buffer full was called");
   finishTests();
 }
 
 function checkEntry(entry, checks) {
   // If the entry is undefined, we return early so we don't get a JS error
   if (entry == undefined)
     return;
@@ -171,12 +175,13 @@ function finishTests() {
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res1.resource"> <!-- cross origin, Timing-Allow-Origin: * header -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res2.resource"> <!-- cross origin redirect to test2.example.com, no header -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res3.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 header -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res4.resource"> <!-- cross origin redirect to mochi.test:8888/.../res1.resource, Timing-Allow-Origin: * -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res5.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8889 -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res6.resource"> <!-- cross origin, Timing-Allow-Origin: "" (empty string) -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res7.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 http://test1.com header -->
     <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res8.resource"> <!-- double cross origin redirect -->
+    <script type="text/javascript" src="http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing.js"></script> <!-- same origin script -->
   </div>
 </body>
 
 </html>
--- a/dom/tests/mochitest/notification/mochitest.ini
+++ b/dom/tests/mochitest/notification/mochitest.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 skip-if = e10s || buildapp == 'mulet'
 support-files =
   MockServices.js
   NotificationTest.js
 
 [test_notification_basics.html]
 [test_notification_storage.html]
-skip-if = (toolkit == 'gonk' && debug) #debug-only timeout
+skip-if = (toolkit == 'gonk')
 [test_bug931307.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only timeout
 [test_notification_resend.html]
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -90,16 +90,17 @@ static const char *sExtensionNames[] = {
     "GL_ARB_pixel_buffer_object",
     "GL_ARB_robustness",
     "GL_ARB_sampler_objects",
     "GL_ARB_sync",
     "GL_ARB_texture_compression",
     "GL_ARB_texture_float",
     "GL_ARB_texture_non_power_of_two",
     "GL_ARB_texture_rectangle",
+    "GL_ARB_texture_storage",
     "GL_ARB_transform_feedback2",
     "GL_ARB_uniform_buffer_object",
     "GL_ARB_vertex_array_object",
     "GL_EXT_bgra",
     "GL_EXT_blend_minmax",
     "GL_EXT_color_buffer_float",
     "GL_EXT_color_buffer_half_float",
     "GL_EXT_copy_texture",
@@ -925,16 +926,32 @@ GLContext::InitWithPrefix(const char *pr
             if (!LoadSymbols(samplerObjectsSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports sampler objects without supplying its functions.");
 
                 MarkUnsupported(GLFeature::sampler_objects);
                 ClearSymbols(samplerObjectsSymbols);
             }
         }
 
+        if (IsSupported(GLFeature::texture_storage)) {
+            SymLoadStruct coreSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2D", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3D", nullptr } },
+                END_SYMBOLS
+            };
+
+            if (!LoadSymbols(coreSymbols, trygl, prefix)) {
+                NS_ERROR("GL supports texture storage without supplying its functions.");
+
+                MarkUnsupported(GLFeature::texture_storage);
+                MarkExtensionUnsupported(ARB_texture_storage);
+                ClearSymbols(coreSymbols);
+            }
+        }
+
         // ARB_transform_feedback2/NV_transform_feedback2 is a
         // superset of EXT_transform_feedback/NV_transform_feedback
         // and adds glPauseTransformFeedback &
         // glResumeTransformFeedback, which are required for WebGL2.
         if (IsSupported(GLFeature::transform_feedback2)) {
             SymLoadStruct coreSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fBindBufferBase, { "BindBufferBase", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fBindBufferRange, { "BindBufferRange", nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -113,16 +113,17 @@ MOZ_BEGIN_ENUM_CLASS(GLFeature)
     texture_3D,
     texture_3D_compressed,
     texture_3D_copy,
     texture_float,
     texture_float_linear,
     texture_half_float,
     texture_half_float_linear,
     texture_non_power_of_two,
+    texture_storage,
     transform_feedback2,
     uniform_buffer_object,
     uniform_matrix_nonsquare,
     vertex_array_object,
     EnumMax
 MOZ_END_ENUM_CLASS(GLFeature)
 
 MOZ_BEGIN_ENUM_CLASS(ContextProfile, uint8_t)
@@ -369,16 +370,17 @@ public:
         ARB_pixel_buffer_object,
         ARB_robustness,
         ARB_sampler_objects,
         ARB_sync,
         ARB_texture_compression,
         ARB_texture_float,
         ARB_texture_non_power_of_two,
         ARB_texture_rectangle,
+        ARB_texture_storage,
         ARB_transform_feedback2,
         ARB_uniform_buffer_object,
         ARB_vertex_array_object,
         EXT_bgra,
         EXT_blend_minmax,
         EXT_color_buffer_float,
         EXT_color_buffer_half_float,
         EXT_copy_texture,
@@ -3061,16 +3063,33 @@ public:
 
     void fUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) {
         ASSERT_SYMBOL_PRESENT(fUniformBlockBinding);
         BEFORE_GL_CALL;
         mSymbols.fUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
         AFTER_GL_CALL;
     }
 
+// -----------------------------------------------------------------------------
+// Core GL 4.2, GL ES 3.0 & Extension ARB_texture_storage/EXT_texture_storage
+    void fTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
+    {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fTexStorage2D);
+        mSymbols.fTexStorage2D(target, levels, internalformat, width, height);
+        AFTER_GL_CALL;
+    }
+
+    void fTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
+    {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fTexStorage3D);
+        mSymbols.fTexStorage3D(target, levels, internalformat, width, height, depth);
+        AFTER_GL_CALL;
+    }
 
 // -----------------------------------------------------------------------------
 // 3D Textures
     void fTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                         GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
                         GLenum format, GLenum type, const GLvoid* pixels)
     {
         BEFORE_GL_CALL;
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -504,16 +504,30 @@ static const FeatureInfo sFeatureInfoArr
         GLContext::Extension_None,
         {
             GLContext::ARB_texture_non_power_of_two,
             GLContext::OES_texture_npot,
             GLContext::Extensions_End
         }
     },
     {
+        "texture_storage",
+        420, // OpenGL version
+        300, // OpenGL ES version
+        GLContext::ARB_texture_storage,
+        {
+            /*
+             * Not including GL_EXT_texture_storage here because it
+             * doesn't guarantee glTexStorage3D, which is required for
+             * WebGL 2.
+             */
+            GLContext::Extensions_End
+        }
+    },
+    {
         "transform_feedback2",
         400, // OpenGL version
         300, // OpenGL ES version
         GLContext::Extension_None,
         {
             GLContext::ARB_transform_feedback2,
             GLContext::NV_transform_feedback2,
             GLContext::Extensions_End
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -575,16 +575,22 @@ struct GLContextSymbols
     PFNGLSAMPLERPARAMETERFPROC fSamplerParameterf;
     typedef void (GLAPIENTRY * PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param);
     PFNGLSAMPLERPARAMETERFVPROC fSamplerParameterfv;
     typedef void (GLAPIENTRY * PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params);
     PFNGLGETSAMPLERPARAMETERIVPROC fGetSamplerParameteriv;
     typedef void (GLAPIENTRY * PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params);
     PFNGLGETSAMPLERPARAMETERFVPROC fGetSamplerParameterfv;
 
+    // texture_storage
+    typedef void (GLAPIENTRY * PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
+    PFNGLTEXSTORAGE2DPROC fTexStorage2D;
+    typedef void (GLAPIENTRY * PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
+    PFNGLTEXSTORAGE3DPROC fTexStorage3D;
+
     // uniform_buffer_object
     typedef void (GLAPIENTRY * PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount,
                                                             const GLchar* const* uniformNames, GLuint* uniformIndices);
     PFNGLGETUNIFORMINDICESPROC fGetUniformIndices;
     typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint* uniformIndices,
                                                              GLenum pname, GLint* params);
     PFNGLGETACTIVEUNIFORMSIVPROC fGetActiveUniformsiv;
     typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIdex, GLsizei bufSize, GLsizei* length, GLchar* uniformName);
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -6,16 +6,83 @@
 #include "SharedSurfaceANGLE.h"
 
 #include "GLContextEGL.h"
 #include "GLLibraryEGL.h"
 
 namespace mozilla {
 namespace gl {
 
+// Returns `EGL_NO_SURFACE` (`0`) on error.
+static EGLSurface
+CreatePBufferSurface(GLLibraryEGL* egl,
+                     EGLDisplay display,
+                     EGLConfig config,
+                     const gfx::IntSize& size)
+{
+    auto width = size.width;
+    auto height = size.height;
+
+    EGLint attribs[] = {
+        LOCAL_EGL_WIDTH, width,
+        LOCAL_EGL_HEIGHT, height,
+        LOCAL_EGL_NONE
+    };
+
+    DebugOnly<EGLint> preCallErr = egl->fGetError();
+    MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
+    EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
+    EGLint err = egl->fGetError();
+    if (err != LOCAL_EGL_SUCCESS)
+        return 0;
+
+    return surface;
+}
+
+/*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
+SharedSurface_ANGLEShareHandle::Create(GLContext* gl,
+                                       EGLContext context, EGLConfig config,
+                                       const gfx::IntSize& size, bool hasAlpha)
+{
+    GLLibraryEGL* egl = &sEGLLibrary;
+    MOZ_ASSERT(egl);
+    MOZ_ASSERT(egl->IsExtensionSupported(
+               GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle));
+
+    if (!context || !config)
+        return nullptr;
+
+    EGLDisplay display = egl->Display();
+    EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size);
+    if (!pbuffer)
+        return nullptr;
+
+    // Declare everything before 'goto's.
+    HANDLE shareHandle = nullptr;
+    bool ok = egl->fQuerySurfacePointerANGLE(display,
+                                             pbuffer,
+                                             LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
+                                             &shareHandle);
+    if (!ok) {
+        egl->fDestroySurface(egl->Display(), pbuffer);
+        return nullptr;
+    }
+
+    GLuint fence = 0;
+    if (gl->IsExtensionSupported(GLContext::NV_fence)) {
+        gl->MakeCurrent();
+        gl->fGenFences(1, &fence);
+    }
+
+    typedef SharedSurface_ANGLEShareHandle ptrT;
+    UniquePtr<ptrT> ret( new ptrT(gl, egl, size, hasAlpha, context,
+                                  pbuffer, shareHandle, fence) );
+    return Move(ret);
+}
+
 EGLDisplay
 SharedSurface_ANGLEShareHandle::Display()
 {
     return mEGL->Display();
 }
 
 SharedSurface_ANGLEShareHandle::SharedSurface_ANGLEShareHandle(GLContext* gl,
                                                                GLLibraryEGL* egl,
@@ -109,16 +176,19 @@ SharedSurface_ANGLEShareHandle::PollSync
     if (mFence) {
         mGL->MakeCurrent();
         return mGL->fTestFence(mFence);
     }
 
     return PollSync();
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Factory
+
 static void
 FillPBufferAttribs_ByBits(nsTArray<EGLint>& aAttrs,
                           int redBits, int greenBits,
                           int blueBits, int alphaBits,
                           int depthBits, int stencilBits)
 {
     aAttrs.Clear();
 
@@ -164,157 +234,126 @@ FillPBufferAttribs_BySizes(nsTArray<EGLi
             blue = 5;
         }
     } else {
         red = green = blue = 8;
         if (hasAlpha)
             alpha = 8;
     }
 
-    FillPBufferAttribs_ByBits(attribs,
-                              red, green, blue, alpha,
-                              depthBits, stencilBits);
+    FillPBufferAttribs_ByBits(attribs, red, green, blue, alpha, depthBits,
+                              stencilBits);
+}
+
+static bool
+DoesAttribBitsMatchCapBool(GLLibraryEGL* egl, EGLConfig config, EGLint attrib,
+                           bool capBool)
+{
+    EGLint bits = 0;
+    egl->fGetConfigAttrib(egl->Display(), config, attrib, &bits);
+    MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
+
+    bool hasBits = !!bits;
+
+    return hasBits == capBool;
 }
 
 static EGLConfig
-ChooseConfig(GLContext* gl,
-             GLLibraryEGL* egl,
-             const SurfaceCaps& caps)
+ChooseConfig(GLContext* gl, GLLibraryEGL* egl, const SurfaceCaps& caps)
 {
     MOZ_ASSERT(egl);
     MOZ_ASSERT(caps.color);
 
     // We might want 24-bit depth, but we're only (fairly) sure to get 16-bit.
     int depthBits = caps.depth ? 16 : 0;
     int stencilBits = caps.stencil ? 8 : 0;
 
     // Ok, now we have everything.
     nsTArray<EGLint> attribs(32);
-    FillPBufferAttribs_BySizes(attribs,
-                               caps.bpp16, caps.alpha,
-                               depthBits, stencilBits);
+    FillPBufferAttribs_BySizes(attribs, caps.bpp16, caps.alpha, depthBits,
+                               stencilBits);
 
     // Time to try to get this config:
     EGLConfig configs[64];
     int numConfigs = sizeof(configs)/sizeof(EGLConfig);
     int foundConfigs = 0;
 
-    if (!egl->fChooseConfig(egl->Display(),
-                            attribs.Elements(),
-                            configs, numConfigs,
-                            &foundConfigs) ||
+    if (!egl->fChooseConfig(egl->Display(), attribs.Elements(), configs,
+                            numConfigs, &foundConfigs) ||
         !foundConfigs)
     {
         NS_WARNING("No configs found for the requested formats.");
         return EGL_NO_CONFIG;
     }
 
-    // TODO: Pick a config progamatically instead of hoping that
-    // the first config will be minimally matching our request.
-    EGLConfig config = configs[0];
+    // The requests passed to ChooseConfig are treated as minimums. If you ask
+    // for 0 bits of alpha, we might still get 8 bits.
+    EGLConfig config = EGL_NO_CONFIG;
+    for (int i = 0; i < foundConfigs; i++) {
+        EGLConfig cur = configs[0];
+        if (DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
+                                       caps.alpha) &&
+            DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
+                                       caps.depth) &&
+            DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_STENCIL_SIZE,
+                                       caps.stencil))
+        {
+            config = cur;
+            break;
+        }
+    }
+
+    if (config == EGL_NO_CONFIG) {
+        NS_WARNING("No acceptable EGLConfig found.");
+        return EGL_NO_CONFIG;
+    }
 
     if (gl->DebugMode()) {
         egl->DumpEGLConfig(config);
     }
 
     return config;
 }
 
-// Returns `EGL_NO_SURFACE` (`0`) on error.
-static EGLSurface
-CreatePBufferSurface(GLLibraryEGL* egl,
-                     EGLDisplay display,
-                     EGLConfig config,
-                     const gfx::IntSize& size)
-{
-    auto width = size.width;
-    auto height = size.height;
-
-    EGLint attribs[] = {
-        LOCAL_EGL_WIDTH, width,
-        LOCAL_EGL_HEIGHT, height,
-        LOCAL_EGL_NONE
-    };
-
-    DebugOnly<EGLint> preCallErr = egl->fGetError();
-    MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
-    EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
-    EGLint err = egl->fGetError();
-    if (err != LOCAL_EGL_SUCCESS)
-        return 0;
-
-    return surface;
-}
-
-/*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
-SharedSurface_ANGLEShareHandle::Create(GLContext* gl,
-                                       EGLContext context, EGLConfig config,
-                                       const gfx::IntSize& size, bool hasAlpha)
-{
-    GLLibraryEGL* egl = &sEGLLibrary;
-    MOZ_ASSERT(egl);
-    MOZ_ASSERT(egl->IsExtensionSupported(
-               GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle));
-
-    if (!context || !config)
-        return nullptr;
-
-    EGLDisplay display = egl->Display();
-    EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size);
-    if (!pbuffer)
-        return nullptr;
-
-    // Declare everything before 'goto's.
-    HANDLE shareHandle = nullptr;
-    bool ok = egl->fQuerySurfacePointerANGLE(display,
-                                             pbuffer,
-                                             LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
-                                             &shareHandle);
-    if (!ok) {
-        egl->fDestroySurface(egl->Display(), pbuffer);
-        return nullptr;
-    }
-
-    GLuint fence = 0;
-    if (gl->IsExtensionSupported(GLContext::NV_fence)) {
-        gl->MakeCurrent();
-        gl->fGenFences(1, &fence);
-    }
-
-    typedef SharedSurface_ANGLEShareHandle ptrT;
-    UniquePtr<ptrT> ret( new ptrT(gl, egl, size, hasAlpha, context,
-                                  pbuffer, shareHandle, fence) );
-    return Move(ret);
-}
-
 /*static*/ UniquePtr<SurfaceFactory_ANGLEShareHandle>
 SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl,
                                         const SurfaceCaps& caps)
 {
     GLLibraryEGL* egl = &sEGLLibrary;
     if (!egl)
         return nullptr;
 
     auto ext = GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle;
     if (!egl->IsExtensionSupported(ext))
-    {
         return nullptr;
-    }
 
+    bool success;
     typedef SurfaceFactory_ANGLEShareHandle ptrT;
-    UniquePtr<ptrT> ret( new ptrT(gl, egl, caps) );
+    UniquePtr<ptrT> ret( new ptrT(gl, egl, caps, &success) );
+
+    if (!success)
+        return nullptr;
+
     return Move(ret);
 }
 
 SurfaceFactory_ANGLEShareHandle::SurfaceFactory_ANGLEShareHandle(GLContext* gl,
                                                                  GLLibraryEGL* egl,
-                                                                 const SurfaceCaps& caps)
+                                                                 const SurfaceCaps& caps,
+                                                                 bool* const out_success)
     : SurfaceFactory(gl, SharedSurfaceType::EGLSurfaceANGLE, caps)
     , mProdGL(gl)
     , mEGL(egl)
 {
-    mConfig = ChooseConfig(mProdGL, mEGL, mReadCaps);
+    MOZ_ASSERT(out_success);
+    *out_success = false;
+
     mContext = GLContextEGL::Cast(mProdGL)->GetEGLContext();
+    mConfig = ChooseConfig(mProdGL, mEGL, mReadCaps);
+    if (mConfig == EGL_NO_CONFIG)
+        return;
+
     MOZ_ASSERT(mConfig && mContext);
+    *out_success = true;
 }
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/SharedSurfaceANGLE.h
+++ b/gfx/gl/SharedSurfaceANGLE.h
@@ -83,17 +83,18 @@ protected:
 
 public:
     static UniquePtr<SurfaceFactory_ANGLEShareHandle> Create(GLContext* gl,
                                                              const SurfaceCaps& caps);
 
 protected:
     SurfaceFactory_ANGLEShareHandle(GLContext* gl,
                                     GLLibraryEGL* egl,
-                                    const SurfaceCaps& caps);
+                                    const SurfaceCaps& caps,
+                                    bool* const out_success);
 
     virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) MOZ_OVERRIDE {
         bool hasAlpha = mReadCaps.alpha;
         return SharedSurface_ANGLEShareHandle::Create(mProdGL,
                                                       mContext, mConfig,
                                                       size, hasAlpha);
     }
 };
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -138,17 +138,17 @@ nsFontCache::GetMetricsFor(const nsFont&
         fm = mFontMetrics[i];
         if (fm->Font().Equals(aFont) && fm->GetUserFontSet() == aUserFontSet &&
             fm->Language() == aLanguage) {
             if (i != n) {
                 // promote it to the end of the cache
                 mFontMetrics.RemoveElementAt(i);
                 mFontMetrics.AppendElement(fm);
             }
-            fm->GetThebesFontGroup()->UpdateFontList();
+            fm->GetThebesFontGroup()->UpdateUserFonts();
             NS_ADDREF(aMetrics = fm);
             return NS_OK;
         }
     }
 
     // It's not in the cache. Get font metrics and then cache them.
 
     fm = new nsFontMetrics();
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -127,35 +127,32 @@ nsFontMetrics::Init(const nsFont& aFont,
                        aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
                        aFont.languageOverride);
 
     aFont.AddFontFeaturesToStyle(&style);
 
     mFontGroup = gfxPlatform::GetPlatform()->
         CreateFontGroup(aFont.fontlist, &style, aUserFontSet);
     mFontGroup->SetTextPerfMetrics(aTextPerf);
-    if (mFontGroup->FontListLength() < 1)
-        return NS_ERROR_UNEXPECTED;
-
     return NS_OK;
 }
 
 void
 nsFontMetrics::Destroy()
 {
     mDeviceContext = nullptr;
 }
 
 // XXXTODO get rid of this macro
 #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
 #define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
 
 const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
 {
-    return mFontGroup->GetFontAt(0)->GetMetrics();
+    return mFontGroup->GetFirstValidFont()->GetMetrics();
 }
 
 nscoord
 nsFontMetrics::XHeight()
 {
     return ROUND_TO_TWIPS(GetMetrics().xHeight);
 }
 
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -121,22 +121,16 @@ public:
      */
     static bool CheckSurfaceSize(const nsIntSize& sz, int32_t limit = 0);
 
     /* Provide a stride value that will respect all alignment requirements of
      * the accelerated image-rendering code.
      */
     static int32_t FormatStrideForWidth(gfxImageFormat format, int32_t width);
 
-    /* Return the default set of context flags for this surface; these are
-     * hints to the context about any special rendering considerations.  See
-     * gfxContext::SetFlag for documentation.
-     */
-    virtual int32_t GetDefaultContextFlags() const { return 0; }
-
     static gfxContentType ContentFromFormat(gfxImageFormat format);
 
     void SetSubpixelAntialiasingEnabled(bool aEnabled);
     bool GetSubpixelAntialiasingEnabled();
 
     /**
      * Record number of bytes for given surface type.  Use positive bytes
      * for allocations and negative bytes for deallocations.
--- a/gfx/thebes/gfxPDFSurface.h
+++ b/gfx/thebes/gfxPDFSurface.h
@@ -29,23 +29,16 @@ public:
     void GetDPI(double *xDPI, double *yDPI);
 
     // this is in points!
     virtual const gfxIntSize GetSize() const
     {
         return gfxIntSize(mSize.width, mSize.height);
     }
 
-    virtual int32_t GetDefaultContextFlags() const
-    {
-        return gfxContext::FLAG_SIMPLIFY_OPERATORS |
-               gfxContext::FLAG_DISABLE_SNAPPING |
-               gfxContext::FLAG_DISABLE_COPY_BACKGROUND;
-    }
-
 private:
     nsCOMPtr<nsIOutputStream> mStream;
     double mXDPI;
     double mYDPI;
     gfxSize mSize;
 };
 
 #endif /* GFX_PDFSURFACE_H */
--- a/gfx/thebes/gfxPSSurface.h
+++ b/gfx/thebes/gfxPSSurface.h
@@ -36,22 +36,16 @@ public:
     virtual bool GetRotateForLandscape() { return (mOrientation == LANDSCAPE); }
 
     // this is in points!
     virtual const gfxIntSize GetSize() const
     {
         return mSize;
     }
 
-    virtual int32_t GetDefaultContextFlags() const
-    {
-        return gfxContext::FLAG_SIMPLIFY_OPERATORS |
-               gfxContext::FLAG_DISABLE_SNAPPING;
-    }
-
 private:
     nsCOMPtr<nsIOutputStream> mStream;
     double mXDPI;
     double mYDPI;
     gfxIntSize mSize;
     PageOrientation mOrientation;
 };
 
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -808,32 +808,33 @@ FindFontPatterns(gfxUserFontSet *mUserFo
     style.style = aStyle;
     style.weight = aWeight;
     style.stretch = aStretch;
 
     gfxUserFcFontEntry *fontEntry = nullptr;
     gfxFontFamily *family = mUserFontSet->LookupFamily(utf16Family);
     if (family) {
         gfxUserFontEntry* userFontEntry =
-            mUserFontSet->FindUserFontEntry(family, style, needsBold,
-                                            aWaitForUserFont);
+            mUserFontSet->FindUserFontEntryAndLoad(family, style, needsBold,
+                                                   aWaitForUserFont);
         if (userFontEntry) {
             fontEntry = static_cast<gfxUserFcFontEntry*>
                 (userFontEntry->GetPlatformFontEntry());
         }
 
         // Accept synthetic oblique for italic and oblique.
         // xxx - this isn't really ideal behavior, for docs that only use a
         //       single italic face it will also pull down the normal face
         //       and probably never use it
         if (!fontEntry && aStyle != NS_FONT_STYLE_NORMAL) {
             style.style = NS_FONT_STYLE_NORMAL;
-            userFontEntry = mUserFontSet->FindUserFontEntry(family, style,
-                                                            needsBold,
-                                                            aWaitForUserFont);
+            userFontEntry =
+                mUserFontSet->FindUserFontEntryAndLoad(family, style,
+                                                       needsBold,
+                                                       aWaitForUserFont);
             if (userFontEntry) {
                 fontEntry = static_cast<gfxUserFcFontEntry*>
                     (userFontEntry->GetPlatformFontEntry());
             }
         }
     }
 
     if (!fontEntry) {
@@ -1303,34 +1304,40 @@ gfxPangoFontGroup::GetBaseFont()
     if (mFonts[0].Font() == nullptr) {
         gfxFont* font = GetBaseFontSet()->GetFontAt(0, GetStyle());
         mFonts[0] = FamilyFace(nullptr, font);
     }
 
     return static_cast<gfxFcFont*>(mFonts[0].Font());
 }
 
+gfxFont*
+gfxPangoFontGroup::GetFirstValidFont()
+{
+    return GetFontAt(0);
+}
+
 gfxFont *
 gfxPangoFontGroup::GetFontAt(int32_t i)
 {
     // If it turns out to be hard for all clients that cache font
-    // groups to call UpdateFontList at appropriate times, we could
-    // instead consider just calling UpdateFontList from someplace
+    // groups to call UpdateUserFonts at appropriate times, we could
+    // instead consider just calling UpdateUserFonts from someplace
     // more central (such as here).
     NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
                  "Whoever was caching this font group should have "
-                 "called UpdateFontList on it");
+                 "called UpdateUserFonts on it");
 
     NS_PRECONDITION(i == 0, "Only have one font");
 
     return GetBaseFont();
 }
 
 void
-gfxPangoFontGroup::UpdateFontList()
+gfxPangoFontGroup::UpdateUserFonts()
 {
     uint64_t newGeneration = GetGeneration();
     if (newGeneration == mCurrGeneration)
         return;
 
     mFonts[0] = FamilyFace();
     mFontSets.Clear();
     mCachedEllipsisTextRun = nullptr;
--- a/gfx/thebes/gfxPangoFonts.h
+++ b/gfx/thebes/gfxPangoFonts.h
@@ -25,19 +25,21 @@ class gfxPangoFontGroup : public gfxFont
 public:
     gfxPangoFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                       const gfxFontStyle *aStyle,
                       gfxUserFontSet *aUserFontSet);
     virtual ~gfxPangoFontGroup();
 
     virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
 
+    virtual gfxFont* GetFirstValidFont();
+
     virtual gfxFont *GetFontAt(int32_t i);
 
-    virtual void UpdateFontList();
+    virtual void UpdateUserFonts();
 
     virtual already_AddRefed<gfxFont>
         FindFontForChar(uint32_t aCh, uint32_t aPrevCh, int32_t aRunScript,
                         gfxFont *aPrevMatchedFont,
                         uint8_t *aMatchType);
 
     static void Shutdown();
 
--- a/gfx/thebes/gfxQuartzSurface.cpp
+++ b/gfx/thebes/gfxQuartzSurface.cpp
@@ -10,19 +10,18 @@
 #include "cairo-quartz.h"
 
 void
 gfxQuartzSurface::MakeInvalid()
 {
     mSize = gfxIntSize(-1, -1);    
 }
 
-gfxQuartzSurface::gfxQuartzSurface(const gfxSize& desiredSize, gfxImageFormat format,
-                                   bool aForPrinting)
-    : mCGContext(nullptr), mSize(desiredSize), mForPrinting(aForPrinting)
+gfxQuartzSurface::gfxQuartzSurface(const gfxSize& desiredSize, gfxImageFormat format)
+    : mCGContext(nullptr), mSize(desiredSize)
 {
     gfxIntSize size((unsigned int) floor(desiredSize.width),
                     (unsigned int) floor(desiredSize.height));
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
@@ -36,19 +35,18 @@ gfxQuartzSurface::gfxQuartzSurface(const
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
     }
 }
 
 gfxQuartzSurface::gfxQuartzSurface(CGContextRef context,
-                                   const gfxSize& desiredSize,
-                                   bool aForPrinting)
-    : mCGContext(context), mSize(desiredSize), mForPrinting(aForPrinting)
+                                   const gfxSize& desiredSize)
+    : mCGContext(context), mSize(desiredSize)
 {
     gfxIntSize size((unsigned int) floor(desiredSize.width),
                     (unsigned int) floor(desiredSize.height));
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
@@ -61,19 +59,18 @@ gfxQuartzSurface::gfxQuartzSurface(CGCon
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
     }
 }
 
 gfxQuartzSurface::gfxQuartzSurface(CGContextRef context,
-                                   const gfxIntSize& size,
-                                   bool aForPrinting)
-    : mCGContext(context), mSize(size), mForPrinting(aForPrinting)
+                                   const gfxIntSize& size)
+    : mCGContext(context), mSize(size)
 {
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
 
     cairo_surface_t *surf = 
@@ -84,32 +81,30 @@ gfxQuartzSurface::gfxQuartzSurface(CGCon
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
     }
 }
 
 gfxQuartzSurface::gfxQuartzSurface(cairo_surface_t *csurf,
-                                   const gfxIntSize& aSize,
-                                   bool aForPrinting) :
-    mSize(aSize), mForPrinting(aForPrinting)
+                                   const gfxIntSize& aSize) :
+    mSize(aSize)
 {
     mCGContext = cairo_quartz_surface_get_cg_context (csurf);
     CGContextRetain (mCGContext);
 
     Init(csurf, true);
 }
 
 gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
                                    const gfxSize& desiredSize,
                                    long stride,
-                                   gfxImageFormat format,
-                                   bool aForPrinting)
-    : mCGContext(nullptr), mSize(desiredSize), mForPrinting(aForPrinting)
+                                   gfxImageFormat format)
+    : mCGContext(nullptr), mSize(desiredSize)
 {
     gfxIntSize size((unsigned int) floor(desiredSize.width),
                     (unsigned int) floor(desiredSize.height));
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
@@ -125,19 +120,18 @@ gfxQuartzSurface::gfxQuartzSurface(unsig
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * stride + sizeof(gfxQuartzSurface));
     }
 }
 
 gfxQuartzSurface::gfxQuartzSurface(unsigned char *data,
                                    const gfxIntSize& aSize,
                                    long stride,
-                                   gfxImageFormat format,
-                                   bool aForPrinting)
-    : mCGContext(nullptr), mSize(aSize.width, aSize.height), mForPrinting(aForPrinting)
+                                   gfxImageFormat format)
+    : mCGContext(nullptr), mSize(aSize.width, aSize.height)
 {
     if (!CheckSurfaceSize(aSize))
         MakeInvalid();
 
     cairo_surface_t *surf = cairo_quartz_surface_create_for_data
         (data, (cairo_format_t) format, aSize.width, aSize.height, stride);
 
     mCGContext = cairo_quartz_surface_get_cg_context (surf);
@@ -168,25 +162,16 @@ gfxQuartzSurface::CreateSimilarSurface(g
 }
 
 CGContextRef
 gfxQuartzSurface::GetCGContextWithClip(gfxContext *ctx)
 {
     return cairo_quartz_get_cg_context_with_clip(ctx->GetCairo());
 }
 
-int32_t gfxQuartzSurface::GetDefaultContextFlags() const
-{
-    if (mForPrinting)
-        return gfxContext::FLAG_DISABLE_SNAPPING |
-               gfxContext::FLAG_DISABLE_COPY_BACKGROUND;
-
-    return 0;
-}
-
 already_AddRefed<gfxImageSurface> gfxQuartzSurface::GetAsImageSurface()
 {
     cairo_surface_t *surface = cairo_quartz_surface_get_image(mSurface);
     if (!surface || cairo_surface_status(surface))
         return nullptr;
 
     nsRefPtr<gfxASurface> img = Wrap(surface);
 
--- a/gfx/thebes/gfxQuartzSurface.h
+++ b/gfx/thebes/gfxQuartzSurface.h
@@ -12,39 +12,36 @@
 
 #include <Carbon/Carbon.h>
 
 class gfxContext;
 class gfxImageSurface;
 
 class gfxQuartzSurface : public gfxASurface {
 public:
-    gfxQuartzSurface(const gfxSize& size, gfxImageFormat format, bool aForPrinting = false);
-    gfxQuartzSurface(CGContextRef context, const gfxSize& size, bool aForPrinting = false);
-    gfxQuartzSurface(CGContextRef context, const gfxIntSize& size, bool aForPrinting = false);
-    gfxQuartzSurface(cairo_surface_t *csurf, const gfxIntSize& aSize, bool aForPrinting = false);
-    gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
-    gfxQuartzSurface(unsigned char *data, const gfxIntSize& size, long stride, gfxImageFormat format, bool aForPrinting = false);
+    gfxQuartzSurface(const gfxSize& size, gfxImageFormat format);
+    gfxQuartzSurface(CGContextRef context, const gfxSize& size);
+    gfxQuartzSurface(CGContextRef context, const gfxIntSize& size);
+    gfxQuartzSurface(cairo_surface_t *csurf, const gfxIntSize& aSize);
+    gfxQuartzSurface(unsigned char *data, const gfxSize& size, long stride, gfxImageFormat format);
+    gfxQuartzSurface(unsigned char *data, const gfxIntSize& size, long stride, gfxImageFormat format);
 
     virtual ~gfxQuartzSurface();
 
     virtual already_AddRefed<gfxASurface> CreateSimilarSurface(gfxContentType aType,
                                                                const gfxIntSize& aSize);
 
     virtual const gfxIntSize GetSize() const { return gfxIntSize(mSize.width, mSize.height); }
 
     CGContextRef GetCGContext() { return mCGContext; }
 
     CGContextRef GetCGContextWithClip(gfxContext *ctx);
 
-    virtual int32_t GetDefaultContextFlags() const;
-
     already_AddRefed<gfxImageSurface> GetAsImageSurface();
 
 protected:
     void MakeInvalid();
 
     CGContextRef mCGContext;
     gfxSize      mSize;
-    bool mForPrinting;
 };
 
 #endif /* GFX_QUARTZSURFACE_H */
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1464,21 +1464,25 @@ gfxFontGroup::gfxFontGroup(const FontFam
     , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
     , mHyphenWidth(-1)
     , mUserFontSet(aUserFontSet)
     , mTextPerf(nullptr)
     , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
     , mSkipDrawing(false)
 {
     // We don't use SetUserFontSet() here, as we want to unconditionally call
-    // BuildFontList() rather than only do UpdateFontList() if it changed.
+    // BuildFontList() rather than only do UpdateUserFonts() if it changed.
     mCurrGeneration = GetGeneration();
     BuildFontList();
 }
 
+gfxFontGroup::~gfxFontGroup()
+{
+}
+
 void
 gfxFontGroup::FindGenericFonts(FontFamilyType aGenericType,
                                nsIAtom *aLanguage,
                                void *aClosure)
 {
     nsAutoTArray<nsString, 5> resolvedGenerics;
     ResolveGenericFontNames(aGenericType, aLanguage, resolvedGenerics);
     uint32_t g = 0, numGenerics = resolvedGenerics.Length();
@@ -1606,87 +1610,17 @@ void gfxFontGroup::EnumerateFontList(nsI
     }
 }
 
 void
 gfxFontGroup::BuildFontList()
 {
 // gfxPangoFontGroup behaves differently, so this method is a no-op on that platform
 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
-
     EnumerateFontList(mStyle.language);
-
-    // at this point, fontlist should have been filled in
-    // get a default font if none exists
-    if (mFonts.Length() == 0) {
-        bool needsBold;
-        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
-        gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
-        NS_ASSERTION(defaultFamily,
-                     "invalid default font returned by GetDefaultFont");
-
-        if (defaultFamily) {
-            gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
-                                                               needsBold);
-            if (fe) {
-                nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
-                                                            needsBold);
-                if (font) {
-                    mFonts.AppendElement(FamilyFace(defaultFamily, font));
-                }
-            }
-        }
-
-        if (mFonts.Length() == 0) {
-            // Try for a "font of last resort...."
-            // Because an empty font list would be Really Bad for later code
-            // that assumes it will be able to get valid metrics for layout,
-            // just look for the first usable font and put in the list.
-            // (see bug 554544)
-            nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
-            pfl->GetFontFamilyList(families);
-            uint32_t count = families.Length();
-            for (uint32_t i = 0; i < count; ++i) {
-                gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
-                                                                 needsBold);
-                if (fe) {
-                    nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
-                                                                needsBold);
-                    if (font) {
-                        mFonts.AppendElement(FamilyFace(families[i], font));
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (mFonts.Length() == 0) {
-            // an empty font list at this point is fatal; we're not going to
-            // be able to do even the most basic layout operations
-            char msg[256]; // CHECK buffer length if revising message below
-            nsAutoString families;
-            mFamilyList.ToString(families);
-            sprintf(msg, "unable to find a usable font (%.220s)",
-                    NS_ConvertUTF16toUTF8(families).get());
-            NS_RUNTIMEABORT(msg);
-        }
-    }
-
-    if (!mStyle.systemFont) {
-        uint32_t count = mFonts.Length();
-        for (uint32_t i = 0; i < count; ++i) {
-            gfxFont* font = mFonts[i].Font();
-            if (font->GetFontEntry()->mIsBadUnderlineFont) {
-                gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset;
-                gfxFloat bad = font->GetMetrics().underlineOffset;
-                mUnderlineOffset = std::min(first, bad);
-                break;
-            }
-        }
-    }
 #endif
 }
 
 void
 gfxFontGroup::FindPlatformFont(const nsAString& aName,
                                bool aUseFontSet,
                                void *aClosure)
 {
@@ -1695,76 +1629,190 @@ gfxFontGroup::FindPlatformFont(const nsA
     gfxFontEntry *fe = nullptr;
 
     if (aUseFontSet) {
         // First, look up in the user font set...
         // If the fontSet matches the family, we must not look for a platform
         // font of the same name, even if we fail to actually get a fontEntry
         // here; we'll fall back to the next name in the CSS font-family list.
         if (mUserFontSet) {
-            // If the fontSet matches the family, but the font has not yet finished
-            // loading (nor has its load timeout fired), the fontGroup should wait
-            // for the download, and not actually draw its text yet.
+            // add the userfont to the fontlist whether it's already been loaded
+            // or not. loading is initiated during font matching.
             family = mUserFontSet->LookupFamily(aName);
             if (family) {
-                bool waitForUserFont = false;
-                gfxUserFontEntry* userFontEntry =
-                    mUserFontSet->FindUserFontEntry(family, mStyle, needsBold,
-                                                    waitForUserFont);
-                if (userFontEntry) {
-                    fe = userFontEntry->GetPlatformFontEntry();
-                }
-                if (!fe && waitForUserFont) {
-                    mSkipDrawing = true;
-                }
+                fe = mUserFontSet->FindUserFontEntry(family, mStyle, needsBold);
             }
         }
     }
 
     // Not known in the user font set ==> check system fonts
     if (!family) {
         gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
         family = fontList->FindFamily(aName, mStyle.systemFont);
         if (family) {
             fe = family->FindFontForStyle(mStyle, needsBold);
         }
     }
 
     // add to the font group, unless it's already there
     if (fe && !HasFont(fe)) {
-        nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
-        if (font) {
-            mFonts.AppendElement(FamilyFace(family, font));
-        }
+        mFonts.AppendElement(FamilyFace(family, fe, needsBold));
     }
 }
 
 bool
 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
 {
     uint32_t count = mFonts.Length();
     for (uint32_t i = 0; i < count; ++i) {
-        if (mFonts[i].Font()->GetFontEntry() == aFontEntry)
+        if (mFonts[i].FontEntry() == aFontEntry) {
             return true;
+        }
     }
     return false;
 }
 
-gfxFontGroup::~gfxFontGroup()
+gfxFont*
+gfxFontGroup::GetFontAt(int32_t i)
+{
+    if (uint32_t(i) >= mFonts.Length()) {
+        return nullptr;
+    }
+
+    FamilyFace& ff = mFonts[i];
+    if (ff.IsInvalid() || ff.IsLoading()) {
+        return nullptr;
+    }
+
+    nsRefPtr<gfxFont> font = ff.Font();
+    if (!font) {
+        gfxFontEntry *fe = mFonts[i].FontEntry();
+        if (fe->mIsUserFontContainer) {
+            gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
+            if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) {
+                ufe->Load();
+                if (ufe->WaitForUserFont()) {
+                    mSkipDrawing = true;
+                }
+            }
+            fe = ufe->GetPlatformFontEntry();
+            if (!fe) {
+                return nullptr;
+            }
+        }
+        font = fe->FindOrMakeFont(&mStyle, mFonts[i].NeedsBold());
+        if (font && !font->Valid()) {
+            ff.SetInvalid();
+            return nullptr;
+        }
+        mFonts[i].SetFont(font);
+    }
+    return font.get();
+}
+
+gfxFont*
+gfxFontGroup::GetDefaultFont()
 {
-    mFonts.Clear();
+    if (mDefaultFont) {
+        return mDefaultFont.get();
+    }
+
+    bool needsBold;
+    gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+    gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
+    NS_ASSERTION(defaultFamily,
+                 "invalid default font returned by GetDefaultFont");
+
+    if (defaultFamily) {
+        gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
+                                                           needsBold);
+        if (fe) {
+            mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
+        }
+    }
+
+    if (!mDefaultFont) {
+        // Try for a "font of last resort...."
+        // Because an empty font list would be Really Bad for later code
+        // that assumes it will be able to get valid metrics for layout,
+        // just look for the first usable font and put in the list.
+        // (see bug 554544)
+        nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
+        pfl->GetFontFamilyList(families);
+        uint32_t count = families.Length();
+        for (uint32_t i = 0; i < count; ++i) {
+            gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
+                                                             needsBold);
+            if (fe) {
+                mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
+            }
+        }
+    }
+
+    if (!mDefaultFont) {
+        // an empty font list at this point is fatal; we're not going to
+        // be able to do even the most basic layout operations
+        char msg[256]; // CHECK buffer length if revising message below
+        nsAutoString families;
+        mFamilyList.ToString(families);
+        sprintf(msg, "unable to find a usable font (%.220s)",
+                NS_ConvertUTF16toUTF8(families).get());
+        NS_RUNTIMEABORT(msg);
+    }
+
+    return mDefaultFont.get();
+}
+
+
+gfxFont*
+gfxFontGroup::GetFirstValidFont()
+{
+    uint32_t count = mFonts.Length();
+    for (uint32_t i = 0; i < count; ++i) {
+        FamilyFace& ff = mFonts[i];
+        if (ff.IsInvalid()) {
+            continue;
+        }
+
+        // already have a font?
+        gfxFont* font = ff.Font();
+        if (font) {
+            return font;
+        }
+
+        // need to build a font, loading userfont if not loaded
+        if (ff.IsUserFont()) {
+            gfxUserFontEntry* ufe =
+                static_cast<gfxUserFontEntry*>(mFonts[i].FontEntry());
+            if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) {
+                ufe->Load();
+                if (ufe->WaitForUserFont()) {
+                    mSkipDrawing = true;
+                }
+            }
+            if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED) {
+                continue;
+            }
+        }
+
+        font = GetFontAt(i);
+        if (font) {
+            return font;
+        }
+    }
+    return GetDefaultFont();
 }
 
 gfxFont *
 gfxFontGroup::GetFirstMathFont()
 {
     uint32_t count = mFonts.Length();
     for (uint32_t i = 0; i < count; ++i) {
         gfxFont* font = GetFontAt(i);
-        if (font->GetFontEntry()->TryGetMathTable()) {
+        if (font && font->GetFontEntry()->TryGetMathTable()) {
             return font;
         }
     }
     return nullptr;
 }
 
 gfxFontGroup *
 gfxFontGroup::Copy(const gfxFontStyle *aStyle)
@@ -1813,17 +1861,17 @@ gfxFontGroup::MakeSpaceTextRun(const Par
         return nullptr;
     }
 
     uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
     if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
         orientation = TEXT_ORIENT_VERTICAL_UPRIGHT;
     }
 
-    gfxFont *font = GetFontAt(0);
+    gfxFont *font = GetFirstValidFont();
     if (MOZ_UNLIKELY(GetStyle()->size == 0)) {
         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
         // them, and always create at least size 1 fonts, i.e. they still
         // render something for size 0 fonts.
         textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false,
                              orientation);
     }
     else {
@@ -1859,30 +1907,30 @@ gfxFontGroup::MakeBlankTextRun(uint32_t 
     if (!textRun) {
         return nullptr;
     }
 
     uint16_t orientation = aFlags & TEXT_ORIENT_MASK;
     if (orientation == TEXT_ORIENT_VERTICAL_MIXED) {
         orientation = TEXT_ORIENT_VERTICAL_UPRIGHT;
     }
-    textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false,
+    textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
                          orientation);
     return textRun;
 }
 
 gfxTextRun *
 gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
 {
     // only use U+2010 if it is supported by the first font in the group;
     // it's better to use ASCII '-' from the primary font than to fall back to
     // U+2010 from some other, possibly poorly-matching face
     static const char16_t hyphen = 0x2010;
-    gfxFont *font = GetFontAt(0);
-    if (font && font->HasCharacter(hyphen)) {
+    gfxFont *font = GetFirstValidFont();
+    if (font->HasCharacter(hyphen)) {
         return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
                            gfxFontGroup::TEXT_IS_PERSISTENT);
     }
 
     static const uint8_t dash = '-';
     return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
                        gfxFontGroup::TEXT_IS_PERSISTENT);
 }
@@ -2142,17 +2190,25 @@ gfxFontGroup::InitScriptRun(gfxContext *
                                               // within the textrun
                             uint32_t aLength, // length of the script run
                             int32_t aRunScript)
 {
     NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
     NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
                  "don't call InitScriptRun with aborted shaping state");
 
-    gfxFont *mainFont = GetFontAt(0);
+#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
+    // non-linux platforms build the fontlist lazily and include userfonts
+    // so need to confirm the load state of userfonts in the list
+    if (mUserFontSet && mCurrGeneration != mUserFontSet->GetGeneration()) {
+        UpdateUserFonts();
+    }
+#endif
+
+    gfxFont *mainFont = GetFirstValidFont();
 
     uint32_t runStart = 0;
     nsAutoTArray<gfxTextRange,3> fontRanges;
     ComputeRanges(fontRanges, aString, aLength, aRunScript,
                   aTextRun->GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK);
     uint32_t numRanges = fontRanges.Length();
 
     for (uint32_t r = 0; r < numRanges; r++) {
@@ -2348,17 +2404,17 @@ gfxFontGroup::GetEllipsisTextRun(int32_t
 {
     if (mCachedEllipsisTextRun &&
         mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
         return mCachedEllipsisTextRun;
     }
 
     // Use a Unicode ellipsis if the font supports it,
     // otherwise use three ASCII periods as fallback.
-    gfxFont* firstFont = GetFontAt(0);
+    gfxFont* firstFont = GetFirstValidFont();
     nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
         ? nsDependentString(kEllipsisChar,
                             ArrayLength(kEllipsisChar) - 1)
         : nsDependentString(kASCIIPeriodsChar,
                             ArrayLength(kASCIIPeriodsChar) - 1);
 
     nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
     Parameters params = {
@@ -2371,62 +2427,105 @@ gfxFontGroup::GetEllipsisTextRun(int32_t
     }
     mCachedEllipsisTextRun = textRun;
     textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
                                  // textrun prolong the fontgroup's life
     return textRun;
 }
 
 already_AddRefed<gfxFont>
-gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh)
+gfxFontGroup::FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh)
 {
+    NS_ASSERTION(mStyle.style != NS_FONT_STYLE_NORMAL,
+                 "should only be called in the italic/oblique case");
+
     if (!aFamily->TestCharacterMap(aCh)) {
         return nullptr;
     }
 
-    // Note that we don't need the actual runScript in matchData for
-    // gfxFontFamily::SearchAllFontsForChar, it's only used for the
-    // system-fallback case. So we can just set it to 0 here.
-    GlobalFontMatch matchData(aCh, 0, &mStyle);
-    aFamily->SearchAllFontsForChar(&matchData);
-    gfxFontEntry *fe = matchData.mBestMatch;
-    if (!fe) {
+    gfxFontStyle regularStyle = mStyle;
+    regularStyle.style = NS_FONT_STYLE_NORMAL;
+    bool needsBold;
+    gfxFontEntry *fe = aFamily->FindFontForStyle(regularStyle, needsBold);
+    NS_ASSERTION(!fe->mIsUserFontContainer,
+                 "should only be searching platform fonts");
+    if (!fe->HasCharacter(aCh)) {
         return nullptr;
     }
 
-    bool needsBold = mStyle.weight >= 600 && !fe->IsBold();
     nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
+    if (!font->Valid()) {
+        return nullptr;
+    }
     return font.forget();
 }
 
+gfxFloat
+gfxFontGroup::GetUnderlineOffset()
+{
+    if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
+        // if the fontlist contains a bad underline font, make the underline
+        // offset the min of the first valid font and bad font underline offsets
+        uint32_t len = mFonts.Length();
+        for (uint32_t i = 0; i < len; i++) {
+            FamilyFace& ff = mFonts[i];
+            if (!ff.IsUserFont() && ff.Family() &&
+                ff.Family()->IsBadUnderlineFamily()) {
+                nsRefPtr<gfxFont> font = GetFontAt(i);
+                if (!font) {
+                    continue;
+                }
+                gfxFloat bad = font->GetMetrics().underlineOffset;
+                gfxFloat first =
+                    GetFirstValidFont()->GetMetrics().underlineOffset;
+                mUnderlineOffset = std::min(first, bad);
+                return mUnderlineOffset;
+            }
+        }
+
+        // no bad underline fonts, use the first valid font's metric
+        mUnderlineOffset = GetFirstValidFont()->GetMetrics().underlineOffset;
+    }
+
+    return mUnderlineOffset;
+}
+
 already_AddRefed<gfxFont>
 gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
                               int32_t aRunScript, gfxFont *aPrevMatchedFont,
                               uint8_t *aMatchType)
 {
     // To optimize common cases, try the first font in the font-group
     // before going into the more detailed checks below
     uint32_t nextIndex = 0;
     bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
     bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
     bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
 
     if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
-        nsRefPtr<gfxFont> firstFont = mFonts[0].Font();
-        if (firstFont->HasCharacter(aCh)) {
-            *aMatchType = gfxTextRange::kFontGroup;
-            return firstFont.forget();
+        nsRefPtr<gfxFont> firstFont = GetFontAt(0);
+        if (firstFont) {
+            if (firstFont->HasCharacter(aCh)) {
+                *aMatchType = gfxTextRange::kFontGroup;
+                return firstFont.forget();
+            }
+
+            // If italic, test the regular face to see if it supports character.
+            // Only do this for platform fonts, not userfonts.
+            if (mStyle.style != NS_FONT_STYLE_NORMAL &&
+                !firstFont->GetFontEntry()->IsUserFont()) {
+                nsRefPtr<gfxFont> font =
+                    FindNonItalicFaceForChar(mFonts[0].Family(), aCh);
+                if (font) {
+                    *aMatchType = gfxTextRange::kFontGroup;
+                    return font.forget();
+                }
+            }
         }
-        // It's possible that another font in the family (e.g. regular face,
-        // where the requested style was italic) will support the character
-        nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh);
-        if (font) {
-            *aMatchType = gfxTextRange::kFontGroup;
-            return font.forget();
-        }
+
         // we don't need to check the first font again below
         ++nextIndex;
     }
 
     if (aPrevMatchedFont) {
         // Don't switch fonts for control characters, regardless of
         // whether they are present in the current font, as they won't
         // actually be rendered (see bug 716229)
@@ -2454,28 +2553,69 @@ gfxFontGroup::FindFontForChar(uint32_t a
             nsRefPtr<gfxFont> ret = aPrevMatchedFont;
             return ret.forget();
         }
         // VS alone. it's meaningless to search different fonts
         return nullptr;
     }
 
     // 1. check remaining fonts in the font group
-    uint32_t fontListLength = FontListLength();
+    uint32_t fontListLength = mFonts.Length();
     for (uint32_t i = nextIndex; i < fontListLength; i++) {
-        nsRefPtr<gfxFont> font = mFonts[i].Font();
-        if (font->HasCharacter(aCh)) {
-            *aMatchType = gfxTextRange::kFontGroup;
-            return font.forget();
+        FamilyFace& ff = mFonts[i];
+        if (ff.IsInvalid() || ff.IsLoading()) {
+            continue;
         }
 
-        font = TryAllFamilyMembers(mFonts[i].Family(), aCh);
-        if (font) {
+        nsRefPtr<gfxFont> font;
+
+        // test the font entry, build font if needed
+        gfxFontEntry *fe = ff.FontEntry();
+
+        if (fe->mIsUserFontContainer) {
+            // for userfonts, need to test the cmap of the platform font entry
+            gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
+            if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED) {
+                ufe->Load();
+                if (ufe->WaitForUserFont()) {
+                    mSkipDrawing = true;
+                }
+            }
+            gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
+            if (pfe && pfe->HasCharacter(aCh)) {
+                font = GetFontAt(i);
+                if (font) {
+                    *aMatchType = gfxTextRange::kFontGroup;
+                    return font.forget();
+                }
+            }
+        } else if (fe->HasCharacter(aCh)) {
+            font = GetFontAt(i);
+            if (font) {
+                *aMatchType = gfxTextRange::kFontGroup;
+                return font.forget();
+            }
+        }
+
+        // If italic, test the regular face to see if it supports the character.
+        // Only do this for platform fonts, not userfonts.
+        if (mStyle.style != NS_FONT_STYLE_NORMAL && !ff.IsUserFont()) {
+            font = FindNonItalicFaceForChar(mFonts[i].Family(), aCh);
+            if (font) {
+                *aMatchType = gfxTextRange::kFontGroup;
+                return font.forget();
+            }
+        }
+    }
+
+    if (fontListLength == 0) {
+        nsRefPtr<gfxFont> defaultFont = GetDefaultFont();
+        if (defaultFont->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
-            return font.forget();
+            return defaultFont.forget();
         }
     }
 
     // if character is in Private Use Area, don't do matching against pref or system fonts
     if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
         return nullptr;
 
     // 2. search pref fonts
@@ -2497,17 +2637,17 @@ gfxFontGroup::FindFontForChar(uint32_t a
     if (aRunScript == HB_SCRIPT_UNKNOWN) {
         return nullptr;
     }
 
     // for known "space" characters, don't do a full system-fallback search;
     // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
     if (GetGeneralCategory(aCh) ==
             HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
-        GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
+        GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0)
     {
         return nullptr;
     }
 
     // -- otherwise look for other stuff
     *aMatchType = gfxTextRange::kSystemFallback;
     font = WhichSystemFontSupportsChar(aCh, aRunScript);
     return font.forget();
@@ -2522,17 +2662,17 @@ void gfxFontGroup::ComputeRanges(nsTArra
     NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
 
     uint32_t prevCh = 0;
     int32_t lastRangeIndex = -1;
 
     // initialize prevFont to the group's primary font, so that this will be
     // used for string-initial control chars, etc rather than risk hitting font
     // fallback for these (bug 716229)
-    gfxFont *prevFont = GetFontAt(0);
+    gfxFont *prevFont = GetFirstValidFont();
 
     // if we use the initial value of prevFont, we treat this as a match from
     // the font group; fixes bug 978313
     uint8_t matchType = gfxTextRange::kFontGroup;
 
     for (uint32_t i = 0; i < aLength; i++) {
 
         const uint32_t origI = i; // save off in case we increase for surrogate
@@ -2625,40 +2765,81 @@ gfxFontGroup::GetUserFontSet()
 void 
 gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
 {
     if (aUserFontSet == mUserFontSet) {
         return;
     }
     mUserFontSet = aUserFontSet;
     mCurrGeneration = GetGeneration() - 1;
-    UpdateFontList();
+    UpdateUserFonts();
 }
 
 uint64_t
 gfxFontGroup::GetGeneration()
 {
     if (!mUserFontSet)
         return 0;
     return mUserFontSet->GetGeneration();
 }
 
-// note: gfxPangoFontGroup overrides UpdateFontList, such that
+uint64_t
+gfxFontGroup::GetRebuildGeneration()
+{
+    if (!mUserFontSet)
+        return 0;
+    return mUserFontSet->GetRebuildGeneration();
+}
+
+// note: gfxPangoFontGroup overrides UpdateUserFonts, such that
 //       BuildFontList is never used
 void
-gfxFontGroup::UpdateFontList()
+gfxFontGroup::UpdateUserFonts()
 {
-    if (mCurrGeneration != GetGeneration()) {
-        // xxx - can probably improve this to detect when all fonts were found, so no need to update list
+    if (mCurrGeneration < GetRebuildGeneration()) {
+        // fonts in userfont set changed, need to redo the fontlist
         mFonts.Clear();
         mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
         mSkipDrawing = false;
         BuildFontList();
         mCurrGeneration = GetGeneration();
         mCachedEllipsisTextRun = nullptr;
+    } else if (mCurrGeneration != GetGeneration()) {
+        // load state change occurred, verify load state and validity of fonts
+        mSkipDrawing = false;
+        mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
+        mCachedEllipsisTextRun = nullptr;
+
+        uint32_t len = mFonts.Length();
+        for (uint32_t i = 0; i < len; i++) {
+            FamilyFace& ff = mFonts[i];
+            if (ff.Font() || !ff.IsUserFont()) {
+                continue;
+            }
+
+            // confirm status
+            gfxUserFontEntry *ufe =
+                static_cast<gfxUserFontEntry*>(mFonts[i].FontEntry());
+            gfxUserFontEntry::UserFontLoadState state = ufe->LoadState();
+            switch (state) {
+                case gfxUserFontEntry::STATUS_LOADING:
+                    ff.SetLoading(true);
+                    break;
+                case gfxUserFontEntry::STATUS_FAILED:
+                    ff.SetInvalid();
+                    // fall-thru to the default case
+                default:
+                    ff.SetLoading(false);
+            }
+            if (ufe->WaitForUserFont()) {
+                mSkipDrawing = true;
+            }
+        }
+
+        mCurrGeneration = GetGeneration();
     }
 }
 
 struct PrefFontCallbackData {
     explicit PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
         : mPrefFamilies(aFamiliesArray)
     {}
 
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -718,68 +718,33 @@ private:
 
     // shaping state for handling variant fallback features
     // such as subscript/superscript variant glyphs
     ShapingState      mShapingState;
 };
 
 class gfxFontGroup : public gfxTextRunFactory {
 public:
-    class FamilyFace {
-    public:
-        FamilyFace() { }
-
-        FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
-            : mFamily(aFamily), mFont(aFont)
-        {
-            NS_ASSERTION(aFont, "font pointer must not be null");
-            NS_ASSERTION(!aFamily ||
-                         aFamily->ContainsFace(aFont->GetFontEntry()),
-                         "font is not a member of the given family");
-        }
-
-        gfxFontFamily* Family() const { return mFamily.get(); }
-        gfxFont* Font() const { return mFont.get(); }
-
-    private:
-        nsRefPtr<gfxFontFamily> mFamily;
-        nsRefPtr<gfxFont>       mFont;
-    };
-
     static void Shutdown(); // platform must call this to release the languageAtomService
 
     gfxFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                  const gfxFontStyle *aStyle,
                  gfxUserFontSet *aUserFontSet = nullptr);
 
     virtual ~gfxFontGroup();
 
-    virtual gfxFont *GetFontAt(int32_t i) {
-        // If it turns out to be hard for all clients that cache font
-        // groups to call UpdateFontList at appropriate times, we could
-        // instead consider just calling UpdateFontList from someplace
-        // more central (such as here).
-        NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
-                     "Whoever was caching this font group should have "
-                     "called UpdateFontList on it");
-        NS_ASSERTION(mFonts.Length() > uint32_t(i) && mFonts[i].Font(), 
-                     "Requesting a font index that doesn't exist");
-
-        return mFonts[i].Font();
-    }
+    // Returns first valid font in the fontlist or default font.
+    // Initiates userfont loads if userfont not loaded
+    virtual gfxFont* GetFirstValidFont();
 
     // Returns the first font in the font-group that has an OpenType MATH table,
     // or null if no such font is available. The GetMathConstant methods may be
     // called on the returned font.
     gfxFont *GetFirstMathFont();
 
-    uint32_t FontListLength() const {
-        return mFonts.Length();
-    }
-
     const gfxFontStyle *GetStyle() const { return &mStyle; }
 
     virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
 
     /**
      * The listed characters should be treated as invisible and zero-width
      * when creating textruns.
      */
@@ -840,25 +805,22 @@ public:
     /**
      * Check whether a given font (specified by its gfxFontEntry)
      * is already in the fontgroup's list of actual fonts
      */
     bool HasFont(const gfxFontEntry *aFontEntry);
 
     // This returns the preferred underline for this font group.
     // Some CJK fonts have wrong underline offset in its metrics.
-    // If this group has such "bad" font, each platform's gfxFontGroup initialized mUnderlineOffset.
-    // The value should be lower value of first font's metrics and the bad font's metrics.
-    // Otherwise, this returns from first font's metrics.
+    // If this group has such "bad" font, each platform's gfxFontGroup
+    // initialized mUnderlineOffset. The value should be lower value of
+    // first font's metrics and the bad font's metrics. Otherwise, this
+    // returns from first font's metrics.
     enum { UNDERLINE_OFFSET_NOT_SET = INT16_MAX };
-    virtual gfxFloat GetUnderlineOffset() {
-        if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET)
-            mUnderlineOffset = GetFontAt(0)->GetMetrics().underlineOffset;
-        return mUnderlineOffset;
-    }
+    virtual gfxFloat GetUnderlineOffset();
 
     virtual already_AddRefed<gfxFont>
         FindFontForChar(uint32_t ch, uint32_t prevCh, int32_t aRunScript,
                         gfxFont *aPrevMatchedFont,
                         uint8_t *aMatchType);
 
     // search through pref fonts for a character, return nullptr if no matching pref font
     virtual already_AddRefed<gfxFont> WhichPrefFontSupportsChar(uint32_t aCh);
@@ -874,53 +836,191 @@ public:
     gfxUserFontSet* GetUserFontSet();
 
     // With downloadable fonts, the composition of the font group can change as fonts are downloaded
     // for each change in state of the user font set, the generation value is bumped to avoid picking up
     // previously created text runs in the text run word cache.  For font groups based on stylesheets
     // with no @font-face rule, this always returns 0.
     uint64_t GetGeneration();
 
+    // generation of the latest fontset rebuild, 0 when no fontset present
+    uint64_t GetRebuildGeneration();
+
     // used when logging text performance
     gfxTextPerfMetrics *GetTextPerfMetrics() { return mTextPerf; }
     void SetTextPerfMetrics(gfxTextPerfMetrics *aTextPerf) { mTextPerf = aTextPerf; }
 
-    // This will call UpdateFontList() if the user font set is changed.
+    // This will call UpdateUserFonts() if the user font set is changed.
     void SetUserFontSet(gfxUserFontSet *aUserFontSet);
 
     // If there is a user font set, check to see whether the font list or any
     // caches need updating.
-    virtual void UpdateFontList();
+    virtual void UpdateUserFonts();
 
     bool ShouldSkipDrawing() const {
         return mSkipDrawing;
     }
 
     class LazyReferenceContextGetter {
     public:
       virtual already_AddRefed<gfxContext> GetRefContext() = 0;
     };
     // The gfxFontGroup keeps ownership of this textrun.
     // It is only guaranteed to exist until the next call to GetEllipsisTextRun
     // (which might use a different appUnitsPerDev value) for the font group,
-    // or until UpdateFontList is called, or the fontgroup is destroyed.
+    // or until UpdateUserFonts is called, or the fontgroup is destroyed.
     // Get it/use it/forget it :) - don't keep a reference that might go stale.
     gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
                                    LazyReferenceContextGetter& aRefContextGetter);
 
     // helper method for resolving generic font families
     static void
     ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
                             nsIAtom *aLanguage,
                             nsTArray<nsString>& aGenericFamilies);
 
 protected:
+    class FamilyFace {
+    public:
+        FamilyFace() : mFamily(nullptr), mFontEntry(nullptr),
+                       mNeedsBold(false), mFontCreated(false),
+                       mLoading(false), mInvalid(false)
+        { }
+
+        FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
+            : mFamily(aFamily), mNeedsBold(false), mFontCreated(true),
+              mLoading(false), mInvalid(false)
+        {
+            NS_ASSERTION(aFont, "font pointer must not be null");
+            NS_ASSERTION(!aFamily ||
+                         aFamily->ContainsFace(aFont->GetFontEntry()),
+                         "font is not a member of the given family");
+            mFont = aFont;
+            NS_ADDREF(aFont);
+        }
+
+        FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
+                   bool aNeedsBold)
+            : mFamily(aFamily), mNeedsBold(aNeedsBold), mFontCreated(false),
+              mLoading(false), mInvalid(false)
+        {
+            NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
+            NS_ASSERTION(!aFamily ||
+                         aFamily->ContainsFace(aFontEntry),
+                         "font is not a member of the given family");
+            mFontEntry = aFontEntry;
+            NS_ADDREF(aFontEntry);
+        }
+
+        FamilyFace(const FamilyFace& aOtherFamilyFace)
+            : mFamily(aOtherFamilyFace.mFamily),
+              mNeedsBold(aOtherFamilyFace.mNeedsBold),
+              mFontCreated(aOtherFamilyFace.mFontCreated),
+              mLoading(aOtherFamilyFace.mLoading),
+              mInvalid(aOtherFamilyFace.mInvalid)
+        {
+            if (mFontCreated) {
+                mFont = aOtherFamilyFace.mFont;
+                NS_ADDREF(mFont);
+            } else {
+                mFontEntry = aOtherFamilyFace.mFontEntry;
+                NS_IF_ADDREF(mFontEntry);
+            }
+        }
+
+        ~FamilyFace()
+        {
+            if (mFontCreated) {
+                NS_RELEASE(mFont);
+            } else {
+                NS_IF_RELEASE(mFontEntry);
+            }
+        }
+
+        FamilyFace& operator=(const FamilyFace& aOther)
+        {
+            if (mFontCreated) {
+                NS_RELEASE(mFont);
+            } else {
+                NS_IF_RELEASE(mFontEntry);
+            }
+
+            mFamily = aOther.mFamily;
+            mNeedsBold = aOther.mNeedsBold;
+            mFontCreated = aOther.mFontCreated;
+            mLoading = aOther.mLoading;
+            mInvalid = aOther.mInvalid;
+
+            if (mFontCreated) {
+                mFont = aOther.mFont;
+                NS_ADDREF(mFont);
+            } else {
+                mFontEntry = aOther.mFontEntry;
+                NS_IF_ADDREF(mFontEntry);
+            }
+
+            return *this;
+        }
+
+        gfxFontFamily* Family() const { return mFamily.get(); }
+        gfxFont* Font() const {
+            return mFontCreated ? mFont : nullptr;
+        }
+
+        gfxFontEntry* FontEntry() const {
+            return mFontCreated ? mFont->GetFontEntry() : mFontEntry;
+        }
+
+        bool NeedsBold() const { return mNeedsBold; }
+        bool IsUserFont() const {
+            return FontEntry()->mIsUserFontContainer;
+        }
+        bool IsLoading() const { return mLoading; }
+        bool IsInvalid() const { return mInvalid; }
+        void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
+        void SetInvalid() { mInvalid = true; }
+
+        void SetFont(gfxFont* aFont)
+        {
+            NS_ASSERTION(aFont, "font pointer must not be null");
+            NS_ADDREF(aFont);
+            if (mFontCreated) {
+                NS_RELEASE(mFont);
+            } else {
+                NS_IF_RELEASE(mFontEntry);
+            }
+            mFont = aFont;
+            mFontCreated = true;
+        }
+
+    private:
+        nsRefPtr<gfxFontFamily> mFamily;
+        // either a font or a font entry exists
+        union {
+            gfxFont*            mFont;
+            gfxFontEntry*       mFontEntry;
+        };
+        bool                    mNeedsBold   : 1;
+        bool                    mFontCreated : 1;
+        bool                    mLoading     : 1;
+        bool                    mInvalid     : 1;
+    };
+
+    // List of font families, either named or generic.
+    // Generic names map to system pref fonts based on language.
     mozilla::FontFamilyList mFamilyList;
+
+    // Fontlist containing a font entry for each family found. gfxFont objects
+    // are created as needed and userfont loads are initiated when needed.
+    // Code should be careful about addressing this array directly.
+    nsTArray<FamilyFace> mFonts;
+
+    nsRefPtr<gfxFont> mDefaultFont;
     gfxFontStyle mStyle;
-    nsTArray<FamilyFace> mFonts;
+
     gfxFloat mUnderlineOffset;
     gfxFloat mHyphenWidth;
 
     nsRefPtr<gfxUserFontSet> mUserFontSet;
     uint64_t mCurrGeneration;  // track the current user font set generation, rebuild font list if needed
 
     gfxTextPerfMetrics *mTextPerf;
 
@@ -946,16 +1046,24 @@ protected:
     gfxTextRun *MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags);
     gfxTextRun *MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags);
     gfxTextRun *MakeBlankTextRun(uint32_t aLength,
                                  const Parameters *aParams, uint32_t aFlags);
 
     // Initialize the list of fonts
     void BuildFontList();
 
+    // Get the font at index i within the fontlist.
+    // Will initiate userfont load if not already loaded.
+    // May return null if userfont not loaded or if font invalid
+    virtual gfxFont* GetFontAt(int32_t i);
+
+    // will always return a font or force a shutdown
+    gfxFont* GetDefaultFont();
+
     // Init this font group's font metrics. If there no bad fonts, you don't need to call this.
     // But if there are one or more bad fonts which have bad underline offset,
     // you should call this with the *first* bad font.
     void InitMetricsForBadFont(gfxFont* aBadFont);
 
     // Set up the textrun glyphs for an entire text run:
     // find script runs, and then call InitScriptRun for each
     template<typename T>
@@ -970,20 +1078,21 @@ protected:
     void InitScriptRun(gfxContext *aContext,
                        gfxTextRun *aTextRun,
                        const T *aString,
                        uint32_t aScriptRunStart,
                        uint32_t aScriptRunEnd,
                        int32_t aRunScript);
 
     // Helper for font-matching:
-    // see if aCh is supported in any of the faces from aFamily;
-    // if so return the best style match, else return null.
-    already_AddRefed<gfxFont> TryAllFamilyMembers(gfxFontFamily* aFamily,
-                                                  uint32_t aCh);
+    // When matching the italic case, allow use of the regular face
+    // if it supports a character but the italic one doesn't.
+    // Return null if regular face doesn't support aCh
+    already_AddRefed<gfxFont>
+    FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
 
     // helper methods for looking up fonts
 
     // iterate over the fontlist, lookup names and expand generics
     void EnumerateFontList(nsIAtom *aLanguage, void *aClosure = nullptr);
 
     // expand a generic to a list of specific names based on prefs
     void FindGenericFonts(mozilla::FontFamilyType aGenericType,
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -115,17 +115,18 @@ gfxUserFontEntry::gfxUserFontEntry(gfxUs
              const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
              uint32_t aWeight,
              int32_t aStretch,
              uint32_t aItalicStyle,
              const nsTArray<gfxFontFeature>& aFeatureSettings,
              uint32_t aLanguageOverride,
              gfxSparseBitSet* aUnicodeRanges)
     : gfxFontEntry(NS_LITERAL_STRING("userfont")),
-      mLoadingState(NOT_LOADING),
+      mUserFontLoadState(STATUS_NOT_LOADED),
+      mFontDataLoadingState(NOT_LOADING),
       mUnsupportedFormat(false),
       mLoader(nullptr),
       mFontSet(aFontSet)
 {
     mIsUserFontContainer = true;
     mSrcList = aFontFaceSrcList;
     mSrcIndex = 0;
     mWeight = aWeight;
@@ -305,26 +306,31 @@ CopyWOFFMetadata(const uint8_t* aFontDat
     }
     if (!aMetadata->SetLength(woff->metaCompLen)) {
         return;
     }
     memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
     *aMetaOrigLen = woff->metaOrigLen;
 }
 
-gfxUserFontEntry::LoadStatus
-gfxUserFontEntry::LoadNext()
+void
+gfxUserFontEntry::LoadNextSrc()
 {
     uint32_t numSrc = mSrcList.Length();
 
     NS_ASSERTION(mSrcIndex < numSrc,
                  "already at the end of the src list for user font");
+    NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
+                  mUserFontLoadState == STATUS_LOADING) &&
+                 mFontDataLoadingState < LOADING_FAILED,
+                 "attempting to load a font that has either completed or failed");
 
-    if (mLoadingState == NOT_LOADING) {
-        mLoadingState = LOADING_STARTED;
+    if (mUserFontLoadState == STATUS_NOT_LOADED) {
+        SetLoadState(STATUS_LOADING);
+        mFontDataLoadingState = LOADING_STARTED;
         mUnsupportedFormat = false;
     } else {
         // we were already loading; move to the next source,
         // but don't reset state - if we've already timed out,
         // that counts against the new download
         mSrcIndex++;
     }
 
@@ -351,17 +357,18 @@ gfxUserFontEntry::LoadNext()
                 fe->mFeatureSettings.AppendElements(mFeatureSettings);
                 fe->mLanguageOverride = mLanguageOverride;
                 fe->mFamilyName = mFamilyName;
                 // For src:local(), we don't care whether the request is from
                 // a private window as there's no issue of caching resources;
                 // local fonts are just available all the time.
                 StoreUserFontData(fe, false, nsString(), nullptr, 0);
                 mPlatformFontEntry = fe;
-                return STATUS_LOADED;
+                SetLoadState(STATUS_LOADED);
+                return;
             } else {
                 LOG(("fontset (%p) [src %d] failed local: (%s) for (%s)\n",
                      mFontSet, mSrcIndex,
                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
                      NS_ConvertUTF16toUTF8(mFamilyName).get()));
             }
         }
 
@@ -380,17 +387,18 @@ gfxUserFontEntry::LoadNext()
                         // see if we have an existing entry for this source
                         gfxFontEntry* fe = gfxUserFontSet::
                             UserFontCache::GetFont(currSrc.mURI,
                                                    principal,
                                                    this,
                                                    mFontSet->GetPrivateBrowsing());
                         if (fe) {
                             mPlatformFontEntry = fe;
-                            return STATUS_LOADED;
+                            SetLoadState(STATUS_LOADED);
+                            return;
                         }
                     }
 
                     // record the principal returned by CheckFontLoad,
                     // for use when creating a channel
                     // and when caching the loaded entry
                     mPrincipal = principal;
 
@@ -403,18 +411,19 @@ gfxUserFontEntry::LoadNext()
                         uint8_t* buffer = nullptr;
                         uint32_t bufferLength = 0;
 
                         // sync load font immediately
                         rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
                                                         bufferLength);
 
                         if (NS_SUCCEEDED(rv) &&
-                            LoadFont(buffer, bufferLength)) {
-                            return STATUS_LOADED;
+                            LoadPlatformFont(buffer, bufferLength)) {
+                            SetLoadState(STATUS_LOADED);
+                            return;
                         } else {
                             mFontSet->LogMessage(this,
                                                  "font load failed",
                                                  nsIScriptError::errorFlag,
                                                  rv);
                         }
 
                     } else {
@@ -427,17 +436,17 @@ gfxUserFontEntry::LoadNext()
                             if (LOG_ENABLED()) {
                                 nsAutoCString fontURI;
                                 currSrc.mURI->GetSpec(fontURI);
                                 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
                                      mFontSet, mSrcIndex, fontURI.get(),
                                      NS_ConvertUTF16toUTF8(mFamilyName).get()));
                             }
 #endif
-                            return STATUS_LOADING;
+                            return;
                         } else {
                             mFontSet->LogMessage(this,
                                                  "download failed",
                                                  nsIScriptError::errorFlag,
                                                  rv);
                         }
                     }
                 } else {
@@ -457,24 +466,34 @@ gfxUserFontEntry::LoadNext()
     if (mUnsupportedFormat) {
         mFontSet->LogMessage(this, "no supported format found",
                              nsIScriptError::warningFlag);
     }
 
     // all src's failed; mark this entry as unusable (so fallback will occur)
     LOG(("userfonts (%p) failed all src for (%s)\n",
         mFontSet, NS_ConvertUTF16toUTF8(mFamilyName).get()));
-    mLoadingState = LOADING_FAILED;
+    mFontDataLoadingState = LOADING_FAILED;
+    SetLoadState(STATUS_FAILED);
+}
 
-    return STATUS_END_OF_LIST;
+void
+gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState)
+{
+    mUserFontLoadState = aLoadState;
 }
 
 bool
-gfxUserFontEntry::LoadFont(const uint8_t* aFontData, uint32_t &aLength)
+gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
 {
+    NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
+                  mUserFontLoadState == STATUS_LOADING) &&
+                 mFontDataLoadingState < LOADING_FAILED,
+                 "attempting to load a font that has either completed or failed");
+
     gfxFontEntry* fe = nullptr;
 
     gfxUserFontType fontType =
         gfxFontUtils::DetermineFontDataType(aFontData, aLength);
 
     // Unwrap/decompress/sanitize or otherwise munge the downloaded data
     // to make a usable sfnt structure.
 
@@ -536,16 +555,17 @@ gfxUserFontEntry::LoadFont(const uint8_t
             mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
                  this, mSrcIndex, fontURI.get(),
                  NS_ConvertUTF16toUTF8(mFamilyName).get(),
                  uint32_t(mFontSet->mGeneration)));
         }
 #endif
         mPlatformFontEntry = fe;
+        SetLoadState(STATUS_LOADED);
         gfxUserFontSet::UserFontCache::CacheFont(fe);
     } else {
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             nsAutoCString fontURI;
             mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
                  " error making platform font\n",
@@ -557,30 +577,39 @@ gfxUserFontEntry::LoadFont(const uint8_t
 
     // The downloaded data can now be discarded; the font entry is using the
     // sanitized copy
     NS_Free((void*)aFontData);
 
     return fe != nullptr;
 }
 
+void
+gfxUserFontEntry::Load()
+{
+    if (mUserFontLoadState == STATUS_NOT_LOADED) {
+        LoadNextSrc();
+    }
+}
+
 // This is called when a font download finishes.
 // Ownership of aFontData passes in here, and the font set must
 // ensure that it is eventually deleted via NS_Free().
 bool
-gfxUserFontEntry::OnLoadComplete(const uint8_t* aFontData, uint32_t aLength,
-                                 nsresult aDownloadStatus)
+gfxUserFontEntry::FontDataDownloadComplete(const uint8_t* aFontData,
+                                           uint32_t aLength,
+                                           nsresult aDownloadStatus)
 {
     // forget about the loader, as we no longer potentially need to cancel it
     // if the entry is obsoleted
     mLoader = nullptr;
 
     // download successful, make platform font using font data
     if (NS_SUCCEEDED(aDownloadStatus)) {
-        bool loaded = LoadFont(aFontData, aLength);
+        bool loaded = LoadPlatformFont(aFontData, aLength);
         aFontData = nullptr;
 
         if (loaded) {
             mFontSet->IncrementGeneration();
             return true;
         }
 
     } else {
@@ -590,30 +619,30 @@ gfxUserFontEntry::OnLoadComplete(const u
                              aDownloadStatus);
     }
 
     if (aFontData) {
         moz_free((void*)aFontData);
     }
 
     // error occurred, load next src
-    LoadNext();
+    LoadNextSrc();
 
     // We ignore the status returned by LoadNext();
     // even if loading failed, we need to bump the font-set generation
     // and return true in order to trigger reflow, so that fallback
     // will be used where the text was "masked" by the pending download
     mFontSet->IncrementGeneration();
     return true;
 }
 
 gfxUserFontSet::gfxUserFontSet()
     : mFontFamilies(4), mLocalRulesUsed(false)
 {
-    IncrementGeneration();
+    IncrementGeneration(true);
     gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
     if (fp) {
         fp->AddUserFontSet(this);
     }
 }
 
 gfxUserFontSet::~gfxUserFontSet()
 {
@@ -739,74 +768,67 @@ gfxUserFontSet::AddFontFace(const nsAStr
              this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry));
     }
 #endif
 }
 
 gfxUserFontEntry*
 gfxUserFontSet::FindUserFontEntry(gfxFontFamily* aFamily,
                                   const gfxFontStyle& aFontStyle,
-                                  bool& aNeedsBold,
-                                  bool& aWaitForUserFont)
+                                  bool& aNeedsBold)
 {
-    aWaitForUserFont = false;
     gfxUserFontFamily* family = static_cast<gfxUserFontFamily*>(aFamily);
-
     gfxFontEntry* fe = family->FindFontForStyle(aFontStyle, aNeedsBold);
 
     NS_ASSERTION(!fe || fe->mIsUserFontContainer,
                  "should only have userfont entries in userfont families");
 
-    // if not a userfont entry, font has already been loaded
     if (!fe || !fe->mIsUserFontContainer) {
         return nullptr;
     }
 
     gfxUserFontEntry* userFontEntry = static_cast<gfxUserFontEntry*> (fe);
+    return userFontEntry;
+}
 
+gfxUserFontEntry*
+gfxUserFontSet::FindUserFontEntryAndLoad(gfxFontFamily* aFamily,
+                                         const gfxFontStyle& aFontStyle,
+                                         bool& aNeedsBold,
+                                         bool& aWaitForUserFont)
+{
+    aWaitForUserFont = false;
+    gfxUserFontEntry* userFontEntry =
+        FindUserFontEntry(aFamily, aFontStyle, aNeedsBold);
+
+    if (!userFontEntry) {
+        return nullptr;
+    }
+
+    // start the load if it hasn't been loaded
+    userFontEntry->Load();
     if (userFontEntry->GetPlatformFontEntry()) {
         return userFontEntry;
     }
 
-    // if currently loading, return null for now
-    if (userFontEntry->mLoadingState > gfxUserFontEntry::NOT_LOADING) {
-        aWaitForUserFont =
-            (userFontEntry->mLoadingState < gfxUserFontEntry::LOADING_SLOWLY);
-        return nullptr;
-    }
-
-    // hasn't been loaded yet, start the load process
-    gfxUserFontEntry::LoadStatus status;
-
-    // NOTE that if all sources in the entry fail, this will delete userFontEntry,
-    // so we cannot use it again if status==STATUS_END_OF_LIST
-    status = userFontEntry->LoadNext();
-
-    // if the load succeeded immediately, return
-    if (status == gfxUserFontEntry::STATUS_LOADED) {
-        return userFontEntry;
-    }
-
-    // check whether we should wait for load to complete before painting
-    // a fallback font -- but not if all sources failed (bug 633500)
-    aWaitForUserFont = (status != gfxUserFontEntry::STATUS_END_OF_LIST) &&
-        (userFontEntry->mLoadingState < gfxUserFontEntry::LOADING_SLOWLY);
-
-    // if either loading or an error occurred, return null
+    aWaitForUserFont = userFontEntry->WaitForUserFont();
     return nullptr;
 }
 
 void
-gfxUserFontSet::IncrementGeneration()
+gfxUserFontSet::IncrementGeneration(bool aIsRebuild)
 {
     // add one, increment again if zero
     ++sFontSetGeneration;
     if (sFontSetGeneration == 0)
        ++sFontSetGeneration;
     mGeneration = sFontSetGeneration;
+    if (aIsRebuild) {
+        mRebuildGeneration = mGeneration;
+    }
 }
 
 void
 gfxUserFontSet::RebuildLocalRules()
 {
     if (mLocalRulesUsed) {
         DoRebuildUserFontSet();
     }
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -184,41 +184,54 @@ public:
     {
         return LookupFamily(aFamilyName) != nullptr;
     }
 
     // Look up and return the gfxUserFontFamily in mFontFamilies with
     // the given name
     gfxUserFontFamily* LookupFamily(const nsAString& aName) const;
 
+    // Lookup a userfont entry for a given style, loaded or not.
+    // aFamily must be a family returned by our LookupFamily method.
+    // If only invalid fonts in family, returns null.
+    gfxUserFontEntry* FindUserFontEntry(gfxFontFamily* aFamily,
+                                        const gfxFontStyle& aFontStyle,
+                                        bool& aNeedsBold);
+
     // Lookup a font entry for a given style, returns null if not loaded.
     // aFamily must be a family returned by our LookupFamily method.
-    gfxUserFontEntry* FindUserFontEntry(gfxFontFamily* aFamily,
-                                        const gfxFontStyle& aFontStyle,
-                                        bool& aNeedsBold,
-                                        bool& aWaitForUserFont);
+    // (only used by gfxPangoFontGroup for now)
+    gfxUserFontEntry* FindUserFontEntryAndLoad(gfxFontFamily* aFamily,
+                                               const gfxFontStyle& aFontStyle,
+                                               bool& aNeedsBold,
+                                               bool& aWaitForUserFont);
 
     // check whether the given source is allowed to be loaded;
     // returns the Principal (for use in the key when caching the loaded font),
     // and whether the load should bypass the cache (force-reload).
     virtual nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
                                    nsIPrincipal** aPrincipal,
                                    bool* aBypassCache) = 0;
 
-    // initialize the process that loads external font data, which upon 
-    // completion will call OnLoadComplete method
+    // initialize the process that loads external font data, which upon
+    // completion will call FontDataDownloadComplete method
     virtual nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
                                const gfxFontFaceSrc* aFontFaceSrc) = 0;
 
     // generation - each time a face is loaded, generation is
-    // incremented so that the change can be recognized 
+    // incremented so that the change can be recognized
     uint64_t GetGeneration() { return mGeneration; }
 
     // increment the generation on font load
-    void IncrementGeneration();
+    void IncrementGeneration(bool aIsRebuild = false);
+
+    // Generation is bumped on font loads but that doesn't affect name-style
+    // mappings. Rebuilds do however affect name-style mappings so need to
+    // lookup fontlists again when that happens.
+    uint64_t GetRebuildGeneration() { return mRebuildGeneration; }
 
     // rebuild if local rules have been used
     void RebuildLocalRules();
 
     class UserFontCache {
     public:
         // Flag passed when caching a font entry, to specify whether the entry
         // should persist in the cache or be discardable.
@@ -443,39 +456,39 @@ protected:
 
     // creates a new gfxUserFontFamily in mFontFamilies, or returns an existing
     // family if there is one
     gfxUserFontFamily* GetFamily(const nsAString& aFamilyName);
 
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxUserFontFamily> mFontFamilies;
 
-    uint64_t        mGeneration;
+    uint64_t        mGeneration;        // bumped on any font load change
+    uint64_t        mRebuildGeneration; // only bumped on rebuilds
 
     // true when local names have been looked up, false otherwise
     bool mLocalRulesUsed;
 
     static PRLogModuleInfo* GetUserFontsLog();
 };
 
 // acts a placeholder until the real font is downloaded
 
 class gfxUserFontEntry : public gfxFontEntry {
     friend class gfxUserFontSet;
     friend class nsUserFontSet;
     friend class nsFontFaceLoader;
     friend class gfxOTSContext;
 
 public:
-    enum LoadStatus {
-        STATUS_LOADING = 0,
+    enum UserFontLoadState {
+        STATUS_NOT_LOADED = 0,
+        STATUS_LOADING,
         STATUS_LOADED,
-        STATUS_FORMAT_NOT_SUPPORTED,
-        STATUS_ERROR,
-        STATUS_END_OF_LIST
+        STATUS_FAILED
     };
 
     gfxUserFontEntry(gfxUserFontSet* aFontSet,
                      const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                      uint32_t aWeight,
                      int32_t aStretch,
                      uint32_t aItalicStyle,
                      const nsTArray<gfxFontFeature>& aFeatureSettings,
@@ -488,62 +501,85 @@ public:
     bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                  uint32_t aWeight,
                  int32_t aStretch,
                  uint32_t aItalicStyle,
                  const nsTArray<gfxFontFeature>& aFeatureSettings,
                  uint32_t aLanguageOverride,
                  gfxSparseBitSet* aUnicodeRanges);
 
-    virtual gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold);
+    virtual gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle,
+                                        bool aNeedsBold);
 
     gfxFontEntry* GetPlatformFontEntry() { return mPlatformFontEntry; }
 
+    // is the font loading or loaded, or did it fail?
+    UserFontLoadState LoadState() const { return mUserFontLoadState; }
+
+    // whether to wait before using fallback font or not
+    bool WaitForUserFont() const {
+        return mUserFontLoadState == STATUS_LOADING &&
+               mFontDataLoadingState < LOADING_SLOWLY;
+    }
+
+    // load the font - starts the loading of sources which continues until
+    // a valid font resource is found or all sources fail
+    void Load();
+
+protected:
+    const uint8_t* SanitizeOpenTypeData(const uint8_t* aData,
+                                        uint32_t aLength,
+                                        uint32_t& aSaneLength,
+                                        bool aIsCompressed);
+
+    // attempt to load the next resource in the src list.
+    void LoadNextSrc();
+
+    // change the load state
+    void SetLoadState(UserFontLoadState aLoadState);
+
     // when download has been completed, pass back data here
     // aDownloadStatus == NS_OK ==> download succeeded, error otherwise
     // returns true if platform font creation sucessful (or local()
     // reference was next in line)
     // Ownership of aFontData is passed in here; the font set must
     // ensure that it is eventually deleted with NS_Free().
-    bool OnLoadComplete(const uint8_t* aFontData, uint32_t aLength,
-                        nsresult aDownloadStatus);
-
-protected:
-    const uint8_t* SanitizeOpenTypeData(const uint8_t* aData,
-                                        uint32_t aLength,
-                                        uint32_t& aSaneLength,
-                                        bool aIsCompressed);
-
-    // Attempt to load the next resource in the src list.
-    LoadStatus LoadNext();
+    bool FontDataDownloadComplete(const uint8_t* aFontData, uint32_t aLength,
+                                  nsresult aDownloadStatus);
 
     // helper method for creating a platform font
     // returns true if platform font creation successful
     // Ownership of aFontData is passed in here; the font must
     // ensure that it is eventually deleted with NS_Free().
-    bool LoadFont(const uint8_t* aFontData, uint32_t &aLength);
+    bool LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength);
 
     // store metadata and src details for current src into aFontEntry
     void StoreUserFontData(gfxFontEntry*      aFontEntry,
                            bool               aPrivate,
                            const nsAString&   aOriginalName,
                            FallibleTArray<uint8_t>* aMetadata,
                            uint32_t           aMetaOrigLen);
 
+    // general load state
+    UserFontLoadState        mUserFontLoadState;
+
+    // detailed load state while font data is loading
+    // used to determine whether to use fallback font or not
     // note that code depends on the ordering of these values!
-    enum LoadingState {
+    enum FontDataLoadingState {
         NOT_LOADING = 0,     // not started to load any font resources yet
         LOADING_STARTED,     // loading has started; hide fallback font
         LOADING_ALMOST_DONE, // timeout happened but we're nearly done,
                              // so keep hiding fallback font
         LOADING_SLOWLY,      // timeout happened and we're not nearly done,
                              // so use the fallback font
         LOADING_FAILED       // failed to load any source: use fallback
     };
-    LoadingState             mLoadingState;
+    FontDataLoadingState     mFontDataLoadingState;
+
     bool                     mUnsupportedFormat;
 
     nsRefPtr<gfxFontEntry>   mPlatformFontEntry;
     nsTArray<gfxFontFaceSrc> mSrcList;
     uint32_t                 mSrcIndex; // index of loading src item
     nsFontFaceLoader*        mLoader; // current loader for this entry, if any
     gfxUserFontSet*          mFontSet; // font-set to which the userfont entry belongs
     nsCOMPtr<nsIPrincipal>   mPrincipal;
--- a/gfx/thebes/gfxWindowsSurface.cpp
+++ b/gfx/thebes/gfxWindowsSurface.cpp
@@ -278,27 +278,16 @@ gfxWindowsSurface::EndPage()
     if (result <= 0)
         return NS_ERROR_FAILURE;
     return NS_OK;
 #else
     return NS_ERROR_FAILURE;
 #endif
 }
 
-int32_t
-gfxWindowsSurface::GetDefaultContextFlags() const
-{
-    if (mForPrinting)
-        return gfxContext::FLAG_SIMPLIFY_OPERATORS |
-               gfxContext::FLAG_DISABLE_SNAPPING |
-               gfxContext::FLAG_DISABLE_COPY_BACKGROUND;
-
-    return 0;
-}
-
 const gfxIntSize 
 gfxWindowsSurface::GetSize() const
 {
     if (!mSurfaceValid) {
         NS_WARNING ("GetImageSurface on an invalid (null) surface; who's calling this without checking for surface errors?");
         return gfxIntSize(-1, -1);
     }
 
--- a/gfx/thebes/gfxWindowsSurface.h
+++ b/gfx/thebes/gfxWindowsSurface.h
@@ -58,18 +58,16 @@ public:
     already_AddRefed<gfxImageSurface> GetAsImageSurface();
 
     nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
     nsresult EndPrinting();
     nsresult AbortPrinting();
     nsresult BeginPage();
     nsresult EndPage();
 
-    virtual int32_t GetDefaultContextFlags() const;
-
     const gfxIntSize GetSize() const;
 
     // The memory used by this surface lives in this process's address space,
     // but not in the heap.
     virtual gfxMemoryLocation GetMemoryLocation() const;
 
 private:
     void MakeInvalid(gfxIntSize& size);
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -607,18 +607,20 @@ RasterImage::GetFrame(uint32_t aFrameNum
   if (!frame) {
     return DrawableFrameRef();
   }
 
   DrawableFrameRef ref = frame->DrawableRef();
   if (!ref) {
     // The OS threw this frame away. We need to discard and redecode.
     MOZ_ASSERT(!mAnim, "Animated frames should be locked");
-    ForceDiscard();
-    WantDecodedFrames();
+    if (CanForciblyDiscardAndRedecode()) {
+      ForceDiscard();
+      WantDecodedFrames();
+    }
     return DrawableFrameRef();
   }
 
   // We will return a paletted frame if it's not marked as compositing failed
   // so we can catch crashes for reasons we haven't investigated.
   if (ref->GetCompositingFailed()) {
     return DrawableFrameRef();
   }
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1703,17 +1703,17 @@ ObjectAddress(JSContext *cx, unsigned ar
     }
     if (!args[0].isObject()) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Expected object");
         return false;
     }
 
 #ifdef JS_MORE_DETERMINISTIC
-    args.rval().setInt(0);
+    args.rval().setInt32(0);
 #else
     char buffer[64];
     JS_snprintf(buffer, sizeof(buffer), "%p", &args[0].toObject());
 
     JSString *str = JS_NewStringCopyZ(cx, buffer);
     if (!str)
         return false;
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1634,19 +1634,19 @@ GetCompartmentName(JSCompartment *c, nsC
         if (compartmentPrivate) {
             const nsACString& location = compartmentPrivate->GetLocation();
             if (!location.IsEmpty() && !location.Equals(name)) {
                 name.AppendLiteral(", ");
                 name.Append(location);
             }
         }
 
-        // We might have a file:// URL that includes paths from the local
-        // filesystem, which should be omitted if we're anonymizing.
         if (*anonymizeID) {
+            // We might have a file:// URL that includes a path from the local
+            // filesystem, which should be omitted if we're anonymizing.
             static const char *filePrefix = "file://";
             int filePos = name.Find(filePrefix);
             if (filePos >= 0) {
                 int pathPos = filePos + strlen(filePrefix);
                 int lastSlashPos = -1;
                 for (int i = pathPos; i < int(name.Length()); i++) {
                     if (name[i] == '/' || name[i] == '\\') {
                         lastSlashPos = i;
@@ -1657,16 +1657,34 @@ GetCompartmentName(JSCompartment *c, nsC
                                       "<anonymized>");
                 } else {
                     // Something went wrong. Anonymize the entire path to be
                     // safe.
                     name.Truncate(pathPos);
                     name += "<anonymized?!>";
                 }
             }
+
+            // We might have a location like this:
+            //   inProcessTabChildGlobal?ownedBy=http://www.example.com/
+            // The owner should be omitted if it's not a chrome: URI and we're
+            // anonymizing.
+            static const char *ownedByPrefix =
+                "inProcessTabChildGlobal?ownedBy=";
+            int ownedByPos = name.Find(ownedByPrefix);
+            if (ownedByPos >= 0) {
+                const char *chrome = "chrome:";
+                int ownerPos = ownedByPos + strlen(ownedByPrefix);
+                const nsDependentCSubstring& ownerFirstPart =
+                    Substring(name, ownerPos, strlen(chrome));
+                if (!ownerFirstPart.EqualsASCII(chrome)) {
+                    name.Truncate(ownerPos);
+                    name += "<anonymized>";
+                }
+            }
         }
 
         // A hack: replace forward slashes with '\\' so they aren't
         // treated as path separators.  Users of the reporters
         // (such as about:memory) have to undo this change.
         if (replaceSlashes)
             name.ReplaceChar('/', '\\');
     } else {
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1696,19 +1696,24 @@ XrayToString(JSContext *cx, unsigned arg
         wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
     }
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "XrayToString called on an incompatible object");
         return false;
     }
 
     RootedObject obj(cx, XrayTraits::getTargetObject(wrapper));
+    XrayType type = GetXrayType(obj);
+    if (type == XrayForDOMObject)
+        return NativeToString(cx, wrapper, obj, args.rval());
 
-    if (UseDOMXray(obj))
-        return NativeToString(cx, wrapper, obj, args.rval());
+    if (type != XrayForWrappedNative) {
+        JS_ReportError(cx, "XrayToString called on an incompatible object");
+        return false;
+    }
 
     static const char start[] = "[object XrayWrapper ";
     static const char end[] = "]";
     nsAutoString result;
     result.AppendASCII(start);
 
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
@@ -2299,22 +2304,23 @@ template class PermissiveXrayOpaque;
 
 template<>
 const SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0);
 template class SCSecurityXrayXPCWN;
 
 static nsQueryInterface
 do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper)
 {
-    nsISupports* nativeSupports;
+    nsISupports* nativeSupports = nullptr;
     if (IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper)) {
         RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
-        if (GetXrayType(target) == XrayForDOMObject) {
+        XrayType type = GetXrayType(target);
+        if (type == XrayForDOMObject) {
             nativeSupports = UnwrapDOMObjectToISupports(target);
-        } else {
+        } else if (type == XrayForWrappedNative) {
             XPCWrappedNative *wn = XPCWrappedNative::Get(target);
             nativeSupports = wn->Native();
         }
     } else {
         nsIXPConnect *xpc = nsXPConnect::XPConnect();
         nativeSupports = xpc->GetNativeOfWrapper(cx, wrapper);
     }
 
new file mode 100644
--- /dev/null
+++ b/layout/base/RestyleLogging.h
@@ -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/. */
+
+/**
+ * Macros used to log restyle events.
+ */
+
+#ifndef mozilla_RestyleLogging_h
+#define mozilla_RestyleLogging_h
+
+#include "mozilla/AutoRestore.h"
+
+#ifdef DEBUG
+#define RESTYLE_LOGGING
+#endif
+
+#ifdef RESTYLE_LOGGING
+#define LOG_RESTYLE_IF(object_, cond_, message_, ...)                         \
+  PR_BEGIN_MACRO                                                              \
+    if (object_->ShouldLogRestyle() && (cond_)) {                             \
+      nsCString line;                                                         \
+      for (int32_t restyle_depth_##__LINE__ = 0;                              \
+           restyle_depth_##__LINE__ < object_->LoggingDepth();                \
+           restyle_depth_##__LINE__++) {                                      \
+        line.AppendLiteral("  ");                                             \
+      }                                                                       \
+      line.AppendPrintf(message_, ##__VA_ARGS__);                             \
+      printf_stderr("%s\n", line.get());                                      \
+    }                                                                         \
+  PR_END_MACRO
+#define LOG_RESTYLE(message_, ...)                                            \
+  LOG_RESTYLE_IF(this, true, message_, ##__VA_ARGS__)
+// Beware that LOG_RESTYLE_INDENT is two statements not wrapped in a block.
+#define LOG_RESTYLE_INDENT()                                                  \
+  AutoRestore<int32_t> ar_depth_##__LINE__(LoggingDepth());                   \
+  ++LoggingDepth();
+#else
+#define LOG_RESTYLE_IF(cond_, message_, ...) /* nothing */
+#define LOG_RESTYLE(message_, ...) /* nothing */
+#define LOG_RESTYLE_INDENT() /* nothing */
+#endif
+
+#endif /* mozilla_RestyleLogging_h */
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -31,40 +31,57 @@
 #include "SVGTextFrame.h"
 #include "StickyScrollContainer.h"
 #include "nsIRootBox.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsContentUtils.h"
 #include "nsIFrameInlines.h"
 #include "ActiveLayerTracker.h"
 #include "nsDisplayList.h"
+#include "RestyleTrackerInlines.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 namespace mozilla {
 
 using namespace layers;
 
+#define LOG_RESTYLE_CONTINUE(reason_, ...) \
+  LOG_RESTYLE("continuing restyle since " reason_, ##__VA_ARGS__)
+
+#ifdef RESTYLE_LOGGING
+static nsCString
+FrameTagToString(const nsIFrame* aFrame)
+{
+  nsCString result;
+  aFrame->ListTag(result);
+  return result;
+}
+#endif
+
 RestyleManager::RestyleManager(nsPresContext* aPresContext)
   : mPresContext(aPresContext)
   , mRebuildAllStyleData(false)
   , mObservingRefreshDriver(false)
   , mInStyleRefresh(false)
   , mHoverGeneration(0)
   , mRebuildAllExtraHint(nsChangeHint(0))
   , mLastUpdateForThrottledAnimations(aPresContext->RefreshDriver()->
                                         MostRecentRefresh())
   , mAnimationGeneration(0)
   , mReframingStyleContexts(nullptr)
   , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE |
                      ELEMENT_IS_POTENTIAL_RESTYLE_ROOT)
   , mPendingAnimationRestyles(ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
                               ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT)
+#ifdef RESTYLE_LOGGING
+  , mLoggingDepth(0)
+#endif
 {
   mPendingRestyles.Init(this);
   mPendingAnimationRestyles.Init(this);
 }
 
 void
 RestyleManager::NotifyDestroyingFrame(nsIFrame* aFrame)
 {
@@ -2252,16 +2269,19 @@ ElementRestyler::ElementRestyler(nsPresC
   , mTreeMatchContext(aTreeMatchContext)
   , mResolvedChild(nullptr)
 #ifdef ACCESSIBILITY
   , mDesiredA11yNotifications(eSendAllNotifications)
   , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
   , mOurA11yNotification(eDontNotify)
   , mVisibleKidsOfHiddenElement(aVisibleKidsOfHiddenElement)
 #endif
+#ifdef RESTYLE_LOGGING
+  , mLoggingDepth(aRestyleTracker.LoggingDepth() + 1)
+#endif
 {
 }
 
 ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler,
                                  nsIFrame* aFrame,
                                  uint32_t aConstructorFlags)
   : mPresContext(aParentRestyler.mPresContext)
   , mFrame(aFrame)
@@ -2279,16 +2299,19 @@ ElementRestyler::ElementRestyler(const E
   , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
   , mResolvedChild(nullptr)
 #ifdef ACCESSIBILITY
   , mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications)
   , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
   , mOurA11yNotification(eDontNotify)
   , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
 #endif
+#ifdef RESTYLE_LOGGING
+  , mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
+#endif
 {
   if (aConstructorFlags & FOR_OUT_OF_FLOW_CHILD) {
     // Note that the out-of-flow may not be a geometric descendant of
     // the frame where we started the reresolve.  Therefore, even if
     // mHintsHandled already includes nsChangeHint_AllReflowHints we
     // don't want to pass that on to the out-of-flow reresolve, since
     // that can lead to the out-of-flow not getting reflowed when it
     // should be (eg a reresolve starting at <body> that involves
@@ -2320,16 +2343,19 @@ ElementRestyler::ElementRestyler(ParentC
   , mTreeMatchContext(aParentRestyler.mTreeMatchContext)
   , mResolvedChild(nullptr)
 #ifdef ACCESSIBILITY
   , mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications)
   , mKidsDesiredA11yNotifications(mDesiredA11yNotifications)
   , mOurA11yNotification(eDontNotify)
   , mVisibleKidsOfHiddenElement(aParentRestyler.mVisibleKidsOfHiddenElement)
 #endif
+#ifdef RESTYLE_LOGGING
+  , mLoggingDepth(aParentRestyler.mLoggingDepth + 1)
+#endif
 {
 }
 
 void
 ElementRestyler::CaptureChange(nsStyleContext* aOldContext,
                                nsStyleContext* aNewContext,
                                nsChangeHint aChangeToAssume,
                                uint32_t* aEqualStructs)
@@ -2346,32 +2372,43 @@ ElementRestyler::CaptureChange(nsStyleCo
   nsChangeHint ourChange =
     aOldContext->CalcStyleDifference(aNewContext,
                                      mParentFrameHintsNotHandledForDescendants,
                                      aEqualStructs);
   NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) ||
                (ourChange & nsChangeHint_NeedReflow),
                "Reflow hint bits set without actually asking for a reflow");
 
+  LOG_RESTYLE("CaptureChange, ourChange = %s, aChangeToAssume = %s",
+              RestyleManager::ChangeHintToString(ourChange).get(),
+              RestyleManager::ChangeHintToString(aChangeToAssume).get());
+  LOG_RESTYLE_INDENT();
+
   // nsChangeHint_UpdateEffects is inherited, but it can be set due to changes
   // in inherited properties (fill and stroke).  Avoid propagating it into
   // text nodes.
   if ((ourChange & nsChangeHint_UpdateEffects) &&
       mContent && !mContent->IsElement()) {
     ourChange = NS_SubtractHint(ourChange, nsChangeHint_UpdateEffects);
   }
 
   NS_UpdateHint(ourChange, aChangeToAssume);
   if (NS_UpdateHint(mHintsHandled, ourChange)) {
     if (!(ourChange & nsChangeHint_ReconstructFrame) || mContent) {
+      LOG_RESTYLE("appending change %s",
+                  RestyleManager::ChangeHintToString(ourChange).get());
       mChangeList->AppendChange(mFrame, mContent, ourChange);
+    } else {
+      LOG_RESTYLE("change has already been handled");
     }
   }
   NS_UpdateHint(mHintsNotHandledForDescendants,
                 NS_HintsNotHandledForDescendantsIn(ourChange));
+  LOG_RESTYLE("mHintsNotHandledForDescendants = %s",
+              RestyleManager::ChangeHintToString(mHintsNotHandledForDescendants).get());
 }
 
 /**
  * Recompute style for mFrame (which should not have a prev continuation
  * with the same style), all of its next continuations with the same
  * style, and all ib-split siblings of the same type (either block or
  * inline, skipping the intermediates of the other type) and accumulate
  * changes into mChangeList given that mHintsHandled is already accumulated
@@ -2444,24 +2481,22 @@ ElementRestyler::Restyle(nsRestyleHint a
   // continuation or block-in-inline sibling.
 
   // We must make a single decision on how to process this frame and
   // its descendants, yet RestyleSelf might return different RestyleResult
   // values for the different same-style continuations.  |result| is our
   // overall decision.
   RestyleResult result = RestyleResult(0);
   uint32_t swappedStructs = 0;
-  nsIFrame* providerFrame = nullptr;
 
   nsRestyleHint thisRestyleHint = aRestyleHint;
 
   bool haveMoreContinuations = false;
   for (nsIFrame* f = mFrame; f; ) {
-    RestyleResult thisResult =
-      RestyleSelf(f, thisRestyleHint, &swappedStructs, &providerFrame);
+    RestyleResult thisResult = RestyleSelf(f, thisRestyleHint, &swappedStructs);
 
     if (thisResult != eRestyleResult_Stop) {
       // Calls to RestyleSelf for later same-style continuations must not
       // return eRestyleResult_Stop, so pass eRestyle_Force in to them.
       thisRestyleHint = nsRestyleHint(thisRestyleHint | eRestyle_Force);
 
       if (result == eRestyleResult_Stop) {
         // We received eRestyleResult_Stop for earlier same-style
@@ -2490,25 +2525,25 @@ ElementRestyler::Restyle(nsRestyleHint a
     mRestyleTracker.AddPendingRestyleToTable(mContent->AsElement(),
                                              hintToRestore, nsChangeHint(0));
   }
 
   if (result == eRestyleResult_Stop) {
     MOZ_ASSERT(mFrame->StyleContext() == oldContext,
                "frame should have been left with its old style context");
 
-    MOZ_ASSERT(providerFrame, "RestyleSelf should have supplied a providerFrame "
-                              "if it returned eRestyleResult_Stop");
-
-    nsStyleContext* newParent = providerFrame->StyleContext();
+    nsStyleContext* newParent =
+      mFrame->GetParentStyleContextFrame()->StyleContext();
 
     if (oldContext->GetParent() != newParent) {
       // If we received eRestyleResult_Stop, then the old style context was
       // left on mFrame.  Since we ended up restyling our parent, change
       // this old style context to point to its new parent.
+      LOG_RESTYLE("moving style context %p from old parent %p to new parent %p",
+                  oldContext.get(), oldContext->GetParent(), newParent);
       oldContext->MoveTo(newParent);
     }
 
     // Send the accessibility notifications that RestyleChildren otherwise
     // would have sent.
     if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
       InitializeAccessibilityNotifications();
       SendAccessibilityNotifications();
@@ -2563,130 +2598,157 @@ ElementRestyler::Restyle(nsRestyleHint a
 ElementRestyler::RestyleResult
 ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf)
 {
   // We can't handle situations where the primary style context of a frame
   // has not had any style data changes, but its additional style contexts
   // have, so we don't considering stopping if this frame has any additional
   // style contexts.
   if (aSelf->GetAdditionalStyleContext(0)) {
+    LOG_RESTYLE_CONTINUE("there are additional style contexts");
     return eRestyleResult_Continue;
   }
 
   // Style changes might have moved children between the two nsLetterFrames
   // (the one matching ::first-letter and the one containing the rest of the
   // content).  Continue restyling to the children of the nsLetterFrame so
   // that they get the correct style context parent.  Similarly for
   // nsLineFrames.
   nsIAtom* type = aSelf->GetType();
-  if (type == nsGkAtoms::letterFrame || type == nsGkAtoms::lineFrame) {
+
+  if (type == nsGkAtoms::letterFrame) {
+    LOG_RESTYLE_CONTINUE("frame is a letter frame");
+    return eRestyleResult_Continue;
+  }
+
+  if (type == nsGkAtoms::lineFrame) {
+    LOG_RESTYLE_CONTINUE("frame is a line frame");
     return eRestyleResult_Continue;
   }
 
   // Some style computations depend not on the parent's style, but a grandparent
   // or one the grandparent's ancestors.  An example is an explicit 'inherit'
   // value for align-self, where if the parent frame's value for the property is
   // 'auto' we end up inheriting the computed value from the grandparent.  We
   // can't stop the restyling process on this frame (the one with 'auto', in
   // this example), as the grandparent's computed value might have changed
   // and we need to recompute the child's 'inherit' to that new value.
   nsStyleContext* oldContext = aSelf->StyleContext();
   if (oldContext->HasChildThatUsesGrandancestorStyle()) {
+    LOG_RESTYLE_CONTINUE("the old context uses grandancestor style");
     return eRestyleResult_Continue;
   }
 
   // We ignore all situations that involve :visited style.
   if (oldContext->GetStyleIfVisited()) {
+    LOG_RESTYLE_CONTINUE("the old style context has StyleIfVisited");
     return eRestyleResult_Continue;
   }
 
   nsStyleContext* parentContext = oldContext->GetParent();
   if (parentContext && parentContext->GetStyleIfVisited()) {
+    LOG_RESTYLE_CONTINUE("the old style context's parent has StyleIfVisited");
     return eRestyleResult_Continue;
   }
 
   // We also ignore frames for pseudos, as their style contexts have
   // inheritance structures that do not match the frame inheritance
   // structure.  To avoid enumerating and checking all of the cases
   // where we have this kind of inheritance, we keep restyling past
   // pseudos.
   nsIAtom* pseudoTag = oldContext->GetPseudo();
   if (pseudoTag && pseudoTag != nsCSSAnonBoxes::mozNonElement) {
+    LOG_RESTYLE_CONTINUE("the old style context is for a pseudo");
     return eRestyleResult_Continue;
   }
 
   nsIFrame* parent = mFrame->GetParent();
 
   if (parent) {
     // Also if the parent has a pseudo, as this frame's style context will
     // be inheriting from a grandparent frame's style context (or a further
     // ancestor).
     nsIAtom* parentPseudoTag = parent->StyleContext()->GetPseudo();
     if (parentPseudoTag && parentPseudoTag != nsCSSAnonBoxes::mozNonElement) {
+      LOG_RESTYLE_CONTINUE("the old style context's parent is for a pseudo");
       return eRestyleResult_Continue;
     }
   }
 
   return eRestyleResult_Stop;
 }
 
 ElementRestyler::RestyleResult
 ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf,
                                                     nsStyleContext* aNewContext)
 {
   // Keep restyling if the new style context has any style-if-visted style, so
   // that we can avoid the style context tree surgery having to deal to deal
   // with visited styles.
   if (aNewContext->GetStyleIfVisited()) {
+    LOG_RESTYLE_CONTINUE("the new style context has StyleIfVisited");
     return eRestyleResult_Continue;
   }
 
   // If link-related information has changed, or the pseudo for the frame has
   // changed, or the new style context points to a different rule node, we can't
   // leave the old style context on the frame.
   nsStyleContext* oldContext = aSelf->StyleContext();
   if (oldContext->IsLinkContext() != aNewContext->IsLinkContext() ||
       oldContext->RelevantLinkVisited() != aNewContext->RelevantLinkVisited() ||
       oldContext->GetPseudo() != aNewContext->GetPseudo() ||
       oldContext->GetPseudoType() != aNewContext->GetPseudoType() ||
       oldContext->RuleNode() != aNewContext->RuleNode()) {
+    LOG_RESTYLE_CONTINUE("the old and new style contexts have different link/"
+                         "visited/pseudo/rulenodes");
     return eRestyleResult_Continue;
   }
 
   // If the old and new style contexts differ in their
   // NS_STYLE_HAS_TEXT_DECORATION_LINES or NS_STYLE_HAS_PSEUDO_ELEMENT_DATA
   // bits, then we must keep restyling so that those new bit values are
   // propagated.
   if (oldContext->HasTextDecorationLines() !=
-        aNewContext->HasTextDecorationLines() ||
-      oldContext->HasPseudoElementData() !=
+        aNewContext->HasTextDecorationLines()) {
+    LOG_RESTYLE_CONTINUE("NS_STYLE_HAS_TEXT_DECORATION_LINES differs between old"
+                         " and new style contexts");
+    return eRestyleResult_Continue;
+  }
+
+  if (oldContext->HasPseudoElementData() !=
         aNewContext->HasPseudoElementData()) {
+    LOG_RESTYLE_CONTINUE("NS_STYLE_HAS_PSEUDO_ELEMENT_DATA differs between old"
+                         " and new style contexts");
     return eRestyleResult_Continue;
   }
 
   return eRestyleResult_Stop;
 }
 
 ElementRestyler::RestyleResult
 ElementRestyler::RestyleSelf(nsIFrame* aSelf,
                              nsRestyleHint aRestyleHint,
-                             uint32_t* aSwappedStructs,
-                             nsIFrame** aProviderFrame)
+                             uint32_t* aSwappedStructs)
 {
   MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings),
              "eRestyle_LaterSiblings must not be part of aRestyleHint");
 
   // XXXldb get new context from prev-in-flow if possible, to avoid
   // duplication.  (Or should we just let |GetContext| handle that?)
   // Getting the hint would be nice too, but that's harder.
 
   // XXXbryner we may be able to avoid some of the refcounting goop here.
   // We do need a reference to oldContext for the lifetime of this function, and it's possible
   // that the frame has the last reference to it, so AddRef it here.
 
+  LOG_RESTYLE("RestyleSelf %s, aRestyleHint = %s",
+              FrameTagToString(aSelf).get(),
+              RestyleManager::RestyleHintToString(aRestyleHint).get());
+  LOG_RESTYLE_INDENT();
+
   RestyleResult result;
 
   if (aRestyleHint & eRestyle_ForceDescendants) {
     result = eRestyleResult_ContinueAndForceDescendants;
   } else if (aRestyleHint) {
     result = eRestyleResult_Continue;
   } else {
     result = ComputeRestyleResultFromFrame(aSelf);
@@ -2702,31 +2764,32 @@ ElementRestyler::RestyleSelf(nsIFrame* a
 #endif
 
   nsIAtom* const pseudoTag = oldContext->GetPseudo();
   const nsCSSPseudoElements::Type pseudoType = oldContext->GetPseudoType();
 
   nsStyleContext* parentContext;
   // Get the frame providing the parent style context.  If it is a
   // child, then resolve the provider first.
-  nsIFrame* const providerFrame = aSelf->GetParentStyleContextFrame();
+  nsIFrame* providerFrame = aSelf->GetParentStyleContextFrame();
   bool isChild = providerFrame && providerFrame->GetParent() == aSelf;
   if (!isChild) {
     if (providerFrame)
       parentContext = providerFrame->StyleContext();
     else
       parentContext = nullptr;
   }
   else {
     MOZ_ASSERT(providerFrame->GetContent() == aSelf->GetContent(),
                "Postcondition for GetParentStyleContextFrame() violated. "
                "That means we need to add the current element to the "
                "ancestor filter.");
 
     // resolve the provider here (before aSelf below).
+    LOG_RESTYLE("resolving child provider frame");
 
     // assumeDifferenceHint forces the parent's change to be also
     // applied to this frame, no matter what
     // nsStyleContext::CalcStyleDifference says. CalcStyleDifference
     // can't be trusted because it assumes any changes to the parent
     // style context provider will be automatically propagated to
     // the frame(s) with child style contexts.
 
@@ -2736,80 +2799,79 @@ ElementRestyler::RestyleSelf(nsIFrame* a
     assumeDifferenceHint = providerRestyler.HintsHandledForFrame();
 
     // The provider's new context becomes the parent context of
     // aSelf's context.
     parentContext = providerFrame->StyleContext();
     // Set |mResolvedChild| so we don't bother resolving the
     // provider again.
     mResolvedChild = providerFrame;
+    LOG_RESTYLE_CONTINUE("we had a provider frame");
     // Continue restyling past the odd style context inheritance.
     result = eRestyleResult_Continue;
   }
 
   if (providerFrame != aSelf->GetParent()) {
     // We don't actually know what the parent style context's
     // non-inherited hints were, so assume the worst.
     mParentFrameHintsNotHandledForDescendants =
       nsChangeHint_Hints_NotHandledForDescendants;
   }
 
-  if (*aProviderFrame && *aProviderFrame != providerFrame) {
-    // XXX Uncomment when bug 979133 (restyle logging) lands.
-    // LOG_RESTYLE_CONTINUE("we had different provider frame from the previous "
-    //                      "same-style continuation");
-    result = eRestyleResult_Continue;
-  }
-
   // We don't support using eRestyle_StyleAttribute when pseudo-elements
   // are involved.  This is mostly irrelevant since style attribute
   // changes on pseudo-elements are very rare, though it does mean we
   // don't get the optimization for table elements.
   if (pseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement &&
       (aRestyleHint & eRestyle_StyleAttribute)) {
     aRestyleHint = (aRestyleHint & ~eRestyle_StyleAttribute) | eRestyle_Self;
   }
 
+  LOG_RESTYLE("parentContext = %p", parentContext);
+
   // do primary context
   nsRefPtr<nsStyleContext> newContext;
   nsIFrame *prevContinuation =
     GetPrevContinuationWithPossiblySameStyle(aSelf);
   nsStyleContext *prevContinuationContext;
   bool copyFromContinuation =
     prevContinuation &&
     (prevContinuationContext = prevContinuation->StyleContext())
       ->GetPseudo() == oldContext->GetPseudo() &&
      prevContinuationContext->GetParent() == parentContext;
   if (copyFromContinuation) {
     // Just use the style context from the frame's previous
     // continuation.
+    LOG_RESTYLE("using previous continuation's context");
     newContext = prevContinuationContext;
   }
   else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
     NS_ASSERTION(aSelf->GetContent(),
                  "non pseudo-element frame without content node");
     newContext = styleSet->ResolveStyleForNonElement(parentContext);
   }
   else if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
     Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType);
     if (!(aRestyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants)) &&
         !styleSet->IsInRuleTreeReconstruct()) {
       nsIContent* pseudoElementContent = aSelf->GetContent();
       Element* pseudoElement =
         (pseudoElementContent && pseudoElementContent->IsElement())
           ? pseudoElementContent->AsElement() : nullptr;
+      LOG_RESTYLE("reparenting style context");
       newContext =
         styleSet->ReparentStyleContext(oldContext, parentContext, element,
                                        pseudoElement);
     } else {
       // Use ResolveStyleWithReplacement either for actual replacements
       // or, with no replacements, as a substitute for
       // ReparentStyleContext that rebuilds the path in the rule tree
       // rather than reusing the rule node, as we need to do during a
       // rule tree reconstruct.
+      LOG_RESTYLE("resolving style with replacement");
       newContext =
         styleSet->ResolveStyleWithReplacement(element, parentContext, oldContext,
                                               aRestyleHint);
     }
   } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
     newContext = styleSet->ResolveAnonymousBoxStyle(pseudoTag,
                                                     parentContext);
   }
@@ -2877,29 +2939,38 @@ ElementRestyler::RestyleSelf(nsIFrame* a
         oldContext->IsLinkContext() == newContext->IsLinkContext() &&
         oldContext->RelevantLinkVisited() ==
           newContext->RelevantLinkVisited()) {
       // We're the root of the style context tree and the new style
       // context returned has the same rule node.  This means that
       // we can use FindChildWithRules to keep a lot of the old
       // style contexts around.  However, we need to start from the
       // same root.
+      LOG_RESTYLE("restyling root and keeping old context");
+      LOG_RESTYLE_IF(this, result != eRestyleResult_Continue,
+                     "continuing restyle since this is the root");
       newContext = oldContext;
       // Never consider stopping restyling at the root.
       result = eRestyleResult_Continue;
     }
   }
 
+  LOG_RESTYLE("oldContext = %p, newContext = %p%s",
+              oldContext.get(), newContext.get(),
+              oldContext == newContext ? (const char*) " (same)" :
+                                         (const char*) "");
+
   if (newContext != oldContext) {
     if (result == eRestyleResult_Stop) {
       if (oldContext->IsShared()) {
         // If the old style context was shared, then we can't return
         // eRestyleResult_Stop and patch its parent to point to the
         // new parent style context, as that change might not be valid
         // for the other frames sharing the style context.
+        LOG_RESTYLE_CONTINUE("the old style context is shared");
         result = eRestyleResult_Continue;
       } else {
         // Look at some details of the new style context to see if it would
         // be safe to stop restyling, if we discover it has the same style
         // data as the old style context.
         result = ComputeRestyleResultFromNewContext(aSelf, newContext);
       }
     }
@@ -2913,28 +2984,34 @@ ElementRestyler::RestyleSelf(nsIFrame* a
       // same-style continuations (bug 918064), we need to check again here to
       // determine whether it is safe to stop restyling.
       if (result == eRestyleResult_Stop) {
         oldContext->CalcStyleDifference(newContext, nsChangeHint(0),
                                         &equalStructs);
         if (equalStructs != NS_STYLE_INHERIT_MASK) {
           // At least one struct had different data in it, so we must
           // continue restyling children.
+          LOG_RESTYLE_CONTINUE("there is different style data: %s",
+                      RestyleManager::StructNamesToString(
+                        ~equalStructs & NS_STYLE_INHERIT_MASK).get());
           result = eRestyleResult_Continue;
         }
       }
     } else {
       RestyleManager::TryStartingTransition(mPresContext, aSelf->GetContent(),
                                             oldContext, &newContext);
 
       CaptureChange(oldContext, newContext, assumeDifferenceHint,
                     &equalStructs);
       if (equalStructs != NS_STYLE_INHERIT_MASK) {
         // At least one struct had different data in it, so we must
         // continue restyling children.
+        LOG_RESTYLE_CONTINUE("there is different style data: %s",
+                    RestyleManager::StructNamesToString(
+                      ~equalStructs & NS_STYLE_INHERIT_MASK).get());
         result = eRestyleResult_Continue;
       }
     }
 
     if (result == eRestyleResult_Stop) {
       // Since we currently have eRestyleResult_Stop, we know at this
       // point that all of our style structs are equal in terms of styles.
       // However, some of them might be different pointers.  Since our
@@ -2953,49 +3030,69 @@ ElementRestyler::RestyleSelf(nsIFrame* a
       //
       // (b) when we were unable to swap the structs on the parent because
       //     either or both of the old parent and new parent are shared.
       for (nsStyleStructID sid = nsStyleStructID(0);
            sid < nsStyleStructID_Length;
            sid = nsStyleStructID(sid + 1)) {
         if (oldContext->HasCachedInheritedStyleData(sid) &&
             !oldContext->HasSameCachedStyleData(newContext, sid)) {
+          LOG_RESTYLE_CONTINUE("there are different struct pointers");
           result = eRestyleResult_Continue;
           break;
         }
       }
     }
 
     if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
       // If the frame gets regenerated, let it keep its old context,
       // which is important to maintain various invariants about
       // frame types matching their style contexts.
       // Note that this check even makes sense if we didn't call
       // CaptureChange because of copyFromContinuation being true,
       // since we'll have copied the existing context from the
       // previous continuation, so newContext == oldContext.
 
       if (result != eRestyleResult_Stop) {
-        if (!oldContext->IsShared() && !newContext->IsShared()) {
+        if (copyFromContinuation) {
+          LOG_RESTYLE("not swapping style structs, since we copied from a "
+                      "continuation");
+        } else if (oldContext->IsShared() && newContext->IsShared()) {
+          LOG_RESTYLE("not swapping style structs, since both old and contexts "
+                      "are shared");
+        } else if (oldContext->IsShared()) {
+          LOG_RESTYLE("not swapping style structs, since the old context is "
+                      "shared");
+        } else if (newContext->IsShared()) {
+          LOG_RESTYLE("not swapping style structs, since the new context is "
+                      "shared");
+        } else {
+          LOG_RESTYLE("swapping style structs between %p and %p",
+                      oldContext.get(), newContext.get());
           oldContext->SwapStyleData(newContext, equalStructs);
           *aSwappedStructs |= equalStructs;
         }
+        LOG_RESTYLE("setting new style context");
         aSelf->SetStyleContext(newContext);
       }
+    } else {
+      LOG_RESTYLE("not setting new style context, since we'll reframe");
     }
   }
   oldContext = nullptr;
 
   // do additional contexts
   // XXXbz might be able to avoid selector matching here in some
   // cases; won't worry about it for now.
   int32_t contextIndex = 0;
   for (nsStyleContext* oldExtraContext;
        (oldExtraContext = aSelf->GetAdditionalStyleContext(contextIndex));
        ++contextIndex) {
+    LOG_RESTYLE("extra context %d", contextIndex);
+    LOG_RESTYLE_INDENT();
     nsRefPtr<nsStyleContext> newExtraContext;
     nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudo();
     const nsCSSPseudoElements::Type extraPseudoType =
       oldExtraContext->GetPseudoType();
     NS_ASSERTION(extraPseudoTag &&
                  extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
                  "extra style context is not pseudo element");
     if (!(aRestyleHint & (eRestyle_Self | eRestyle_Subtree))) {
@@ -3031,29 +3128,32 @@ ElementRestyler::RestyleSelf(nsIFrame* a
       newExtraContext = styleSet->ResolvePseudoElementStyle(mContent->AsElement(),
                                                             extraPseudoType,
                                                             newContext,
                                                             nullptr);
     }
 
     MOZ_ASSERT(newExtraContext);
 
+    LOG_RESTYLE("newExtraContext = %p", newExtraContext.get());
+
     if (oldExtraContext != newExtraContext) {
       uint32_t equalStructs;
       CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint,
                     &equalStructs);
       if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) {
+        LOG_RESTYLE("setting new extra style context");
         aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext);
+      } else {
+        LOG_RESTYLE("not setting new extra style context, since we'll reframe");
       }
     }
   }
 
-  if (result == eRestyleResult_Stop) {
-    *aProviderFrame = providerFrame;
-  }
+  LOG_RESTYLE("returning %s", RestyleResultToString(result).get());
 
   return result;
 }
 
 void
 ElementRestyler::RestyleChildren(nsRestyleHint aChildRestyleHint)
 {
   // We'd like style resolution to be exact in the sense that an
@@ -3148,16 +3248,19 @@ ElementRestyler::RestyleUndisplayedChild
       NS_ASSERTION(undisplayedParent ||
                    undisplayed->mContent ==
                      mPresContext->Document()->GetRootElement(),
                    "undisplayed node child of null must be root");
       NS_ASSERTION(!undisplayed->mStyle->GetPseudo(),
                    "Shouldn't have random pseudo style contexts in the "
                    "undisplayed map");
 
+      LOG_RESTYLE("RestyleUndisplayedChildren: undisplayed->mContent = %p",
+                  undisplayed->mContent.get());
+
       // Get the parent of the undisplayed content and check if it is a XBL
       // children element. Push the children element as an ancestor here because it does
       // not have a frame and would not otherwise be pushed as an ancestor.
       nsIContent* parent = undisplayed->mContent->GetParent();
       TreeMatchContext::AutoAncestorPusher insertionPointPusher(mTreeMatchContext);
       if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
         insertionPointPusher.PushAncestorAndStyleScope(parent);
       }
@@ -3229,16 +3332,18 @@ ElementRestyler::MaybeReframeForBeforePs
       // Checking for a :before frame is cheaper than getting the
       // :before style context.
       if (!nsLayoutUtils::GetBeforeFrame(mFrame) &&
           nsLayoutUtils::HasPseudoStyle(mFrame->GetContent(),
                                         mFrame->StyleContext(),
                                         nsCSSPseudoElements::ePseudo_before,
                                         mPresContext)) {
         // Have to create the new :before frame
+        LOG_RESTYLE("MaybeReframeForBeforePseudo, appending "
+                    "nsChangeHint_ReconstructFrame");
         NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
         mChangeList->AppendChange(mFrame, mContent,
                                   nsChangeHint_ReconstructFrame);
       }
     }
   }
 }
 
@@ -3265,16 +3370,18 @@ ElementRestyler::MaybeReframeForAfterPse
       // Getting the :after frame is more expensive than getting the pseudo
       // context, so get the pseudo context first.
       if (nsLayoutUtils::HasPseudoStyle(aFrame->GetContent(),
                                         aFrame->StyleContext(),
                                         nsCSSPseudoElements::ePseudo_after,
                                         mPresContext) &&
           !nsLayoutUtils::GetAfterFrame(aFrame)) {
         // have to create the new :after frame
+        LOG_RESTYLE("MaybeReframeForAfterPseudo, appending "
+                    "nsChangeHint_ReconstructFrame");
         NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame);
         mChangeList->AppendChange(aFrame, mContent,
                                   nsChangeHint_ReconstructFrame);
       }
     }
   }
 }
 
@@ -3317,16 +3424,18 @@ ElementRestyler::InitializeAccessibility
   }
 #endif
 }
 
 void
 ElementRestyler::RestyleContentChildren(nsIFrame* aParent,
                                         nsRestyleHint aChildRestyleHint)
 {
+  LOG_RESTYLE("RestyleContentChildren");
+
   nsIFrame::ChildListIterator lists(aParent);
   TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
   if (!lists.IsDone()) {
     ancestorPusher.PushAncestorAndStyleScope(mContent);
   }
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
@@ -3522,9 +3631,143 @@ RestyleManager::ComputeStyleChangeFor(ns
         NS_ASSERTION(!cont->GetPrevContinuation(),
                      "continuing frame had more severe impact than first-in-flow");
         return;
       }
     }
   }
 }
 
+#ifdef DEBUG
+/* static */ nsCString
+RestyleManager::RestyleHintToString(nsRestyleHint aHint)
+{
+  nsCString result;
+  bool any = false;
+  const char* names[] = { "Self", "Subtree", "LaterSiblings", "CSSTransitions",
+                          "CSSAnimations", "SVGAttrAnimations", "StyleAttribute",
+                          "ChangeAnimationPhase", "Force", "ForceDescendants" };
+  uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
+  uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
+  for (uint32_t i = 0; i < ArrayLength(names); i++) {
+    if (hint & (1 << i)) {
+      if (any) {
+        result.AppendLiteral(" | ");
+      }
+      result.AppendPrintf("eRestyle_%s", names[i]);
+      any = true;
+    }
+  }
+  if (rest) {
+    if (any) {
+      result.AppendLiteral(" | ");
+    }
+    result.AppendPrintf("0x%0x", rest);
+  } else {
+    if (!any) {
+      result.AppendLiteral("0");
+    }
+  }
+  return result;
+}
+
+/* static */ nsCString
+RestyleManager::ChangeHintToString(nsChangeHint aHint)
+{
+  nsCString result;
+  bool any = false;
+  const char* names[] = {
+    "RepaintFrame", "NeedReflow", "ClearAncestorIntrinsics",
+    "ClearDescendantIntrinsics", "NeedDirtyReflow", "SyncFrameView",
+    "UpdateCursor", "UpdateEffects", "UpdateOpacityLayer",
+    "UpdateTransformLayer", "ReconstructFrame", "UpdateOverflow",
+    "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
+    "ChildrenOnlyTransform", "RecomputePosition", "AddOrRemoveTransform",
+    "BorderStyleNoneChange", "UpdateTextPath", "NeutralChange"
+  };
+  uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
+  uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
+  if (hint == nsChangeHint_Hints_NotHandledForDescendants) {
+    result.AppendLiteral("nsChangeHint_Hints_NotHandledForDescendants");
+    hint = 0;
+    any = true;
+  } else {
+    if ((hint & NS_STYLE_HINT_FRAMECHANGE) == NS_STYLE_HINT_FRAMECHANGE) {
+      result.AppendLiteral("NS_STYLE_HINT_FRAMECHANGE");
+      hint = hint & ~NS_STYLE_HINT_FRAMECHANGE;
+      any = true;
+    } else if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
+      result.AppendLiteral("NS_STYLE_HINT_REFLOW");
+      hint = hint & ~NS_STYLE_HINT_REFLOW;
+      any = true;
+    } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
+      result.AppendLiteral("nsChangeHint_AllReflowHints");
+      hint = hint & ~nsChangeHint_AllReflowHints;
+      any = true;
+    } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
+      result.AppendLiteral("NS_STYLE_HINT_VISUAL");
+      hint = hint & ~NS_STYLE_HINT_VISUAL;
+      any = true;
+    }
+  }
+  for (uint32_t i = 0; i < ArrayLength(names); i++) {
+    if (hint & (1 << i)) {
+      if (any) {
+        result.AppendLiteral(" | ");
+      }
+      result.AppendPrintf("nsChangeHint_%s", names[i]);
+      any = true;
+    }
+  }
+  if (rest) {
+    if (any) {
+      result.AppendLiteral(" | ");
+    }
+    result.AppendPrintf("0x%0x", rest);
+  } else {
+    if (!any) {
+      result.AppendLiteral("NS_STYLE_HINT_NONE");
+    }
+  }
+  return result;
+}
+
+/* static */ nsCString
+RestyleManager::StructNamesToString(uint32_t aSIDs)
+{
+  nsCString result;
+  bool any = false;
+  for (nsStyleStructID sid = nsStyleStructID(0);
+       sid < nsStyleStructID_Length;
+       sid = nsStyleStructID(sid + 1)) {
+    if (aSIDs & nsCachedStyleData::GetBitForSID(sid)) {
+      if (any) {
+        result.AppendLiteral(",");
+      }
+      result.AppendPrintf("%s", nsStyleContext::StructName(sid));
+      any = true;
+    }
+  }
+  return result;
+}
+
+/* static */ nsCString
+ElementRestyler::RestyleResultToString(RestyleResult aRestyleResult)
+{
+  nsCString result;
+  switch (aRestyleResult) {
+    case eRestyleResult_Stop:
+      result.AssignLiteral("eRestyleResult_Stop");
+      break;
+    case eRestyleResult_Continue:
+      result.AssignLiteral("eRestyleResult_Continue");
+      break;
+    case eRestyleResult_ContinueAndForceDescendants:
+      result.AssignLiteral("eRestyleResult_ContinueAndForceDescendants");
+      break;
+    default:
+      result.AppendPrintf("RestyleResult(%d)", aRestyleResult);
+  }
+  return result;
+}
+#endif
+
 } // namespace mozilla
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -6,16 +6,17 @@
 /**
  * Code responsible for managing style changes: tracking what style
  * changes need to happen, scheduling them, and doing them.
  */
 
 #ifndef mozilla_RestyleManager_h
 #define mozilla_RestyleManager_h
 
+#include "mozilla/RestyleLogging.h"
 #include "nsISupportsImpl.h"
 #include "nsChangeHint.h"
 #include "RestyleTracker.h"
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
 #include "nsRefPtrHashtable.h"
 #include "nsCSSPseudoElements.h"
 
@@ -311,16 +312,21 @@ public:
     PostRestyleEventInternal(true);
   }
 
   void FlushOverflowChangedTracker()
   {
     mOverflowChangedTracker.Flush();
   }
 
+#ifdef DEBUG
+  static nsCString RestyleHintToString(nsRestyleHint aHint);
+  static nsCString ChangeHintToString(nsChangeHint aHint);
+#endif
+
 private:
   /**
    * Notify the frame constructor that an element needs to have its
    * style recomputed.
    * @param aElement: The element to be restyled.
    * @param aRestyleHint: Which nodes need to have selector matching run
    *                      on them.
    * @param aMinChangeHint: A minimum change hint for aContent and its
@@ -346,16 +352,49 @@ public:
    * This method is used to recompute the style data when some change happens
    * outside of any style rules, like a color preference change or a change
    * in a system font size, or to fix things up when an optimization in the
    * style data has become invalid. We assume that the root frame will not
    * need to be reframed.
    */
   void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint);
 
+#ifdef RESTYLE_LOGGING
+  /**
+   * Returns whether a restyle event currently being processed by this
+   * RestyleManager should be logged.
+   */
+  bool ShouldLogRestyle() {
+    return ShouldLogRestyle(mPresContext);
+  }
+
+  /**
+   * Returns whether a restyle event currently being processed for the
+   * document with the specified nsPresContext should be logged.
+   */
+  static bool ShouldLogRestyle(nsPresContext* aPresContext) {
+    return aPresContext->RestyleLoggingEnabled() &&
+           (!aPresContext->IsProcessingAnimationStyleChange() ||
+            AnimationRestyleLoggingEnabled());
+  }
+
+  static bool RestyleLoggingInitiallyEnabled() {
+    static bool enabled = getenv("MOZ_DEBUG_RESTYLE") != 0;
+    return enabled;
+  }
+
+  static bool AnimationRestyleLoggingEnabled() {
+    static bool animations = getenv("MOZ_DEBUG_RESTYLE_ANIMATIONS") != 0;
+    return animations;
+  }
+
+  static nsCString StructNamesToString(uint32_t aSIDs);
+  int32_t& LoggingDepth() { return mLoggingDepth; }
+#endif
+
 private:
   /* aMinHint is the minimal change that should be made to the element */
   // XXXbz do we really need the aPrimaryFrame argument here?
   void RestyleElement(Element*        aElement,
                       nsIFrame*       aPrimaryFrame,
                       nsChangeHint    aMinHint,
                       RestyleTracker& aRestyleTracker,
                       nsRestyleHint   aRestyleHint);
@@ -388,16 +427,20 @@ private:
   // The total number of animation flushes by this frame constructor.
   // Used to keep the layer and animation manager in sync.
   uint64_t mAnimationGeneration;
 
   ReframingStyleContexts* mReframingStyleContexts;
 
   RestyleTracker mPendingRestyles;
   RestyleTracker mPendingAnimationRestyles;
+
+#ifdef RESTYLE_LOGGING
+  int32_t mLoggingDepth;
+#endif
 };
 
 /**
  * An ElementRestyler is created for *each* element in a subtree that we
  * recompute styles for.
  */
 class ElementRestyler MOZ_FINAL {
 public:
@@ -446,16 +489,22 @@ public:
    * mHintsHandled changes over time; it starts off as the hints that
    * have been handled by ancestors, and by the end of Restyle it
    * represents the hints that have been handled for this frame.  This
    * method is intended to be called after Restyle, to find out what
    * hints have been handled for this frame.
    */
   nsChangeHint HintsHandledForFrame() { return mHintsHandled; }
 
+#ifdef RESTYLE_LOGGING
+  bool ShouldLogRestyle() {
+    return RestyleManager::ShouldLogRestyle(mPresContext);
+  }
+#endif
+
 private:
   // Enum for the result of RestyleSelf, which indicates whether the
   // restyle procedure should continue to the children, and how.
   //
   // These values must be ordered so that later values imply that all
   // the work of the earlier values is also done.
   enum RestyleResult {
 
@@ -469,18 +518,17 @@ private:
     eRestyleResult_ContinueAndForceDescendants
   };
 
   /**
    * First half of Restyle().
    */
   RestyleResult RestyleSelf(nsIFrame* aSelf,
                             nsRestyleHint aRestyleHint,
-                            uint32_t* aSwappedStructs,
-                            nsIFrame** aProviderFrame);
+                            uint32_t* aSwappedStructs);
 
   /**
    * Restyle the children of this frame (and, in turn, their children).
    *
    * Second half of Restyle().
    */
   void RestyleChildren(nsRestyleHint aChildRestyleHint);
 
@@ -513,16 +561,24 @@ private:
   };
 
   enum A11yNotificationType {
     eDontNotify,
     eNotifyShown,
     eNotifyHidden
   };
 
+#ifdef RESTYLE_LOGGING
+  int32_t& LoggingDepth() { return mLoggingDepth; }
+#endif
+
+#ifdef DEBUG
+  static nsCString RestyleResultToString(RestyleResult aRestyleResult);
+#endif
+
 private:
   nsPresContext* const mPresContext;
   nsIFrame* const mFrame;
   nsIContent* const mParentContent;
   // |mContent| is the node that we used for rule matching of
   // normal elements (not pseudo-elements) and for which we generate
   // framechange hints if we need them.
   nsIContent* const mContent;
@@ -542,13 +598,17 @@ private:
 
 #ifdef ACCESSIBILITY
   const DesiredA11yNotifications mDesiredA11yNotifications;
   DesiredA11yNotifications mKidsDesiredA11yNotifications;
   A11yNotificationType mOurA11yNotification;
   nsTArray<nsIContent*>& mVisibleKidsOfHiddenElement;
   bool mWasFrameVisible;
 #endif
+
+#ifdef RESTYLE_LOGGING
+  int32_t mLoggingDepth;
+#endif
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_RestyleManager_h */
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -7,19 +7,48 @@
  * A class which manages pending restyles.  This handles keeping track
  * of what nodes restyles need to happen on and so forth.
  */
 
 #include "RestyleTracker.h"
 #include "nsStyleChangeList.h"
 #include "RestyleManager.h"
 #include "GeckoProfiler.h"
+#include "nsIDocument.h"
+#include "RestyleTrackerInlines.h"
 
 namespace mozilla {
 
+#ifdef RESTYLE_LOGGING
+static nsCString
+GetDocumentURI(nsIDocument* aDocument)
+{
+  nsCString result;
+  nsString url;
+  aDocument->GetDocumentURI(url);
+  result.Append(NS_ConvertUTF16toUTF8(url).get());
+  return result;
+}
+
+static nsCString
+FrameTagToString(dom::Element* aElement)
+{
+  nsCString result;
+  nsIFrame* frame = aElement->GetPrimaryFrame();
+  if (frame) {
+    nsFrame::ListTag(result, frame);
+  } else {
+    nsAutoString buf;
+    aElement->Tag()->ToString(buf);
+    result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement);
+  }
+  return result;
+}
+#endif
+
 inline nsIDocument*
 RestyleTracker::Document() const {
   return mRestyleManager->PresContext()->Document();
 }
 
 #define RESTYLE_ARRAY_STACKSIZE 128
 
 struct LaterSiblingCollector {
@@ -51,16 +80,19 @@ CollectLaterSiblings(nsISupports* aEleme
 
 struct RestyleEnumerateData : RestyleTracker::Hints {
   nsRefPtr<dom::Element> mElement;
 };
 
 struct RestyleCollector {
   RestyleTracker* tracker;
   RestyleEnumerateData** restyleArrayPtr;
+#ifdef RESTYLE_LOGGING
+  uint32_t count;
+#endif
 };
 
 static PLDHashOperator
 CollectRestyles(nsISupports* aElement,
                 nsAutoPtr<RestyleTracker::RestyleData>& aData,
                 void* aRestyleCollector)
 {
   dom::Element* element =
@@ -68,16 +100,19 @@ CollectRestyles(nsISupports* aElement,
   RestyleCollector* collector =
     static_cast<RestyleCollector*>(aRestyleCollector);
   // Only collect the entries that actually need restyling by us (and
   // haven't, for example, already been restyled).
   // It's important to not mess with the flags on entries not in our
   // document.
   if (element->GetCrossShadowCurrentDoc() != collector->tracker->Document() ||
       !element->HasFlag(collector->tracker->RestyleBit())) {
+    LOG_RESTYLE_IF(collector->tracker, true,
+                   "skipping pending restyle %s, already restyled or no longer "
+                   "in the document", FrameTagToString(element).get());
     return PL_DHASH_NEXT;
   }
 
   NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) ||
                // Maybe we're just not reachable via the frame tree?
                (element->GetFlattenedTreeParent() &&
                 (!element->GetFlattenedTreeParent()->GetPrimaryFrame()||
                  element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
@@ -96,16 +131,20 @@ CollectRestyles(nsISupports* aElement,
                       collector->tracker->RootBit());
 
   RestyleEnumerateData** restyleArrayPtr = collector->restyleArrayPtr;
   RestyleEnumerateData* currentRestyle = *restyleArrayPtr;
   currentRestyle->mElement = element;
   currentRestyle->mRestyleHint = aData->mRestyleHint;
   currentRestyle->mChangeHint = aData->mChangeHint;
 
+#ifdef RESTYLE_LOGGING
+  collector->count++;
+#endif
+
   // Increment to the next slot in the array
   *restyleArrayPtr = currentRestyle + 1;
 
   return PL_DHASH_NEXT;
 }
 
 inline void
 RestyleTracker::ProcessOneRestyle(Element* aElement,
@@ -113,16 +152,20 @@ RestyleTracker::ProcessOneRestyle(Elemen
                                   nsChangeHint aChangeHint)
 {
   NS_PRECONDITION((aRestyleHint & eRestyle_LaterSiblings) == 0,
                   "Someone should have handled this before calling us");
   NS_PRECONDITION(Document(), "Must have a document");
   NS_PRECONDITION(aElement->GetCrossShadowCurrentDoc() == Document(),
                   "Element has unexpected document");
 
+  LOG_RESTYLE("aRestyleHint = %s, aChangeHint = %s",
+              RestyleManager::RestyleHintToString(aRestyleHint).get(),
+              RestyleManager::ChangeHintToString(aChangeHint).get());
+
   nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
   if (aRestyleHint & ~eRestyle_LaterSiblings) {
     mRestyleManager->RestyleElement(aElement, primaryFrame, aChangeHint,
                                     *this, aRestyleHint);
   } else if (aChangeHint &&
              (primaryFrame ||
               (aChangeHint & nsChangeHint_ReconstructFrame))) {
     // Don't need to recompute style; just apply the hint
@@ -135,34 +178,47 @@ RestyleTracker::ProcessOneRestyle(Elemen
 void
 RestyleTracker::DoProcessRestyles()
 {
   PROFILER_LABEL("RestyleTracker", "ProcessRestyles",
     js::ProfileEntry::Category::CSS);
 
   mRestyleManager->BeginProcessingRestyles();
 
+  LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s",
+              mPendingRestyles.Count(),
+              mRestyleManager->PresContext()->IsProcessingAnimationStyleChange()
+                ? (const char*) "animation " : (const char*) "",
+              static_cast<int>(mRestyleRoots.Length()),
+              GetDocumentURI(Document()).get());
+  LOG_RESTYLE_INDENT();
+
   // loop so that we process any restyle events generated by processing
   while (mPendingRestyles.Count()) {
     if (mHaveLaterSiblingRestyles) {
       // Convert them to individual restyles on all the later siblings
       nsAutoTArray<nsRefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
       LaterSiblingCollector siblingCollector = { this, &laterSiblingArr };
       mPendingRestyles.Enumerate(CollectLaterSiblings, &siblingCollector);
       for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
         Element* element = laterSiblingArr[i];
         for (nsIContent* sibling = element->GetNextSibling();
              sibling;
              sibling = sibling->GetNextSibling()) {
-          if (sibling->IsElement() &&
-              AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
-                                NS_STYLE_HINT_NONE)) {
-              // Nothing else to do here; we'll handle the following
-              // siblings when we get to |sibling| in laterSiblingArr.
-            break;
+          if (sibling->IsElement()) {
+            LOG_RESTYLE("adding pending restyle for %s due to "
+                        "eRestyle_LaterSiblings hint on %s",
+                        FrameTagToString(sibling->AsElement()).get(),
+                        FrameTagToString(element->AsElement()).get());
+            if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
+                                  NS_STYLE_HINT_NONE)) {
+                // Nothing else to do here; we'll handle the following
+                // siblings when we get to |sibling| in laterSiblingArr.
+              break;
+            }
           }
         }
       }
 
       // Now remove all those eRestyle_LaterSiblings bits
       for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
         Element* element = laterSiblingArr[i];
         NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
@@ -171,39 +227,48 @@ RestyleTracker::DoProcessRestyles()
         bool found =
 #endif
           mPendingRestyles.Get(element, &data);
         NS_ASSERTION(found, "Where did our entry go?");
         data->mRestyleHint =
           nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings);
       }
 
+      LOG_RESTYLE("%d pending restyles after expanding out "
+                  "eRestyle_LaterSiblings", mPendingRestyles.Count());
+
       mHaveLaterSiblingRestyles = false;
     }
 
     uint32_t rootCount;
     while ((rootCount = mRestyleRoots.Length())) {
       // Make sure to pop the element off our restyle root array, so
       // that we can freely append to the array as we process this
       // element.
       nsRefPtr<Element> element;
       element.swap(mRestyleRoots[rootCount - 1]);
       mRestyleRoots.RemoveElementAt(rootCount - 1);
 
+      LOG_RESTYLE("processing style root %s at index %d",
+                  FrameTagToString(element).get(), rootCount - 1);
+      LOG_RESTYLE_INDENT();
+
       // Do the document check before calling GetRestyleData, since we
       // don't want to do the sibling-processing GetRestyleData does if
       // the node is no longer relevant.
       if (element->GetCrossShadowCurrentDoc() != Document()) {
         // Content node has been removed from our document; nothing else
         // to do here
+        LOG_RESTYLE("skipping, no longer in the document");
         continue;
       }
 
       nsAutoPtr<RestyleData> data;
       if (!GetRestyleData(element, data)) {
+        LOG_RESTYLE("skipping, already restyled");
         continue;
       }
 
       ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint);
       AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
     }
 
     if (mHaveLaterSiblingRestyles) {
@@ -222,19 +287,26 @@ RestyleTracker::DoProcessRestyles()
     if (restylesToProcess) {
       RestyleEnumerateData* lastRestyle = restylesToProcess;
       RestyleCollector collector = { this, &lastRestyle };
       mPendingRestyles.Enumerate(CollectRestyles, &collector);
 
       // Clear the hashtable now that we don't need it anymore
       mPendingRestyles.Clear();
 
+#ifdef RESTYLE_LOGGING
+      uint32_t index = 0;
+#endif
       for (RestyleEnumerateData* currentRestyle = restylesToProcess;
            currentRestyle != lastRestyle;
            ++currentRestyle) {
+        LOG_RESTYLE("processing pending restyle %s at index %d/%d",
+                    FrameTagToString(currentRestyle->mElement).get(),
+                    index++, collector.count);
+        LOG_RESTYLE_INDENT();
         ProcessOneRestyle(currentRestyle->mElement,
                           currentRestyle->mRestyleHint,
                           currentRestyle->mChangeHint);
       }
     }
   }
 
   mRestyleManager->EndProcessingRestyles();
--- a/layout/base/RestyleTracker.h
+++ b/layout/base/RestyleTracker.h
@@ -10,16 +10,17 @@
 
 #ifndef mozilla_RestyleTracker_h
 #define mozilla_RestyleTracker_h
 
 #include "mozilla/dom/Element.h"
 #include "nsClassHashtable.h"
 #include "nsContainerFrame.h"
 #include "mozilla/SplayTree.h"
+#include "mozilla/RestyleLogging.h"
 
 namespace mozilla {
 
 class RestyleManager;
 class ElementRestyler;
 
 /** 
  * Helper class that collects a list of frames that need
@@ -228,19 +229,19 @@ private:
 };
 
 class RestyleTracker {
 public:
   typedef mozilla::dom::Element Element;
 
   friend class ElementRestyler; // for AddPendingRestyleToTable
 
-  explicit RestyleTracker(Element::FlagsType aRestyleBits) :
-    mRestyleBits(aRestyleBits),
-    mHaveLaterSiblingRestyles(false)
+  explicit RestyleTracker(Element::FlagsType aRestyleBits)
+    : mRestyleBits(aRestyleBits)
+    , mHaveLaterSiblingRestyles(false)
   {
     NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0,
                     "Why do we have these bits set?");
     NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != 0,
                     "Must have a restyle flag");
     NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) !=
                       ELEMENT_PENDING_RESTYLE_FLAGS,
                     "Shouldn't have both restyle flags set");
@@ -337,16 +338,22 @@ public:
   void AddRestyleRootsIfAwaitingRestyle(
                                   const nsTArray<nsRefPtr<Element>>& aElements);
 
   /**
    * The document we're associated with.
    */
   inline nsIDocument* Document() const;
 
+#ifdef RESTYLE_LOGGING
+  // Defined in RestyleTrackerInlines.h.
+  inline bool ShouldLogRestyle();
+  inline int32_t& LoggingDepth();
+#endif
+
 private:
   bool AddPendingRestyleToTable(Element* aElement, nsRestyleHint aRestyleHint,
                                 nsChangeHint aMinChangeHint);
 
   /**
    * Handle a single mPendingRestyles entry.  aRestyleHint must not
    * include eRestyle_LaterSiblings; that needs to be dealt with
    * before calling this function.
new file mode 100644
--- /dev/null
+++ b/layout/base/RestyleTrackerInlines.h
@@ -0,0 +1,23 @@
+/* -*- 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 mozilla_RestyleTrackerInlines_h
+#define mozilla_RestyleTrackerInlines_h
+
+#ifdef RESTYLE_LOGGING
+bool
+mozilla::RestyleTracker::ShouldLogRestyle()
+{
+  return mRestyleManager->ShouldLogRestyle();
+}
+
+int32_t&
+mozilla::RestyleTracker::LoggingDepth()
+{
+  return mRestyleManager->LoggingDepth();
+}
+#endif
+
+#endif // !defined(mozilla_RestyleTrackerInlines_h)
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -54,16 +54,17 @@ EXPORTS += [
     'Units.h',
     'UnitTransforms.h',
     'WordMovementType.h',
 ]
 
 EXPORTS.mozilla += [
     'GeometryUtils.h',
     'PaintTracker.h',
+    'RestyleLogging.h',
 ]
 
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
     'DisplayItemClip.cpp',
     'DisplayListClipState.cpp',
     'FrameLayerBuilder.cpp',
     'FramePropertyTable.cpp',
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -148,17 +148,18 @@ enum nsChangeHint {
    * set, then nsChangeHint_NeutralChange need not also be included, but it is
    * safe to do so.  (An example of style structs having non-meaningfully
    * different data would be cached information that would be re-calculated
    * to the same values, such as nsStyleBorder::mSubImages.)
    */
   nsChangeHint_NeutralChange = 0x100000
 
   // IMPORTANT NOTE: When adding new hints, consider whether you need to
-  // add them to NS_HintsNotHandledForDescendantsIn() below.
+  // add them to NS_HintsNotHandledForDescendantsIn() below.  Please also
+  // add them to RestyleManager::ChangeHintToString.
 };
 
 // Redefine these operators to return nothing. This will catch any use
 // of these operators on hints. We should not be using these operators
 // on nsChangeHints
 inline void operator<(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator>(nsChangeHint s1, nsChangeHint s2) {}
 inline void operator!=(nsChangeHint s1, nsChangeHint s2) {}
@@ -272,16 +273,19 @@ inline nsChangeHint NS_HintsNotHandledFo
  * can stop processing at a frame when it detects no style changes and it is
  * known that the styles of the subtree beneath it will not change, leaving
  * the old style context on the frame.  eRestyle_Force can be used to skip this
  * optimization on a frame, and to force its new style context to be used.
  *
  * Similarly, eRestyle_ForceDescendants will cause the frame and all of its
  * descendants to be traversed and for the new style contexts that are created
  * to be set on the frames.
+ *
+ * NOTE: When adding new restyle hints, please also add them to
+ * RestyleManager::RestyleHintToString.
  */
 enum nsRestyleHint {
   // Rerun selector matching on the element.  If a new style context
   // results, update the style contexts of descendants.  (Irrelevant if
   // eRestyle_Subtree is also set, since that implies a superset of the
   // work.)
   eRestyle_Self = (1<<0),
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -266,16 +266,21 @@ public:
   bool GetSelectedFramesOnly() { return mSelectedFramesOnly; }
   /**
    * Calling this setter makes us compute accurate visible regions at the cost
    * of performance if regions get very complex.
    */
   void SetAccurateVisibleRegions() { mAccurateVisibleRegions = true; }
   bool GetAccurateVisibleRegions() { return mAccurateVisibleRegions; }
   /**
+   * @return Returns true if we should include the caret in any display lists
+   * that we make.
+   */
+  bool IsBuildingCaret() { return mBuildCaret; }
+  /**
    * Allows callers to selectively override the regular paint suppression checks,
    * so that methods like GetFrameForPoint work when painting is suppressed.
    */
   void IgnorePaintSuppression() { mIgnoreSuppression = true; }
   /**
    * @return Returns if this builder will ignore paint suppression.
    */
   bool IsIgnoringPaintSuppression() { return mIgnoreSuppression; }
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -36,17 +36,17 @@
 #include "SurfaceCache.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleNode.h"
 #include "gfxPlatform.h"
 #include "nsCSSRules.h"
 #include "nsFontFaceLoader.h"
 #include "mozilla/EventListenerManager.h"
 #include "prenv.h"
-#include "nsObjectFrame.h"
+#include "nsPluginFrame.h"
 #include "nsTransitionManager.h"
 #include "nsAnimationManager.h"
 #include "CounterStyleManager.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
 #include "nsIMessageManager.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "nsSMILAnimationController.h"
@@ -1069,16 +1069,20 @@ nsPresContext::Init(nsDeviceContext* aDe
                                 "nglayout.debug.paint_flashing_chrome",
                                 this);
 
   nsresult rv = mEventManager->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mEventManager->SetPresContext(this);
 
+#ifdef RESTYLE_LOGGING
+  mRestyleLoggingEnabled = RestyleManager::RestyleLoggingInitiallyEnabled();
+#endif
+
 #ifdef DEBUG
   mInitialized = true;
 #endif
 
   mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THIN] = CSSPixelsToAppUnits(1);
   mBorderWidthTable[NS_STYLE_BORDER_WIDTH_MEDIUM] = CSSPixelsToAppUnits(3);
   mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THICK] = CSSPixelsToAppUnits(5);
 
@@ -2851,17 +2855,17 @@ nsRootPresContext::UnregisterPluginForGe
 {
   mRegisteredPlugins.RemoveEntry(aPlugin);
 }
 
 static PLDHashOperator
 SetPluginHidden(nsRefPtrHashKey<nsIContent>* aEntry, void* userArg)
 {
   nsIFrame* root = static_cast<nsIFrame*>(userArg);
-  nsObjectFrame* f = static_cast<nsObjectFrame*>(aEntry->GetKey()->GetPrimaryFrame());
+  nsPluginFrame* f = static_cast<nsPluginFrame*>(aEntry->GetKey()->GetPrimaryFrame());
   if (!f) {
     NS_WARNING("Null frame in SetPluginHidden");
     return PL_DHASH_NEXT;
   }
   if (!nsLayoutUtils::IsAncestorFrameCrossDoc(root, f)) {
     // f is not managed by this frame so we should ignore it.
     return PL_DHASH_NEXT;
   }
@@ -3011,34 +3015,34 @@ SortConfigurations(nsTArray<nsIWidget::C
     aConfigurations->AppendElement(pluginsToMove[i]);
     pluginsToMove.RemoveElementAt(i);
   }
 }
 
 static PLDHashOperator
 PluginDidSetGeometryEnumerator(nsRefPtrHashKey<nsIContent>* aEntry, void* userArg)
 {
-  nsObjectFrame* f = static_cast<nsObjectFrame*>(aEntry->GetKey()->GetPrimaryFrame());
+  nsPluginFrame* f = static_cast<nsPluginFrame*>(aEntry->GetKey()->GetPrimaryFrame());
   if (!f) {
     NS_WARNING("Null frame in PluginDidSetGeometryEnumerator");
     return PL_DHASH_NEXT;
   }
   f->DidSetWidgetGeometry();
   return PL_DHASH_NEXT;
 }
 
 struct PluginGetGeometryUpdateClosure {
   nsTArray<nsIWidget::Configuration> mConfigurations;
 };
 static PLDHashOperator
 PluginGetGeometryUpdate(nsRefPtrHashKey<nsIContent>* aEntry, void* userArg)
 {
   PluginGetGeometryUpdateClosure* closure =
     static_cast<PluginGetGeometryUpdateClosure*>(userArg);
-  nsObjectFrame* f = static_cast<nsObjectFrame*>(aEntry->GetKey()->GetPrimaryFrame());
+  nsPluginFrame* f = static_cast<nsPluginFrame*>(aEntry->GetKey()->GetPrimaryFrame());
   if (!f) {
     NS_WARNING("Null frame in GetPluginGeometryUpdate");
     return PL_DHASH_NEXT;
   }
   f->GetWidgetConfiguration(&closure->mConfigurations);
   return PL_DHASH_NEXT;
 }
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -32,16 +32,17 @@
 #include "nsAutoPtr.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/AppUnits.h"
 #include "prclist.h"
 #include "nsThreadUtils.h"
 #include "ScrollbarStyles.h"
 #include "nsIMessageManager.h"
+#include "mozilla/RestyleLogging.h"
 
 class nsBidiPresUtils;
 class nsAString;
 class nsIPrintSettings;
 class nsDocShell;
 class nsIDocShell;
 class nsIDocument;
 class nsILanguageAtomService;
@@ -54,17 +55,17 @@ class nsIAtom;
 class nsICSSPseudoComparator;
 struct nsStyleBackground;
 struct nsStyleBorder;
 class nsIRunnable;
 class gfxUserFontSet;
 class gfxTextPerfMetrics;
 class nsUserFontSet;
 struct nsFontFaceRuleContainer;
-class nsObjectFrame;
+class nsPluginFrame;
 class nsTransitionManager;
 class nsAnimationManager;
 class nsRefreshDriver;
 class nsIWidget;
 class nsDeviceContext;
 
 namespace mozilla {
 class EventStateManager;
@@ -1146,16 +1147,24 @@ public:
 
   /**
    * Checks for MozAfterPaint listeners on the document and
    * any subdocuments, except for subdocuments that are non-top-level
    * content documents.
    */
   bool MayHavePaintEventListenerInSubDocument();
 
+#ifdef RESTYLE_LOGGING
+  // Controls for whether debug information about restyling in this
+  // document should be output.
+  bool RestyleLoggingEnabled() const { return mRestyleLoggingEnabled; }
+  void StartRestyleLogging() { mRestyleLoggingEnabled = true; }
+  void StopRestyleLogging() { mRestyleLoggingEnabled = false; }
+#endif
+
 protected:
   void InvalidateThebesLayers();
   void AppUnitsPerDevPixelChanged();
 
   void HandleRebuildUserFontSet() {
     mPostedFlushUserFontSet = false;
     FlushUserFontSet();
   }
@@ -1347,16 +1356,21 @@ protected:
 
   // Should we paint flash in this context? Do not use this variable directly.
   // Use GetPaintFlashing() method instead.
   mutable unsigned mPaintFlashing : 1;
   mutable unsigned mPaintFlashingInitialized : 1;
 
   unsigned mHasWarnedAboutPositionedTableParts : 1;
 
+#ifdef RESTYLE_LOGGING
+  // Should we output debug information about restyling for this document?
+  bool                  mRestyleLoggingEnabled;
+#endif
+
 #ifdef DEBUG
   bool                  mInitialized;
 #endif
 
 
 protected:
 
   virtual ~nsPresContext();
@@ -1426,17 +1440,17 @@ public:
   bool NeedToComputePluginGeometryUpdates()
   {
     return mRegisteredPlugins.Count() > 0;
   }
   /**
    * Compute geometry updates for each plugin given that aList is the display
    * list for aFrame. The updates are not yet applied;
    * ApplyPluginGeometryUpdates is responsible for that. In the meantime they
-   * are stored on each nsObjectFrame.
+   * are stored on each nsPluginFrame.
    * This needs to be called even when aFrame is a popup, since although
    * windowed plugins aren't allowed in popups, windowless plugins are
    * and ComputePluginGeometryUpdates needs to be called for them.
    */
   void ComputePluginGeometryUpdates(nsIFrame* aFrame,
                                     nsDisplayListBuilder* aBuilder,
                                     nsDisplayList* aList);
 
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -485,8 +485,9 @@ skip-if = true || (toolkit == 'android')
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 948948
 [test_bug687297.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 948948
 support-files =
   bug687297_a.html
   bug687297_b.html
   bug687297_c.html
 [test_bug990340.html]
+[test_bug1070851.html]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug1070851.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1070851
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1070851</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <style>
+  /* Eliminate the blue glow when focusing an element. */
+  input, textarea, div {
+    background: none;
+    border: none;
+    outline: none;
+  }
+  </style>
+
+  <script type="application/javascript">
+
+  /** Test for Bug 1070851 **/
+
+  SimpleTest.waitForExplicitFinish();
+
+  function testSnapshot(aElementId)
+  {
+    var noTouchCaret = snapshotWindow(window, true);
+
+    // Focus the element to make the touch caret show, but do not capture it.
+    var element = document.getElementById(aElementId);
+    element.focus();
+    var notCaptureTouchCaret = snapshotWindow(window, false);
+
+    [result, first, second] = compareSnapshots(noTouchCaret, notCaptureTouchCaret,
+                                               true);
+    ok(result, "Both snapshots of " + aElementId + " should have no touch caret." +
+       "\nFirst snapshot: " + first + "\nSecond snapshot: " + second);
+
+    element.blur();
+  }
+
+  SimpleTest.waitForFocus(function() {
+    testSnapshot('input');
+    testSnapshot('textarea');
+    testSnapshot('content');
+    SimpleTest.finish();
+  })
+  </script>
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1070851">Mozilla Bug 1070851</a>
+  <input id="input">
+  <textarea id="textarea"></textarea>
+  <div id="content" contenteditable="true">test</div>
+</body>
+</html>
--- a/layout/generic/moz.build
+++ b/layout/generic/moz.build
@@ -22,17 +22,17 @@ EXPORTS += [
     'nsIFrameInlines.h',
     'nsIFrameUtil.h',
     'nsILineIterator.h',
     'nsIObjectFrame.h',
     'nsIPageSequenceFrame.h',
     'nsIScrollableFrame.h',
     'nsIScrollPositionListener.h',
     'nsIStatefulFrame.h',
-    'nsObjectFrame.h',
+    'nsPluginFrame.h',
     'nsQueryFrame.h',
     'nsSplittableFrame.h',
     'nsSubDocumentFrame.h',
     'nsTextRunTransformations.h',
     'ScrollbarActivity.h',
     'Selection.h',
     'WritingModes.h',
 ]
@@ -95,20 +95,20 @@ UNIFIED_SOURCES += [
     'nsVideoFrame.cpp',
     'nsViewportFrame.cpp',
     'ScrollbarActivity.cpp',
     'StickyScrollContainer.cpp',
     'TextOverflow.cpp',
 ]
 
 # nsLineLayout.cpp needs to be built separately because it uses plarena.h.
-# nsObjectFrame.cpp needs to be built separately because of name clashes in the OS X headers.
+# nsPluginFrame.cpp needs to be built separately because of name clashes in the OS X headers.
 SOURCES += [
     'nsLineLayout.cpp',
-    'nsObjectFrame.cpp',
+    'nsPluginFrame.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'nsPluginUtilsOSX.mm',
     ]
 
 FAIL_ON_WARNINGS = not CONFIG['_MSC_VER']
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -411,16 +411,21 @@ nsCanvasFrame::BuildDisplayList(nsDispla
     if (needBlendContainer) {
       aLists.BorderBackground()->AppendNewToTop(
         new (aBuilder) nsDisplayBlendContainer(aBuilder, this, aLists.BorderBackground()));
     }
   }
 
   nsIFrame* kid;
   for (kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
+    // Skip touch caret frame if we do not build caret.
+    if (!aBuilder->IsBuildingCaret() && kid->GetContent() == mTouchCaretElement) {
+      continue;
+    }
+
     // Put our child into its own pseudo-stack.
     BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   }
 
 #ifdef DEBUG_CANVAS_FOCUS
   nsCOMPtr<nsIContent> focusContent;
   aPresContext->EventStateManager()->
     GetFocusedContent(getter_AddRefs(focusContent));
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -98,17 +98,17 @@ FRAME_ID(nsMathMLmunderFrame)
 FRAME_ID(nsMathMLmunderoverFrame)
 FRAME_ID(nsMathMLsemanticsFrame)
 FRAME_ID(nsMathMLTokenFrame)
 FRAME_ID(nsMenuBarFrame)
 FRAME_ID(nsMenuFrame)
 FRAME_ID(nsMenuPopupFrame)
 FRAME_ID(nsMeterFrame)
 FRAME_ID(nsNumberControlFrame)
-FRAME_ID(nsObjectFrame)
+FRAME_ID(nsPluginFrame)
 FRAME_ID(nsPageBreakFrame)
 FRAME_ID(nsPageContentFrame)
 FRAME_ID(nsPageFrame)
 FRAME_ID(nsPlaceholderFrame)
 FRAME_ID(nsPopupSetFrame)
 FRAME_ID(nsProgressFrame)
 FRAME_ID(nsProgressMeterFrame)
 FRAME_ID(nsRangeFrame)
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -483,19 +483,19 @@ nsHTMLScrollFrame::ReflowScrolledFrame(S
   // it to the correct size anyway in PlaceScrollArea. Allowing it to
   // resize here would size it to the natural height of the frame,
   // which will usually be different from the scrollport height;
   // invalidating the difference will cause unnecessary repainting.
   FinishReflowChild(mHelper.mScrolledFrame, presContext,
                     *aMetrics, &kidReflowState, 0, 0,
                     NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW);
 
-  // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
+  // XXX Some frames (e.g., nsPluginFrame, nsFrameFrame, nsTextFrame) don't bother
   // setting their mOverflowArea. This is wrong because every frame should
-  // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
+  // always set mOverflowArea. In fact nsPluginFrame and nsFrameFrame don't
   // support the 'outline' property because of this. Rather than fix the world
   // right now, just fix up the overflow area if necessary. Note that we don't
   // check HasOverflowRect() because it could be set even though the
   // overflow area doesn't include the frame bounds.
   aMetrics->UnionOverflowAreasWithDesiredBounds();
 
   if (MOZ_UNLIKELY(StyleDisplay()->mOverflowClipBox ==
                      NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
rename from layout/generic/nsObjectFrame.cpp
rename to layout/generic/nsPluginFrame.cpp
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 // vim:set ts=2 sts=2 sw=2 et cin:
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* rendering objects for replaced elements implemented by a plugin */
 
-#include "nsObjectFrame.h"
+#include "nsPluginFrame.h"
 
 #include "gfx2DGlue.h"
 #include "gfxMatrix.h"
 #include "mozilla/BasicEvents.h"
 #ifdef XP_WIN
 // This is needed for DoublePassRenderingEvent.
 #include "mozilla/plugins/PluginMessageUtils.h"
 #endif
@@ -92,17 +92,17 @@ using mozilla::DefaultXDisplay;
 #endif
 
 #ifdef PR_LOGGING 
 static PRLogModuleInfo *
 GetObjectFrameLog()
 {
   static PRLogModuleInfo *sLog;
   if (!sLog)
-    sLog = PR_NewLogModule("nsObjectFrame");
+    sLog = PR_NewLogModule("nsPluginFrame");
   return sLog;
 }
 #endif /* PR_LOGGING */
 
 #if defined(XP_MACOSX) && !defined(__LP64__)
 
 // The header files QuickdrawAPI.h and QDOffscreen.h are missing on OS X 10.7
 // and up (though the QuickDraw APIs defined in them are still present) -- so
@@ -140,17 +140,17 @@ extern "C" {
 #endif /* #if defined(XP_MACOSX) && !defined(__LP64__) */
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 class PluginBackgroundSink : public ReadbackSink {
 public:
-  PluginBackgroundSink(nsObjectFrame* aFrame, uint64_t aStartSequenceNumber)
+  PluginBackgroundSink(nsPluginFrame* aFrame, uint64_t aStartSequenceNumber)
     : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {}
   ~PluginBackgroundSink()
   {
     if (mFrame) {
       mFrame->mBackgroundSink = nullptr;
     }
   }
 
@@ -182,67 +182,67 @@ protected:
         mFrame->mInstanceOwner) {
       mLastSequenceNumber = aSequenceNumber;
       return true;
     }
     return false;
   }
 
   uint64_t mLastSequenceNumber;
-  nsObjectFrame* mFrame;
+  nsPluginFrame* mFrame;
 };
 
-nsObjectFrame::nsObjectFrame(nsStyleContext* aContext)
-  : nsObjectFrameSuper(aContext)
+nsPluginFrame::nsPluginFrame(nsStyleContext* aContext)
+  : nsPluginFrameSuper(aContext)
   , mReflowCallbackPosted(false)
 {
   PR_LOG(GetObjectFrameLog(), PR_LOG_DEBUG,
-         ("Created new nsObjectFrame %p\n", this));
+         ("Created new nsPluginFrame %p\n", this));
 }
 
-nsObjectFrame::~nsObjectFrame()
+nsPluginFrame::~nsPluginFrame()
 {
   PR_LOG(GetObjectFrameLog(), PR_LOG_DEBUG,
-         ("nsObjectFrame %p deleted\n", this));
+         ("nsPluginFrame %p deleted\n", this));
 }
 
-NS_QUERYFRAME_HEAD(nsObjectFrame)
-  NS_QUERYFRAME_ENTRY(nsObjectFrame)
+NS_QUERYFRAME_HEAD(nsPluginFrame)
+  NS_QUERYFRAME_ENTRY(nsPluginFrame)
   NS_QUERYFRAME_ENTRY(nsIObjectFrame)
-NS_QUERYFRAME_TAIL_INHERITING(nsObjectFrameSuper)
+NS_QUERYFRAME_TAIL_INHERITING(nsPluginFrameSuper)
 
 #ifdef ACCESSIBILITY
 a11y::AccType
-nsObjectFrame::AccessibleType()
+nsPluginFrame::AccessibleType()
 {
   return a11y::ePluginType;
 }
 
 #ifdef XP_WIN
-NS_IMETHODIMP nsObjectFrame::GetPluginPort(HWND *aPort)
+NS_IMETHODIMP nsPluginFrame::GetPluginPort(HWND *aPort)
 {
   *aPort = (HWND) mInstanceOwner->GetPluginPortFromWidget();
   return NS_OK;
 }
 #endif
 #endif
 
 void
-nsObjectFrame::Init(nsIContent*       aContent,
+nsPluginFrame::Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow)
 {
   PR_LOG(GetObjectFrameLog(), PR_LOG_DEBUG,
-         ("Initializing nsObjectFrame %p for content %p\n", this, aContent));
+         ("Initializing nsPluginFrame %p for content %p\n", this, aContent));
 
-  nsObjectFrameSuper::Init(aContent, aParent, aPrevInFlow);
+  nsPluginFrameSuper::Init(aContent, aParent, aPrevInFlow);
 }
 
 void
-nsObjectFrame::DestroyFrom(nsIFrame* aDestructRoot)
+nsPluginFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   if (mReflowCallbackPosted) {
     PresContext()->PresShell()->CancelReflowCallback(this);
   }
 
   // Tell content owner of the instance to disconnect its frame.
   nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent));
   NS_ASSERTION(objContent, "Why not an object loading content?");
@@ -254,51 +254,51 @@ nsObjectFrame::DestroyFrom(nsIFrame* aDe
     mInstanceOwner->SetFrame(nullptr);
   }
   objContent->HasNewFrame(nullptr);
 
   if (mBackgroundSink) {
     mBackgroundSink->Destroy();
   }
 
-  nsObjectFrameSuper::DestroyFrom(aDestructRoot);
+  nsPluginFrameSuper::DestroyFrom(aDestructRoot);
 }
 
 /* virtual */ void
-nsObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
+nsPluginFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   if (HasView()) {
     nsView* view = GetView();
     nsViewManager* vm = view->GetViewManager();
     if (vm) {
       nsViewVisibility visibility = 
         IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow;
       vm->SetViewVisibility(view, visibility);
     }
   }
 
-  nsObjectFrameSuper::DidSetStyleContext(aOldStyleContext);
+  nsPluginFrameSuper::DidSetStyleContext(aOldStyleContext);
 }
 
 nsIAtom*
-nsObjectFrame::GetType() const
+nsPluginFrame::GetType() const
 {
   return nsGkAtoms::objectFrame; 
 }
 
 #ifdef DEBUG_FRAME_DUMP
 nsresult
-nsObjectFrame::GetFrameName(nsAString& aResult) const
+nsPluginFrame::GetFrameName(nsAString& aResult) const
 {
-  return MakeFrameName(NS_LITERAL_STRING("ObjectFrame"), aResult);
+  return MakeFrameName(NS_LITERAL_STRING("PluginFrame"), aResult);
 }
 #endif
 
 nsresult
-nsObjectFrame::PrepForDrawing(nsIWidget *aWidget)
+nsPluginFrame::PrepForDrawing(nsIWidget *aWidget)
 {
   mWidget = aWidget;
 
   nsView* view = GetView();
   NS_ASSERTION(view, "Object frames must have views");  
   if (!view) {
     return NS_ERROR_FAILURE;
   }
@@ -406,39 +406,39 @@ nsObjectFrame::PrepForDrawing(nsIWidget 
 
   return NS_OK;
 }
 
 #define EMBED_DEF_WIDTH 240
 #define EMBED_DEF_HEIGHT 200
 
 /* virtual */ nscoord
-nsObjectFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+nsPluginFrame::GetMinISize(nsRenderingContext *aRenderingContext)
 {
   nscoord result = 0;
 
   if (!IsHidden(false)) {
     nsIAtom *atom = mContent->Tag();
     if (atom == nsGkAtoms::applet || atom == nsGkAtoms::embed) {
       result = nsPresContext::CSSPixelsToAppUnits(EMBED_DEF_WIDTH);
     }
   }
 
   DISPLAY_MIN_WIDTH(this, result);
   return result;
 }
 
 /* virtual */ nscoord
-nsObjectFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+nsPluginFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
 {
-  return nsObjectFrame::GetMinISize(aRenderingContext);
+  return nsPluginFrame::GetMinISize(aRenderingContext);
 }
 
 void
-nsObjectFrame::GetDesiredSize(nsPresContext* aPresContext,
+nsPluginFrame::GetDesiredSize(nsPresContext* aPresContext,
                               const nsHTMLReflowState& aReflowState,
                               nsHTMLReflowMetrics& aMetrics)
 {
   // By default, we have no area
   aMetrics.ClearSize();
 
   if (IsHidden(false)) {
     return;
@@ -493,22 +493,22 @@ nsObjectFrame::GetDesiredSize(nsPresCont
   // XXXbz don't add in the border and padding, because we screw up our
   // plugin's size and positioning if we do...  Eventually we _do_ want to
   // paint borders, though!  At that point, we will need to adjust the desired
   // size either here or in Reflow....  Further, we will need to fix Paint() to
   // call the superclass in all cases.
 }
 
 void
-nsObjectFrame::Reflow(nsPresContext*           aPresContext,
+nsPluginFrame::Reflow(nsPresContext*           aPresContext,
                       nsHTMLReflowMetrics&     aMetrics,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus)
 {
-  DO_GLOBAL_REFLOW_COUNT("nsObjectFrame");
+  DO_GLOBAL_REFLOW_COUNT("nsPluginFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 
   // Get our desired size
   GetDesiredSize(aPresContext, aReflowState, aMetrics);
   aMetrics.SetOverflowAreasToDesiredBounds();
   FinishAndStoreOverflow(&aMetrics);
 
   // delay plugin instantiation until all children have
@@ -543,31 +543,31 @@ nsObjectFrame::Reflow(nsPresContext*    
   aStatus = NS_FRAME_COMPLETE;
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
 }
 
 ///////////// nsIReflowCallback ///////////////
 
 bool
-nsObjectFrame::ReflowFinished()
+nsPluginFrame::ReflowFinished()
 {
   mReflowCallbackPosted = false;
   CallSetWindow();
   return true;
 }
 
 void
-nsObjectFrame::ReflowCallbackCanceled()
+nsPluginFrame::ReflowCallbackCanceled()
 {
   mReflowCallbackPosted = false;
 }
 
 void
-nsObjectFrame::FixupWindow(const nsSize& aSize)
+nsPluginFrame::FixupWindow(const nsSize& aSize)
 {
   nsPresContext* presContext = PresContext();
 
   if (!mInstanceOwner)
     return;
 
   NPWindow *window;
   mInstanceOwner->GetWindow(window);
@@ -608,17 +608,17 @@ nsObjectFrame::FixupWindow(const nsSize&
 #else
   mInstanceOwner->UpdateWindowPositionAndClipRect(false);
 #endif
 
   NotifyPluginReflowObservers();
 }
 
 nsresult
-nsObjectFrame::CallSetWindow(bool aCheckIsHidden)
+nsPluginFrame::CallSetWindow(bool aCheckIsHidden)
 {
   NPWindow *win = nullptr;
  
   nsresult rv = NS_ERROR_FAILURE;
   nsRefPtr<nsNPAPIPluginInstance> pi;
   if (!mInstanceOwner ||
       NS_FAILED(rv = mInstanceOwner->GetInstance(getter_AddRefs(pi))) ||
       !pi ||
@@ -678,17 +678,17 @@ nsObjectFrame::CallSetWindow(bool aCheck
   }
 
   instanceOwnerRef->ReleasePluginPort(window->window);
 
   return rv;
 }
 
 void
-nsObjectFrame::RegisterPluginForGeometryUpdates()
+nsPluginFrame::RegisterPluginForGeometryUpdates()
 {
   nsRootPresContext* rpc = PresContext()->GetRootPresContext();
   NS_ASSERTION(rpc, "We should have a root pres context!");
   if (mRootPresContextRegisteredWith == rpc || !rpc) {
     // Already registered with current root pres context,
     // or null root pres context...
     return;
   }
@@ -697,28 +697,28 @@ nsObjectFrame::RegisterPluginForGeometry
     // re-register with our current one...
     UnregisterPluginForGeometryUpdates();
   }
   mRootPresContextRegisteredWith = rpc;
   mRootPresContextRegisteredWith->RegisterPluginForGeometryUpdates(mContent);
 }
 
 void
-nsObjectFrame::UnregisterPluginForGeometryUpdates()
+nsPluginFrame::UnregisterPluginForGeometryUpdates()
 {
   if (!mRootPresContextRegisteredWith) {
     // Not registered...
     return;
   }
   mRootPresContextRegisteredWith->UnregisterPluginForGeometryUpdates(mContent);
   mRootPresContextRegisteredWith = nullptr;
 }
 
 void
-nsObjectFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner)
+nsPluginFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner)
 {
   // The ownership model here is historically fuzzy. This should only be called
   // by nsPluginInstanceOwner when it is given a new frame, and
   // nsObjectLoadingContent should be arbitrating frame-ownership via its
   // HasNewFrame callback.
   mInstanceOwner = aOwner;
   if (mInstanceOwner) {
     return;
@@ -738,25 +738,25 @@ nsObjectFrame::SetInstanceOwner(nsPlugin
       mWidget->Show(false);
       mWidget->Enable(false);
       mWidget->SetParent(nullptr);
     }
   }
 }
 
 bool
-nsObjectFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
+nsPluginFrame::IsFocusable(int32_t *aTabIndex, bool aWithMouse)
 {
   if (aTabIndex)
     *aTabIndex = -1;
-  return nsObjectFrameSuper::IsFocusable(aTabIndex, aWithMouse);
+  return nsPluginFrameSuper::IsFocusable(aTabIndex, aWithMouse);
 }
 
 bool
-nsObjectFrame::IsHidden(bool aCheckVisibilityStyle) const
+nsPluginFrame::IsHidden(bool aCheckVisibilityStyle) const
 {
   if (aCheckVisibilityStyle) {
     if (!StyleVisibility()->IsVisibleOrCollapsed())
       return true;    
   }
 
   // only <embed> tags support the HIDDEN attribute
   if (mContent->Tag() == nsGkAtoms::embed) {
@@ -775,17 +775,17 @@ nsObjectFrame::IsHidden(bool aCheckVisib
          !hidden.LowerCaseEqualsLiteral("off")))) {
       return true;
     }
   }
 
   return false;
 }
 
-nsIntPoint nsObjectFrame::GetWindowOriginInPixels(bool aWindowless)
+nsIntPoint nsPluginFrame::GetWindowOriginInPixels(bool aWindowless)
 {
   nsView * parentWithView;
   nsPoint origin(0,0);
 
   GetOffsetFromView(origin, &parentWithView);
 
   // if it's windowless, let's make sure we have our origin set right
   // it may need to be corrected, like after scrolling
@@ -796,61 +796,61 @@ nsIntPoint nsObjectFrame::GetWindowOrigi
   }
   origin += GetContentRectRelativeToSelf().TopLeft();
 
   return nsIntPoint(PresContext()->AppUnitsToDevPixels(origin.x),
                     PresContext()->AppUnitsToDevPixels(origin.y));
 }
 
 void
-nsObjectFrame::DidReflow(nsPresContext*            aPresContext,
+nsPluginFrame::DidReflow(nsPresContext*            aPresContext,
                          const nsHTMLReflowState*  aReflowState,
                          nsDidReflowStatus         aStatus)
 {
   // Do this check before calling the superclass, as that clears
   // NS_FRAME_FIRST_REFLOW
   if (aStatus == nsDidReflowStatus::FINISHED &&
       (GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     nsCOMPtr<nsIObjectLoadingContent> objContent(do_QueryInterface(mContent));
     NS_ASSERTION(objContent, "Why not an object loading content?");
     objContent->HasNewFrame(this);
   }
 
-  nsObjectFrameSuper::DidReflow(aPresContext, aReflowState, aStatus);
+  nsPluginFrameSuper::DidReflow(aPresContext, aReflowState, aStatus);
 
   // The view is created hidden; once we have reflowed it and it has been
   // positioned then we show it.
   if (aStatus != nsDidReflowStatus::FINISHED)
     return;
 
   if (HasView()) {
     nsView* view = GetView();
     nsViewManager* vm = view->GetViewManager();
     if (vm)
       vm->SetViewVisibility(view, IsHidden() ? nsViewVisibility_kHide : nsViewVisibility_kShow);
   }
 }
 
 /* static */ void
-nsObjectFrame::PaintPrintPlugin(nsIFrame* aFrame, nsRenderingContext* aCtx,
+nsPluginFrame::PaintPrintPlugin(nsIFrame* aFrame, nsRenderingContext* aCtx,
                                 const nsRect& aDirtyRect, nsPoint aPt)
 {
   gfxContext* ctx = aCtx->ThebesContext();
 
   // Translate the context:
   nsPoint pt = aPt + aFrame->GetContentRectRelativeToSelf().TopLeft();
   gfxPoint devPixelPt =
     nsLayoutUtils::PointToGfxPoint(pt, aFrame->PresContext()->AppUnitsPerDevPixel());
 
   gfxContextMatrixAutoSaveRestore autoSR(ctx);
   ctx->SetMatrix(ctx->CurrentMatrix().Translate(devPixelPt));
 
   // FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
 
-  static_cast<nsObjectFrame*>(aFrame)->PrintPlugin(*aCtx, aDirtyRect);
+  static_cast<nsPluginFrame*>(aFrame)->PrintPlugin(*aCtx, aDirtyRect);
 }
 
 /**
  * nsDisplayPluginReadback creates an active ReadbackLayer. The ReadbackLayer
  * obtains from the compositor the contents of the window underneath
  * the ReadbackLayer, which we then use as an opaque buffer for plugins to
  * asynchronously draw onto.
  */
@@ -871,17 +871,17 @@ public:
                            bool* aSnap) MOZ_OVERRIDE;
 
   NS_DISPLAY_DECL_NAME("PluginReadback", TYPE_PLUGIN_READBACK)
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE
   {
-    return static_cast<nsObjectFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
+    return static_cast<nsPluginFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
   }
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) MOZ_OVERRIDE
   {
     return LAYER_ACTIVE;
   }
@@ -920,17 +920,17 @@ public:
                            bool* aSnap) MOZ_OVERRIDE;
 
   NS_DISPLAY_DECL_NAME("PluginVideo", TYPE_PLUGIN_VIDEO)
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE
   {
-    return static_cast<nsObjectFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
+    return static_cast<nsPluginFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this, aContainerParameters);
   }
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) MOZ_OVERRIDE
   {
     return LAYER_ACTIVE;
   }
@@ -956,27 +956,27 @@ nsDisplayPlugin::GetBounds(nsDisplayList
   *aSnap = true;
   return GetDisplayItemBounds(aBuilder, this, mFrame);
 }
 
 void
 nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder,
                        nsRenderingContext* aCtx)
 {
-  nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
+  nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame);
   bool snap;
   f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder, &snap));
 }
 
 bool
 nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion)
 {
   if (aBuilder->IsForPluginGeometry()) {
-    nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
+    nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame);
     if (!aBuilder->IsInTransform() || f->IsPaintedByGecko()) {
       // Since transforms induce reference frames, we don't need to worry
       // about this method fluffing out due to non-rectilinear transforms.
       nsRect rAncestor = nsLayoutUtils::TransformFrameRectToAncestor(f,
           f->GetContentRectRelativeToSelf(), ReferenceFrame());
       nscoord appUnitsPerDevPixel =
         ReferenceFrame()->PresContext()->AppUnitsPerDevPixel();
       f->mNextConfigurationBounds = rAncestor.ToNearestPixels(appUnitsPerDevPixel);
@@ -1011,17 +1011,17 @@ nsDisplayPlugin::ComputeVisibility(nsDis
 }
 
 nsRegion
 nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                  bool* aSnap)
 {
   *aSnap = false;
   nsRegion result;
-  nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
+  nsPluginFrame* f = static_cast<nsPluginFrame*>(mFrame);
   if (!aBuilder->IsForPluginGeometry()) {
     nsIWidget* widget = f->GetWidget();
     if (widget) {
       // Be conservative and treat plugins with widgets as not opaque,
       // because that's simple and we might need the content under the widget
       // if the widget is unexpectedly clipped away. (As can happen when
       // chrome content over a plugin forces us to clip out the plugin for
       // security reasons.)
@@ -1040,31 +1040,31 @@ nsDisplayPlugin::GetOpaqueRegion(nsDispl
       result = bounds;
     }
   }
 
   return result;
 }
 
 nsresult
-nsObjectFrame::PluginEventNotifier::Run() {
+nsPluginFrame::PluginEventNotifier::Run() {
   nsCOMPtr<nsIObserverService> obsSvc =
     mozilla::services::GetObserverService();
   obsSvc->NotifyObservers(nullptr, "plugin-changed-event", mEventType.get());
   return NS_OK;
 }
 
 void
-nsObjectFrame::NotifyPluginReflowObservers()
+nsPluginFrame::NotifyPluginReflowObservers()
 {
   nsContentUtils::AddScriptRunner(new PluginEventNotifier(NS_LITERAL_STRING("reflow")));
 }
 
 void
-nsObjectFrame::DidSetWidgetGeometry()
+nsPluginFrame::DidSetWidgetGeometry()
 {
 #if defined(XP_MACOSX)
   if (mInstanceOwner) {
     mInstanceOwner->FixUpPluginWindow(nsPluginInstanceOwner::ePluginPaintEnable);
   }
 #else
   if (!mWidget && mInstanceOwner) {
     // UpdateWindowVisibility will notify the plugin of position changes
@@ -1075,31 +1075,31 @@ nsObjectFrame::DidSetWidgetGeometry()
     mInstanceOwner->UpdateWindowVisibility(
       nsLayoutUtils::IsPopup(nsLayoutUtils::GetDisplayRootFrame(this)) ||
       !mNextConfigurationBounds.IsEmpty());
   }
 #endif
 }
 
 bool
-nsObjectFrame::IsOpaque() const
+nsPluginFrame::IsOpaque() const
 {
 #if defined(XP_MACOSX)
   // ???
   return false;
 #elif defined(MOZ_WIDGET_ANDROID)
   // We don't know, so just assume transparent
   return false;
 #else
   return !IsTransparentMode();
 #endif
 }
 
 bool
-nsObjectFrame::IsTransparentMode() const
+nsPluginFrame::IsTransparentMode() const
 {
 #if defined(XP_MACOSX)
   // ???
   return false;
 #else
   if (!mInstanceOwner)
     return false;
 
@@ -1120,33 +1120,33 @@ nsObjectFrame::IsTransparentMode() const
 
   bool transparent = false;
   pi->IsTransparent(&transparent);
   return transparent;
 #endif
 }
 
 void
-nsObjectFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+nsPluginFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists)
 {
   // XXX why are we painting collapsed object frames?
   if (!IsVisibleOrCollapsedForPainting(aBuilder))
     return;
 
   DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   nsPresContext::nsPresContextType type = PresContext()->Type();
 
   // If we are painting in Print Preview do nothing....
   if (type == nsPresContext::eContext_PrintPreview)
     return;
 
-  DO_GLOBAL_REFLOW_COUNT_DSP("nsObjectFrame");
+  DO_GLOBAL_REFLOW_COUNT_DSP("nsPluginFrame");
 
 #ifndef XP_MACOSX
   if (mWidget && aBuilder->IsInTransform()) {
     // Windowed plugins should not be rendered inside a transform.
     return;
   }
 #endif
 
@@ -1202,17 +1202,17 @@ nsObjectFrame::BuildDisplayList(nsDispla
 #endif
 
     aLists.Content()->AppendNewToTop(new (aBuilder)
       nsDisplayPlugin(aBuilder, this));
   }
 }
 
 void
-nsObjectFrame::PrintPlugin(nsRenderingContext& aRenderingContext,
+nsPluginFrame::PrintPlugin(nsRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect)
 {
   nsCOMPtr<nsIObjectLoadingContent> obj(do_QueryInterface(mContent));
   if (!obj)
     return;
 
   nsIFrame* frame = nullptr;
   obj->GetPrintFrame(&frame);
@@ -1415,33 +1415,33 @@ nsObjectFrame::PrintPlugin(nsRenderingCo
   // XXX Nav 4.x always sent a SetWindow call after print. Should we do the same?
   // XXX Calling DidReflow here makes no sense!!!
   nsDidReflowStatus status = nsDidReflowStatus::FINISHED; // should we use a special status?
   frame->DidReflow(presContext,
                    nullptr, status);  // DidReflow will take care of it
 }
 
 nsRect
-nsObjectFrame::GetPaintedRect(nsDisplayPlugin* aItem)
+nsPluginFrame::GetPaintedRect(nsDisplayPlugin* aItem)
 {
   if (!mInstanceOwner)
     return nsRect();
   nsRect r = GetContentRectRelativeToSelf();
   if (!mInstanceOwner->UseAsyncRendering())
     return r;
 
   nsIntSize size = mInstanceOwner->GetCurrentImageSize();
   nsPresContext* pc = PresContext();
   r.IntersectRect(r, nsRect(0, 0, pc->DevPixelsToAppUnits(size.width),
                                   pc->DevPixelsToAppUnits(size.height)));
   return r;
 }
 
 LayerState
-nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder,
+nsPluginFrame::GetLayerState(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager)
 {
   if (!mInstanceOwner)
     return LAYER_NONE;
 
 #ifdef MOZ_WIDGET_ANDROID
   // We always wan