Merge autoland to mozilla-central r=merge a=merge
authorTiberius Oros <toros@mozilla.com>
Fri, 24 Nov 2017 00:23:46 +0200
changeset 438026 b705100d6ca823ddafe43ba4188dce181dcd79fd
parent 437927 0bb0f14672fdda31c19aea1ed829e050d693b9af (current diff)
parent 438025 be5e92dc51ec0fe08ac21ce20e4d639a396b5165 (diff)
child 438027 3f5d48c08903475b5f556f3d5906773978b30489
push id117
push userfmarier@mozilla.com
push dateTue, 28 Nov 2017 20:17:16 +0000
reviewersmerge, merge
milestone59.0a1
Merge autoland to mozilla-central r=merge a=merge
devtools/client/framework/about-devtools-toolbox.js
devtools/client/webconsole/new-console-output/test/mochitest/test_bug1045902_console_csp_ignore_reflected_xss_message.html
devtools/client/webconsole/new-console-output/test/mochitest/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
testing/talos/talos/pageloader/pageloader-signed.xpi
testing/web-platform/meta/custom-elements/reactions/DOMStringMap.html.ini
testing/web-platform/meta/custom-elements/reactions/DOMTokenList.html.ini
testing/web-platform/meta/webvtt/api/interfaces.html.ini
toolkit/components/telemetry/gen-event-data.py
toolkit/components/telemetry/gen-event-enum.py
toolkit/components/telemetry/gen-histogram-data.py
toolkit/components/telemetry/gen-histogram-enum.py
toolkit/components/telemetry/gen-process-data.py
toolkit/components/telemetry/gen-process-enum.py
toolkit/components/telemetry/gen-scalar-data.py
toolkit/components/telemetry/gen-scalar-enum.py
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1711,16 +1711,18 @@ pref("extensions.formautofill.creditCard
 //    because of a duplicate profile in the storage)
 // 2: saw the doorhanger
 // 3: submitted an autofill'ed credit card form
 pref("extensions.formautofill.creditCards.used", 0);
 pref("extensions.formautofill.firstTimeUse", true);
 pref("extensions.formautofill.heuristics.enabled", true);
 pref("extensions.formautofill.section.enabled", true);
 pref("extensions.formautofill.loglevel", "Warn");
+// Comma separated list of countries Form Autofill supports
+pref("extensions.formautofill.supportedCountries", "US");
 
 // Whether or not to restore a session with lazy-browser tabs.
 pref("browser.sessionstore.restore_tabs_lazily", true);
 
 pref("browser.suppress_first_window_animation", true);
 
 // Preferences for Photon onboarding system extension
 pref("browser.onboarding.enabled", true);
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -7,26 +7,24 @@
 this.EXPORTED_SYMBOLS = ["FormAutofillUtils", "AddressDataLoader"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const ADDRESS_METADATA_PATH = "resource://formautofill/addressmetadata/";
 const ADDRESS_REFERENCES = "addressReferences.js";
 const ADDRESS_REFERENCES_EXT = "addressReferencesExt.js";
 
-// TODO: This list should become a pref in Bug 1413494
-const SUPPORTED_COUNTRY_LIST = ["US"];
-
 const ADDRESSES_COLLECTION_NAME = "addresses";
 const CREDITCARDS_COLLECTION_NAME = "creditCards";
 const ADDRESSES_FIRST_TIME_USE_PREF = "extensions.formautofill.firstTimeUse";
 const ENABLED_AUTOFILL_ADDRESSES_PREF = "extensions.formautofill.addresses.enabled";
 const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";
 const AUTOFILL_CREDITCARDS_AVAILABLE_PREF = "extensions.formautofill.creditCards.available";
 const ENABLED_AUTOFILL_CREDITCARDS_PREF = "extensions.formautofill.creditCards.enabled";
+const SUPPORTED_COUNTRIES_PREF = "extensions.formautofill.supportedCountries";
 const MANAGE_ADDRESSES_KEYWORDS = ["manageAddressesTitle", "addNewAddressTitle"];
 const EDIT_ADDRESS_KEYWORDS = [
   "givenName", "additionalName", "familyName", "organization2", "streetAddress",
   "state", "province", "city", "country", "zip", "postalCode", "email", "tel",
 ];
 const MANAGE_CREDITCARDS_KEYWORDS = ["manageCreditCardsTitle", "addNewCreditCardTitle", "showCreditCardsBtnLabel"];
 const EDIT_CREDITCARD_KEYWORDS = ["cardNumber", "nameOnCard", "cardExpires"];
 const FIELD_STATES = {
@@ -373,17 +371,17 @@ this.FormAutofillUtils = {
    * Use alternative country name list to identify a country code from a
    * specified country name.
    * @param   {string} countryName A country name to be identified
    * @param   {string} [countrySpecified] A country code indicating that we only
    *                                      search its alternative names if specified.
    * @returns {string} The matching country code.
    */
   identifyCountryCode(countryName, countrySpecified) {
-    let countries = countrySpecified ? [countrySpecified] : SUPPORTED_COUNTRY_LIST;
+    let countries = countrySpecified ? [countrySpecified] : this.supportedCountries;
 
     for (let country of countries) {
       let collators = this.getCollators(country);
 
       let metadata = this.getCountryAddressData(country);
       let alternativeCountryNames = metadata.alternative_names || [metadata.name];
       let reAlternativeCountryNames = this._reAlternativeCountryNames[country];
       if (!reAlternativeCountryNames) {
@@ -691,8 +689,11 @@ XPCOMUtils.defineLazyPreferenceGetter(th
 XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
                                       "isAutofillCreditCardsAvailable", AUTOFILL_CREDITCARDS_AVAILABLE_PREF);
 XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
                                       "_isAutofillCreditCardsEnabled", ENABLED_AUTOFILL_CREDITCARDS_PREF);
 XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
                                       "isAutofillAddressesFirstTimeUse", ADDRESSES_FIRST_TIME_USE_PREF);
 XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
                                       "AutofillCreditCardsUsedStatus", CREDITCARDS_USED_STATUS_PREF);
+XPCOMUtils.defineLazyPreferenceGetter(this.FormAutofillUtils,
+                                      "supportedCountries", SUPPORTED_COUNTRIES_PREF, null, null,
+                                      val => val.split(","));
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -1211,19 +1211,17 @@ class AutofillRecords {
 }
 
 class Addresses extends AutofillRecords {
   constructor(store) {
     super(store, "addresses", VALID_ADDRESS_FIELDS, VALID_ADDRESS_COMPUTED_FIELDS, ADDRESS_SCHEMA_VERSION);
   }
 
   _recordReadProcessor(address) {
-    // TODO: We only support US in MVP so hide the field if it's not. We
-    //       are going to support more countries in bug 1370193.
-    if (address.country && address.country != "US") {
+    if (address.country && !FormAutofillUtils.supportedCountries.includes(address.country)) {
       delete address.country;
       delete address["country-name"];
     }
   }
 
   _computeFields(address) {
     // NOTE: Remember to bump the schema version number if any of the existing
     //       computing algorithm changes. (No need to bump when just adding new
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -54,17 +54,19 @@ function addUpgradeListener(instanceID) 
 
 function isAvailable() {
   let availablePref = Services.prefs.getCharPref("extensions.formautofill.available");
   if (availablePref == "on") {
     return true;
   } else if (availablePref == "detect") {
     let locale = Services.locale.getRequestedLocale();
     let region = Services.prefs.getCharPref("browser.search.region", "");
-    return locale == "en-US" && region == "US";
+    let supportedCountries = Services.prefs.getCharPref("extensions.formautofill.supportedCountries")
+                                           .split(",");
+    return locale == "en-US" && supportedCountries.includes(region);
   }
   return false;
 }
 
 function startup(data) {
   if (!isAvailable()) {
     Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill");
     // reset the sync related prefs incase the feature was previously available
--- a/browser/extensions/formautofill/content/editAddress.xhtml
+++ b/browser/extensions/formautofill/content/editAddress.xhtml
@@ -44,20 +44,19 @@
     <label id="postal-code-container">
       <span/>
       <input id="postal-code" type="text"/>
     </label>
     <label id="country-container">
       <span data-localization="country"/>
       <select id="country">
         <option/>
-        <option value="US" data-localization="us"/>
       </select>
     </label>
-    <p id="country-warning-message" data-localization="countryWarningMessage"/>
+    <p id="country-warning-message" data-localization="countryWarningMessage2"/>
     <label id="email-container">
       <span data-localization="email"/>
       <input id="email" type="email"/>
     </label>
     <label id="tel-container">
       <span data-localization="tel"/>
       <input id="tel" type="tel"/>
     </label>
--- a/browser/extensions/formautofill/content/editDialog.js
+++ b/browser/extensions/formautofill/content/editDialog.js
@@ -200,16 +200,24 @@ class EditAddress extends EditDialog {
     this._elements.postalCodeLabel.dataset.localization = postalCodeLabel;
     FormAutofillUtils.localizeMarkup(AUTOFILL_BUNDLE_URI, document);
   }
 
   localizeDocument() {
     if (this._record) {
       this._elements.title.dataset.localization = "editAddressTitle";
     }
+    let fragment = document.createDocumentFragment();
+    for (let country of FormAutofillUtils.supportedCountries) {
+      let option = new Option();
+      option.value = country;
+      option.dataset.localization = country.toLowerCase();
+      fragment.appendChild(option);
+    }
+    this._elements.country.appendChild(fragment);
     FormAutofillUtils.localizeMarkup(REGIONS_BUNDLE_URI, this._elements.country);
   }
 
   async handleSubmit() {
     await this.saveRecord(this.buildFormObject(), this._record ? this._record.guid : null);
     window.close();
   }
 }
--- a/browser/extensions/formautofill/locales/en-US/formautofill.properties
+++ b/browser/extensions/formautofill/locales/en-US/formautofill.properties
@@ -121,17 +121,17 @@ province = Province
 state = State
 postalCode = Postal Code
 zip = Zip Code
 country = Country or Region
 tel = Phone
 email = Email
 cancelBtnLabel = Cancel
 saveBtnLabel = Save
-countryWarningMessage = Form Autofill is currently available only for US addresses
+countryWarningMessage2 = Form Autofill is currently available only for certain countries.
 
 # LOCALIZATION NOTE (addNewCreditCardTitle, editCreditCardTitle): The dialog title for creating or editing
 # credit cards in browser preferences.
 addNewCreditCardTitle = Add New Credit Card
 editCreditCardTitle = Edit Credit Card
 cardNumber = Card Number
 nameOnCard = Name on Card
 cardExpires = Expires
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -371,16 +371,18 @@
 @RESPATH@/browser/components/nsSetDefaultBrowser.manifest
 @RESPATH@/browser/components/nsSetDefaultBrowser.js
 @RESPATH@/browser/components/devtools-startup.manifest
 @RESPATH@/browser/components/devtools-startup.js
 @RESPATH@/browser/components/aboutdebugging-registration.js
 @RESPATH@/browser/components/aboutdebugging.manifest
 @RESPATH@/browser/components/aboutdevtools-registration.js
 @RESPATH@/browser/components/aboutdevtools.manifest
+@RESPATH@/browser/components/aboutdevtoolstoolbox-registration.js
+@RESPATH@/browser/components/aboutdevtoolstoolbox.manifest
 @RESPATH@/browser/components/Experiments.manifest
 @RESPATH@/browser/components/ExperimentsService.js
 @RESPATH@/browser/components/browser-newtab.xpt
 @RESPATH@/browser/components/aboutNewTabService.js
 @RESPATH@/browser/components/NewTabComponents.manifest
 @RESPATH@/components/Downloads.manifest
 @RESPATH@/components/DownloadLegacy.js
 @RESPATH@/components/thumbnails.xpt
--- a/build/moz.configure/memory.configure
+++ b/build/moz.configure/memory.configure
@@ -9,38 +9,27 @@
 def moz_jemalloc4(value):
     die('MOZ_JEMALLOC4 is deprecated')
 
 
 option('--enable-jemalloc', env='MOZ_MEMORY',
        help='Replace memory allocator with jemalloc')
 
 
-@depends('--enable-jemalloc', target, build_project, c_compiler)
-def jemalloc(value, target, build_project, c_compiler):
+@depends('--enable-jemalloc', target, c_compiler)
+def jemalloc(value, target, c_compiler):
     if value.origin != 'default':
         return bool(value) or None
 
-    if build_project == 'js':
-        return True
-
-    if target.kernel == 'Darwin' and target.cpu == 'x86_64':
-        # Don't enable by default on 32-bits OSX. See bug 702250.
+    if target.kernel in ('Darwin', 'Linux'):
         return True
 
     if target.kernel == 'WINNT' and c_compiler.type in ('msvc', 'clang-cl'):
         return True
 
-    if target.kernel == 'Linux':
-        return True
-
-    if value and target.kernel not in ('WINNT', 'Linux', 'Darwin', 'kFreeBSD',
-                                       'FreeBSD', 'NetBSD'):
-        die('--enable-jemalloc is not supported on %s', target.kernel)
-
 
 set_config('MOZ_MEMORY', jemalloc)
 set_define('MOZ_MEMORY', jemalloc)
 add_old_configure_assignment('MOZ_MEMORY', jemalloc)
 
 
 # Because --enable-jemalloc doesn't use a default because of the dependency
 # on the target, we can't use a js_option for it to propagate to js/src
--- a/devtools/client/aboutdebugging/initializer.js
+++ b/devtools/client/aboutdebugging/initializer.js
@@ -34,23 +34,21 @@ const AboutDebuggingApp = createFactory(
 var AboutDebugging = {
   init() {
     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
       // If DevTools are disabled, navigate to about:devtools.
       window.location = "about:devtools?reason=AboutDebugging";
       return;
     }
 
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-    }
+    DebuggerServer.init();
     DebuggerServer.allowChromeProcess = true;
     // We want a full featured server for about:debugging. Especially the
     // "browser actors" like addons.
-    DebuggerServer.registerActors({ root: true, browser: true, tab: true });
+    DebuggerServer.registerAllActors();
 
     this.client = new DebuggerClient(DebuggerServer.connectPipe());
 
     this.client.connect().then(() => {
       let client = this.client;
       let telemetry = new Telemetry();
 
       render(AboutDebuggingApp({ client, telemetry }),
--- a/devtools/client/animationinspector/components/animation-timeline.js
+++ b/devtools/client/animationinspector/components/animation-timeline.js
@@ -1,28 +1,28 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Task} = require("devtools/shared/task");
+const { Task } = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {
   createNode,
   findOptimalTimeInterval,
   getFormattedAnimationTitle,
   TimeScale,
   getCssPropertyName
 } = require("devtools/client/animationinspector/utils");
-const {AnimationDetails} = require("devtools/client/animationinspector/components/animation-details");
-const {AnimationTargetNode} = require("devtools/client/animationinspector/components/animation-target-node");
-const {AnimationTimeBlock} = require("devtools/client/animationinspector/components/animation-time-block");
+const { AnimationDetails } = require("devtools/client/animationinspector/components/animation-details");
+const { AnimationTargetNode } = require("devtools/client/animationinspector/components/animation-target-node");
+const { AnimationTimeBlock } = require("devtools/client/animationinspector/components/animation-time-block");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const L10N =
   new LocalizationHelper("devtools/client/locales/animationinspector.properties");
 
 // The minimum spacing between 2 time graduation headers in the timeline (px).
 const TIME_GRADUATION_MIN_SPACING = 40;
 // When the container window is resized, the timeline background gets refreshed,
@@ -80,31 +80,32 @@ AnimationsTimeline.prototype = {
   },
 
   setupSplitBox: function () {
     const browserRequire = this.win.BrowserLoader({
       window: this.win,
       useOnlyShared: true
     }).require;
 
-    const React = browserRequire("devtools/client/shared/vendor/react");
+    const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+    const dom = require("devtools/client/shared/vendor/react-dom-factories");
     const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
 
-    const SplitBox = React.createFactory(
+    const SplitBox = createFactory(
       browserRequire("devtools/client/shared/components/splitter/SplitBox"));
 
     const splitter = SplitBox({
       className: "animation-root",
       splitterSize: 1,
       initialHeight: "50%",
       endPanelControl: true,
-      startPanel: React.DOM.div({
+      startPanel: dom.div({
         className: "animation-timeline"
       }),
-      endPanel: React.DOM.div({
+      endPanel: dom.div({
         className: "animation-detail"
       }),
       vert: false
     });
 
     ReactDOM.render(splitter, this.rootWrapperEl);
 
     this.animationRootEl = this.rootWrapperEl.querySelector(".animation-root");
--- a/devtools/client/canvasdebugger/test/head.js
+++ b/devtools/client/canvasdebugger/test/head.js
@@ -186,20 +186,18 @@ function navigate(aTarget, aUrl, aWaitFo
 }
 
 function reload(aTarget, aWaitForTargetEvent = "navigate") {
   executeSoon(() => aTarget.activeTab.reload());
   return once(aTarget, aWaitForTargetEvent);
 }
 
 function initServer() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 }
 
 function initCallWatcherBackend(aUrl) {
   info("Initializing a call watcher front.");
   initServer();
 
   return Task.spawn(function* () {
     let tab = yield addTab(aUrl);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-debugging.js
@@ -13,20 +13,18 @@ var gNewChromeSource = promise.defer();
 
 var { DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var customLoader = new DevToolsLoader();
 customLoader.invisibleToDebugger = true;
 var { DebuggerServer } = customLoader.require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/debugger-client");
 
 function initDebuggerClient() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let transport = DebuggerServer.connectPipe();
   return new DebuggerClient(transport);
 }
 
 function attachThread(client, actor) {
   return new Promise(resolve => {
--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attach.js
@@ -6,17 +6,17 @@ var WORKER1_URL = "code_WorkerActor.atta
 var WORKER2_URL = "code_WorkerActor.attach-worker2.js";
 
 function test() {
   Task.spawn(function* () {
     let oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
     SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
 
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let tab = yield addTab(TAB1_URL);
     let { tabs } = yield listTabs(client);
     let [, tabClient] = yield attachTab(client, findTab(tabs, TAB1_URL));
     yield listWorkers(tabClient);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_WorkerActor.attachThread.js
@@ -1,15 +1,15 @@
 var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
 
 function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let client1 = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client1);
     let client2 = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client2);
 
     let tab = yield addTab(TAB_URL);
     let { tabs: tabs1 } = yield listTabs(client1);
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-01.js
@@ -7,20 +7,18 @@
  * Tests that the break-on-dom-events request works.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
 
 var gClient, gThreadClient, gInput, gButton;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_break-on-dom-event-02.js
@@ -8,20 +8,18 @@
  * listeners and handler objects with 'handleEvent' methods.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
 
 var gClient, gThreadClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
@@ -15,20 +15,18 @@ var gNewGlobal = promise.defer();
 var gNewChromeSource = promise.defer();
 
 var { DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var customLoader = new DevToolsLoader();
 customLoader.invisibleToDebugger = true;
 var { DebuggerServer } = customLoader.require("devtools/server/main");
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
@@ -8,20 +8,18 @@
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
 
 var gClient;
 var gTab;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
@@ -8,20 +8,18 @@
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
 
 var gClient;
 var gTab;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
@@ -9,20 +9,18 @@
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
 
 var gClient;
 var gTab;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
@@ -9,20 +9,18 @@
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
 
 var gClient;
 var gTab;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
@@ -7,20 +7,18 @@
  * Check extension-added global actor API.
  */
 
 const ACTORS_URL = CHROME_URL + "testactors.js";
 
 function test() {
   let gClient;
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   DebuggerServer.addActors(ACTORS_URL);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
@@ -9,20 +9,18 @@
 const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
 const ADDON1_PATH = "addon1.xpi";
 const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
 const ADDON2_PATH = "addon2.xpi";
 
 var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
@@ -8,20 +8,18 @@
  */
 
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
 
 var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
@@ -20,20 +20,18 @@ var gNewWindow;
 
 // Stock onListChanged handler.
 var onListChangedCount = 0;
 function onListChangedHandler() {
   onListChangedCount++;
 }
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   gTabList = new BrowserTabList("fake DebuggerServerConnection");
   gTabList._testing = true;
   gTabList.onListChanged = onListChangedHandler;
 
   checkSingleTab()
     .then(addTabA)
     .then(testTabA)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
@@ -7,20 +7,18 @@
  * Make sure the listTabs request works as specified.
  */
 
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
     let tab = yield addTab(TAB1_URL);
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
@@ -1,16 +1,16 @@
 var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
 var WORKER1_URL = "code_listworkers-worker1.js";
 var WORKER2_URL = "code_listworkers-worker2.js";
 
 function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
     let tab = yield addTab(TAB_URL);
     let { tabs } = yield listTabs(client);
     let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
@@ -10,20 +10,18 @@
 
 const TAB1_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_script-switching-02.html";
 
 var gNewTab, gNewWindow;
 var gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
@@ -8,20 +8,18 @@
  */
 
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
 
 var gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB1_URL)
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-allocation-stack.js
@@ -11,17 +11,17 @@
 
 const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html";
 const { PromisesFront } = require("devtools/shared/fronts/promises");
 var EventEmitter = require("devtools/shared/event-emitter");
 
 function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let options = {
       source: TAB_URL,
       line: 1
     };
     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-chrome-allocation-stack.js
@@ -19,17 +19,17 @@ const STACK_DATA = [
   { functionDisplayName: "testGetAllocationStack" },
 ];
 
 function test() {
   Task.spawn(function* () {
     requestLongerTimeout(10);
 
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
     let chrome = yield client.getProcess();
     let [, tabClient] = yield attachTab(client, chrome.form);
     yield tabClient.attachThread();
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-fulfillment-stack.js
@@ -29,17 +29,17 @@ const TEST_DATA = [
     line: 14,
     column: 15
   },
 ];
 
 function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let options = {
       source: TAB_URL,
       line: 1
     };
     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
--- a/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_promises-rejection-stack.js
@@ -36,17 +36,17 @@ const TEST_DATA = [
     line: 14,
     column: 15
   },
 ];
 
 function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let options = {
       source: TAB_URL,
       line: 1
     };
     const [ tab,, panel ] = yield initDebugger(TAB_URL, options);
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-01.js
@@ -8,20 +8,18 @@
  */
 
 const ACTORS_URL = CHROME_URL + "testactors.js";
 const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 var gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   DebuggerServer.addActors(ACTORS_URL);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_tabactor-02.js
@@ -8,20 +8,18 @@
  */
 
 const ACTORS_URL = CHROME_URL + "testactors.js";
 const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 var gClient;
 
 function test() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   DebuggerServer.addActors(ACTORS_URL);
 
   let transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
@@ -10,17 +10,17 @@ PromiseTestUtils.expectUncaughtRejection
 
 var TAB_URL = EXAMPLE_URL + "doc_WorkerActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerActor.attachThread-worker.js";
 
 add_task(function* () {
   yield pushPrefs(["devtools.scratchpad.enabled", true]);
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   yield connect(client);
 
   let tab = yield addTab(TAB_URL);
   let { tabs } = yield listTabs(client);
   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
 
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -622,20 +622,18 @@ function AddonDebugger() {
   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
   EventEmitter.decorate(this);
 }
 
 AddonDebugger.prototype = {
   init: Task.async(function* (aAddonId) {
     info("Initializing an addon debugger panel.");
 
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     this.frame = document.createElement("iframe");
     this.frame.setAttribute("height", 400);
     document.documentElement.appendChild(this.frame);
     window.addEventListener("message", this._onMessage);
 
     let transport = DebuggerServer.connectPipe();
@@ -1319,20 +1317,18 @@ function waitForDispatch(panel, type, ev
       yield _afterDispatchDone(controller, actionType);
       count++;
       info(type + " dispatched " + count + " time(s)");
     }
   });
 }
 
 function* initWorkerDebugger(TAB_URL, WORKER_URL) {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   yield connect(client);
 
   let tab = yield addTab(TAB_URL);
   let { tabs } = yield listTabs(client);
   let [, tabClient] = yield attachTab(client, findTab(tabs, TAB_URL));
 
--- a/devtools/client/framework/ToolboxProcess.jsm
+++ b/devtools/client/framework/ToolboxProcess.jsm
@@ -140,17 +140,17 @@ BrowserToolboxProcess.prototype = {
 
     // Forward interesting events.
     this.debuggerServer.on("connectionchange", this._onConnectionChange);
 
     this.debuggerServer.init();
     // We mainly need a root actor and tab actors for opening a toolbox, even
     // against chrome/content/addon. But the "no auto hide" button uses the
     // preference actor, so also register the browser actors.
-    this.debuggerServer.registerActors({ root: true, browser: true, tab: true });
+    this.debuggerServer.registerAllActors();
     this.debuggerServer.allowChromeProcess = true;
     dumpn("initialized and added the browser actors for the DebuggerServer.");
 
     let chromeDebuggingWebSocket =
       Services.prefs.getBoolPref("devtools.debugger.chrome-debugging-websocket");
     let listener = this.debuggerServer.createListener();
     listener.portOrPath = -1;
     listener.webSocket = chromeDebuggingWebSocket;
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -55,23 +55,16 @@ var gDevToolsBrowser = exports.gDevTools
    */
   _browserStyleSheets: new WeakMap(),
 
   /**
    * WeakMap keeping track of DeveloperToolbar instances for each firefox window.
    */
   _toolbars: new WeakMap(),
 
-  _tabStats: {
-    peakOpen: 0,
-    peakPinned: 0,
-    histOpen: [],
-    histPinned: []
-  },
-
   /**
    * This function is for the benefit of Tools:DevToolbox in
    * browser/base/content/browser-sets.inc and should not be used outside
    * of there
    */
   // used by browser-sets.inc, command
   toggleToolboxCommand(gBrowser, startTime) {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
@@ -323,20 +316,18 @@ var gDevToolsBrowser = exports.gDevTools
       win.focus();
     } else {
       Services.ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
     }
   },
 
   _getContentProcessTarget(processId) {
     // Create a DebuggerServer in order to connect locally to it
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     let transport = DebuggerServer.connectPipe();
     let client = new DebuggerClient(transport);
 
     let deferred = defer();
     client.connect().then(() => {
       client.getProcess(processId)
@@ -471,20 +462,16 @@ var gDevToolsBrowser = exports.gDevTools
 
     this.updateCommandAvailability(win);
     this.updateDevtoolsThemeAttribute(win);
     this.ensurePrefObserver();
     win.addEventListener("unload", this);
 
     let tabContainer = win.gBrowser.tabContainer;
     tabContainer.addEventListener("TabSelect", this);
-    tabContainer.addEventListener("TabOpen", this);
-    tabContainer.addEventListener("TabClose", this);
-    tabContainer.addEventListener("TabPinned", this);
-    tabContainer.addEventListener("TabUnpinned", this);
   },
 
   /**
    * Create singleton instance of the developer toolbar for a given top level window.
    *
    * @param {Window} win
    *        The window to which the toolbar should be created.
    */
@@ -706,45 +693,20 @@ var gDevToolsBrowser = exports.gDevTools
       styleSheet.remove();
       this._browserStyleSheets.delete(win);
     }
 
     this._toolbars.delete(win);
 
     let tabContainer = win.gBrowser.tabContainer;
     tabContainer.removeEventListener("TabSelect", this);
-    tabContainer.removeEventListener("TabOpen", this);
-    tabContainer.removeEventListener("TabClose", this);
-    tabContainer.removeEventListener("TabPinned", this);
-    tabContainer.removeEventListener("TabUnpinned", this);
   },
 
   handleEvent(event) {
     switch (event.type) {
-      case "TabOpen":
-      case "TabClose":
-      case "TabPinned":
-      case "TabUnpinned":
-        let open = 0;
-        let pinned = 0;
-
-        for (let win of this._trackedBrowserWindows) {
-          let tabContainer = win.gBrowser.tabContainer;
-          let numPinnedTabs = win.gBrowser._numPinnedTabs || 0;
-          let numTabs = tabContainer.itemCount - numPinnedTabs;
-
-          open += numTabs;
-          pinned += numPinnedTabs;
-        }
-
-        this._tabStats.histOpen.push(open);
-        this._tabStats.histPinned.push(pinned);
-        this._tabStats.peakOpen = Math.max(open, this._tabStats.peakOpen);
-        this._tabStats.peakPinned = Math.max(pinned, this._tabStats.peakPinned);
-        break;
       case "TabSelect":
         gDevToolsBrowser._updateMenuCheckbox();
         break;
       case "unload":
         // top-level browser window unload
         gDevToolsBrowser._forgetBrowserWindow(event.target.defaultView);
         break;
     }
--- a/devtools/client/framework/devtools.js
+++ b/devtools/client/framework/devtools.js
@@ -19,17 +19,16 @@ loader.lazyRequireGetter(this, "HUDServi
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 loader.lazyRequireGetter(this, "WebExtensionInspectedWindowFront",
       "devtools/shared/fronts/webextension-inspected-window", true);
 
 const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
   require("devtools/client/definitions");
 const EventEmitter = require("devtools/shared/old-event-emitter");
-const AboutDevTools = require("devtools/client/framework/about-devtools-toolbox");
 const {Task} = require("devtools/shared/task");
 const {getTheme, setTheme, addThemeObserver, removeThemeObserver} =
   require("devtools/client/shared/theme");
 
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
 
 /**
@@ -38,18 +37,16 @@ const MAX_ORDINAL = 99;
  */
 function DevTools() {
   this._tools = new Map(); // Map<toolId, tool>
   this._themes = new Map(); // Map<themeId, theme>
   this._toolboxes = new Map(); // Map<target, toolbox>
   // List of toolboxes that are still in process of creation
   this._creatingToolboxes = new Map(); // Map<target, toolbox Promise>
 
-  AboutDevTools.register();
-
   EventEmitter.decorate(this);
 
   // Listen for changes to the theme pref.
   this._onThemeChanged = this._onThemeChanged.bind(this);
   addThemeObserver(this._onThemeChanged);
 
   // This is important step in initialization codepath where we are going to
   // start registering all default tools and themes: create menuitems, keys, emit
@@ -663,17 +660,16 @@ DevTools.prototype = {
    */
   destroy({ shuttingDown }) {
     // Do not cleanup everything during firefox shutdown, but only when
     // devtools are reloaded via the add-on contribution workflow.
     if (!shuttingDown) {
       for (let [, toolbox] of this._toolboxes) {
         toolbox.destroy();
       }
-      AboutDevTools.unregister();
     }
 
     for (let [key, ] of this.getToolDefinitionMap()) {
       this.unregisterTool(key, true);
     }
 
     gDevTools.unregisterDefaults();
 
--- a/devtools/client/framework/moz.build
+++ b/devtools/client/framework/moz.build
@@ -9,17 +9,16 @@ TEST_HARNESS_FILES.xpcshell.devtools.cli
     'test/shared-redux-head.js',
 ]
 
 DIRS += [
     'components',
 ]
 
 DevToolsModules(
-    'about-devtools-toolbox.js',
     'attach-thread.js',
     'browser-menus.js',
     'devtools-browser.js',
     'devtools.js',
     'gDevTools.jsm',
     'menu-item.js',
     'menu.js',
     'selection.js',
@@ -30,10 +29,10 @@ DevToolsModules(
     'toolbox-highlighter-utils.js',
     'toolbox-host-manager.js',
     'toolbox-hosts.js',
     'toolbox-options.js',
     'toolbox.js',
     'ToolboxProcess.jsm',
 )
 
-with Files('**'):
-    BUG_COMPONENT = ('Firefox', 'Developer Tools: Framework')
+with Files('**'):
+    BUG_COMPONENT = ('Firefox', 'Developer Tools: Framework')
--- a/devtools/client/framework/target-from-url.js
+++ b/devtools/client/framework/target-from-url.js
@@ -127,16 +127,14 @@ function* createClient(params) {
   let port = params.get("port");
   let webSocket = !!params.get("ws");
 
   let transport;
   if (port) {
     transport = yield DebuggerClient.socketConnect({ host, port, webSocket });
   } else {
     // Setup a server if we don't have one already running
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     transport = DebuggerServer.connectPipe();
   }
   return new DebuggerClient(transport);
 }
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -399,25 +399,25 @@ TabTarget.prototype = {
       return this._remote.promise;
     }
 
     this._remote = defer();
 
     if (this.isLocalTab) {
       // Since a remote protocol connection will be made, let's start the
       // DebuggerServer here, once and for all tools.
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-      }
+      DebuggerServer.init();
+
       // When connecting to a local tab, we only need the root actor.
       // Then we are going to call DebuggerServer.connectToChild and talk
       // directly with actors living in the child process.
       // We also need browser actors for actor registry which enabled addons
       // to register custom actors.
-      DebuggerServer.registerActors({ root: true, browser: true, tab: false });
+      // TODO: the comment and implementation are out of sync here. See Bug 1420134.
+      DebuggerServer.registerAllActors();
 
       this._client = new DebuggerClient(DebuggerServer.connectPipe());
       // A local TabTarget will never perform chrome debugging.
       this._chrome = false;
     } else if (this._form.isWebExtension &&
           this.client.mainRoot.traits.webExtensionAddonConnect) {
       // The addonActor form is related to a WebExtensionParentActor instance,
       // which isn't a tab actor on its own, it is an actor living in the parent process
--- a/devtools/client/framework/test/browser_target_from_url.js
+++ b/devtools/client/framework/test/browser_target_from_url.js
@@ -76,17 +76,17 @@ add_task(function* () {
 });
 
 function* setupDebuggerServer(websocket) {
   info("Create a separate loader instance for the DebuggerServer.");
   let loader = new DevToolsLoader();
   let { DebuggerServer } = loader.require("devtools/server/main");
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let listener = DebuggerServer.createListener();
   ok(listener, "Socket listener created");
   // Pass -1 to automatically choose an available port
   listener.portOrPath = -1;
   listener.webSocket = websocket;
   yield listener.open();
--- a/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
+++ b/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js
@@ -58,20 +58,18 @@ function runTools(target) {
 
     yield toolbox.destroy();
   });
 }
 
 function getClient() {
   let deferred = defer();
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let transport = DebuggerServer.connectPipe();
   let client = new DebuggerClient(transport);
 
   return client.connect().then(() => client);
 }
 
 function getTarget(client) {
--- a/devtools/client/framework/test/browser_two_tabs.js
+++ b/devtools/client/framework/test/browser_two_tabs.js
@@ -15,20 +15,18 @@ const TAB_URL_2 = "data:text/html;charse
 
 var gClient;
 var gTab1, gTab2;
 var gTabActor1, gTabActor2;
 
 function test() {
   waitForExplicitFinish();
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   openTabs();
 }
 
 function openTabs() {
   // Open two tabs, select the second
   addTab(TAB_URL_1).then(tab1 => {
     gTab1 = tab1;
--- a/devtools/client/framework/test/head.js
+++ b/devtools/client/framework/test/head.js
@@ -23,20 +23,18 @@ function toggleAllTools(state) {
   }
 }
 
 function getChromeActors(callback)
 {
   let { DebuggerServer } = require("devtools/server/main");
   let { DebuggerClient } = require("devtools/shared/client/debugger-client");
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect()
     .then(() => client.getProcess())
     .then(response => {
       callback(client, response.form);
     });
--- a/devtools/client/framework/toolbox-init.js
+++ b/devtools/client/framework/toolbox-init.js
@@ -65,20 +65,18 @@ if (url.search.length > 1) {
       // Need to use a xray to have attributes and behavior expected by
       // devtools codebase
       iframe = XPCNativeWrapper(iframe);
 
       // Fake a xul:tab object as we don't have one.
       // linkedBrowser is the only one attribute being queried by client.getTab
       let tab = { linkedBrowser: iframe };
 
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-        DebuggerServer.addBrowserActors();
-      }
+      DebuggerServer.init();
+      DebuggerServer.registerAllActors();
       let client = new DebuggerClient(DebuggerServer.connectPipe());
 
       yield client.connect();
       // Creates a target for a given browser iframe.
       let response = yield client.getTab({ tab });
       let form = response.tab;
       target = yield TargetFactory.forRemoteTab({client, form, chrome: false});
     } else {
--- a/devtools/client/inspector/animation/components/AnimationListContainer.js
+++ b/devtools/client/inspector/animation/components/AnimationListContainer.js
@@ -1,16 +1,18 @@
 /* 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/. */
 
 "use strict";
 
-const { createFactory, DOM: dom, PropTypes, PureComponent } =
+const { createFactory, PureComponent } =
   require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
 const AnimationList = createFactory(require("./AnimationList"));
 const AnimationListHeader = createFactory(require("./AnimationListHeader"));
 
 class AnimationListContainer extends PureComponent {
   static get propTypes() {
     return {
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
--- a/devtools/client/inspector/animation/components/AnimationListHeader.js
+++ b/devtools/client/inspector/animation/components/AnimationListHeader.js
@@ -1,16 +1,18 @@
 /* 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/. */
 
 "use strict";
 
-const { createFactory, DOM: dom, PropTypes, PureComponent } =
+const { createFactory, PureComponent } =
   require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
 const AnimationTimelineTickList = createFactory(require("./AnimationTimelineTickList"));
 
 class AnimationListHeader extends PureComponent {
   static get propTypes() {
     return {
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
     };
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickItem.js
+++ b/devtools/client/inspector/animation/components/AnimationTimelineTickItem.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { DOM: dom, PropTypes, PureComponent } =
-  require("devtools/client/shared/vendor/react");
+const { PureComponent } = require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
 class AnimationTimeTickItem extends PureComponent {
   static get propTypes() {
     return {
       position: PropTypes.number.isRequired,
       timeTickLabel: PropTypes.string.isRequired,
     };
   }
--- a/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
+++ b/devtools/client/inspector/animation/components/AnimationTimelineTickList.js
@@ -1,16 +1,18 @@
 /* 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/. */
 
 "use strict";
 
-const { createFactory, DOM: dom, PropTypes, PureComponent } =
+const { createFactory, PureComponent } =
   require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
 const AnimationTimelineTickItem = createFactory(require("./AnimationTimelineTickItem"));
 
 const TimeScale = require("../utils/timescale");
 const { findOptimalTimeInterval } = require("../utils/utils");
 
--- a/devtools/client/inspector/changes/components/ChangesApp.js
+++ b/devtools/client/inspector/changes/components/ChangesApp.js
@@ -1,18 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {
-  DOM: dom,
-  PureComponent,
-} = require("devtools/client/shared/vendor/react");
+const { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 class ChangesApp extends PureComponent {
   static get propTypes() {
     return {};
   }
 
   render() {
--- a/devtools/client/inspector/events/components/EventsApp.js
+++ b/devtools/client/inspector/events/components/EventsApp.js
@@ -1,18 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {
-  DOM: dom,
-  PureComponent,
-} = require("devtools/client/shared/vendor/react");
+const { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 class EventsApp extends PureComponent {
   static get propTypes() {
     return {};
   }
 
   render() {
--- a/devtools/client/inspector/test/browser_inspector_addSidebarTab.js
+++ b/devtools/client/inspector/test/browser_inspector_addSidebarTab.js
@@ -10,51 +10,51 @@ const CONTENT_TEXT = "Hello World!";
 
 /**
  * Verify InspectorPanel.addSidebarTab() API that can be consumed
  * by DevTools extensions as well as DevTools code base.
  */
 add_task(function* () {
   let { inspector } = yield openInspectorForURL(TEST_URI);
 
-  const React = inspector.React;
+  const { Component, createFactory } = inspector.React;
   const dom = require("devtools/client/shared/vendor/react-dom-factories");
   const { div } = dom;
 
   info("Adding custom panel.");
 
   // Define custom side-panel.
-  let tabPanel = React.createFactory(React.createClass({
-    displayName: "myTabPanel",
-    render: function () {
+  class myTabPanel extends Component {
+    render() {
       return (
         div({className: "my-tab-panel"},
           CONTENT_TEXT
         )
       );
     }
-  }));
+  }
+  let tabPanel = createFactory(myTabPanel);
 
   // Append custom panel (tab) into the Inspector panel and
   // make sure it's selected by default (the last arg = true).
   inspector.addSidebarTab("myPanel", "My Panel", tabPanel, true);
   is(inspector.sidebar.getCurrentTabID(), "myPanel",
      "My Panel is selected by default");
 
   // Define another custom side-panel.
-  tabPanel = React.createFactory(React.createClass({
-    displayName: "myTabPanel2",
-    render: function () {
+  class myTabPanel2 extends Component {
+    render() {
       return (
         div({className: "my-tab-panel2"},
           "Another Content"
         )
       );
     }
-  }));
+  }
+  tabPanel = createFactory(myTabPanel2);
 
   // Append second panel, but don't select it by default.
   inspector.addSidebarTab("myPanel", "My Panel", tabPanel, false);
   is(inspector.sidebar.getCurrentTabID(), "myPanel",
      "My Panel is selected by default");
 
   // Check the the panel content is properly rendered.
   let tabPanelNode = inspector.panelDoc.querySelector(".my-tab-panel");
--- a/devtools/client/netmonitor/src/components/StackTracePanel.js
+++ b/devtools/client/netmonitor/src/components/StackTracePanel.js
@@ -1,51 +1,91 @@
 /* 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/. */
 
 "use strict";
 
-const { createFactory } = require("devtools/client/shared/vendor/react");
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { div } = dom;
 
 // Components
 const StackTrace = createFactory(require("devtools/client/shared/components/StackTrace"));
 
 /**
  * This component represents a side panel responsible for
  * rendering stack-trace info for selected request.
  */
-function StackTracePanel({
-  connector,
-  openLink,
-  request,
-  sourceMapService,
-}) {
-  let { stacktrace } = request.cause;
+class StackTracePanel extends Component {
+  static get propTypes() {
+    return {
+      connector: PropTypes.object.isRequired,
+      request: PropTypes.object.isRequired,
+      sourceMapService: PropTypes.object,
+      openLink: PropTypes.func,
+    };
+  }
+
+  /**
+   * `componentDidMount` is called when opening the StackTracePanel
+   * for the first time
+   */
+  componentDidMount() {
+    this.maybeFetchStackTrace(this.props);
+  }
+
+  /**
+   * `componentWillReceiveProps` is the only method called when
+   * switching between two requests while this panel is displayed.
+   */
+  componentWillReceiveProps(nextProps) {
+    this.maybeFetchStackTrace(nextProps);
+  }
 
-  return (
-    div({ className: "panel-container" },
-      StackTrace({
-        stacktrace,
-        onViewSourceInDebugger: ({ url, line }) => {
-          return connector.viewSourceInDebugger(url, line);
-        },
-        sourceMapService,
-        openLink,
-      }),
-    )
-  );
+  /**
+   * When switching to another request, lazily fetch stack-trace
+   * from the backend. This Panel will first be empty and then
+   * display the content.
+   */
+  maybeFetchStackTrace(props) {
+    // Fetch stack trace only if it's available and not yet
+    // on the client.
+    if (!props.request.stacktrace &&
+      props.request.cause.stacktraceAvailable) {
+      // This method will set `props.request.stacktrace`
+      // asynchronously and force another render.
+      props.connector.requestData(props.request.id, "stackTrace");
+    }
+  }
+
+  // Rendering
+
+  render() {
+    let {
+      connector,
+      openLink,
+      request,
+      sourceMapService,
+    } = this.props;
+
+    let {
+      stacktrace = []
+    } = request;
+
+    return (
+      div({ className: "panel-container" },
+        StackTrace({
+          stacktrace,
+          onViewSourceInDebugger: ({ url, line }) => {
+            return connector.viewSourceInDebugger(url, line);
+          },
+          sourceMapService,
+          openLink,
+        }),
+      )
+    );
+  }
 }
 
-StackTracePanel.displayName = "StackTracePanel";
-
-StackTracePanel.propTypes = {
-  connector: PropTypes.object.isRequired,
-  request: PropTypes.object.isRequired,
-  sourceMapService: PropTypes.object,
-  openLink: PropTypes.func,
-};
-
 module.exports = StackTracePanel;
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -79,17 +79,17 @@ function TabboxPanel({
         ResponsePanel({ request, openLink, connector }),
       ),
       TabPanel({
         id: PANELS.TIMINGS,
         title: TIMINGS_TITLE,
       },
         TimingsPanel({ request }),
       ),
-      request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 &&
+      request.cause && request.cause.stacktraceAvailable &&
       TabPanel({
         id: PANELS.STACK_TRACE,
         title: STACK_TRACE_TITLE,
       },
         StackTracePanel({ connector, openLink, request, sourceMapService }),
       ),
       request.securityState && request.securityState !== "insecure" &&
       TabPanel({
--- a/devtools/client/netmonitor/src/connector/firefox-connector.js
+++ b/devtools/client/netmonitor/src/connector/firefox-connector.js
@@ -78,16 +78,17 @@ class FirefoxConnector {
     this.removeListeners();
 
     this.tabTarget.off("will-navigate");
 
     this.tabTarget = null;
     this.webConsoleClient = null;
     this.timelineFront = null;
     this.dataProvider = null;
+    this.panel = null;
   }
 
   pause() {
     this.removeListeners();
   }
 
   resume() {
     this.addListeners();
@@ -124,21 +125,25 @@ class FirefoxConnector {
   }
 
   navigate() {
     if (this.dataProvider.isPayloadQueueEmpty()) {
       this.onReloaded();
       return;
     }
     let listener = () => {
-      if (!this.dataProvider.isPayloadQueueEmpty()) {
+      if (this.dataProvider && !this.dataProvider.isPayloadQueueEmpty()) {
         return;
       }
       window.off(EVENTS.PAYLOAD_READY, listener);
-      this.onReloaded();
+      // Netmonitor may already be destroyed,
+      // so do not try to notify the listeners
+      if (this.dataProvider) {
+        this.onReloaded();
+      }
     };
     window.on(EVENTS.PAYLOAD_READY, listener);
   }
 
   onReloaded() {
     if (this.panel) {
       this.panel.emit("reloaded");
     }
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -61,16 +61,22 @@ class FirefoxDataProvider {
     if (this.actions.addRequest) {
       await this.actions.addRequest(id, {
         // Convert the received date/time string to a unix timestamp.
         startedMillis: Date.parse(startedDateTime),
         method,
         url,
         isXHR,
         cause,
+
+        // Compatibility code to support Firefox 58 and earlier that always
+        // send stack-trace immediately on networkEvent message.
+        // FF59+ supports fetching the traces lazily via requestData.
+        stacktrace: cause.stacktrace,
+
         fromCache,
         fromServiceWorker},
         true,
       );
     }
 
     emit(EVENTS.REQUEST_ADDED, id);
   }
@@ -656,16 +662,29 @@ class FirefoxDataProvider {
    */
   onEventTimings(response) {
     return this.updateRequest(response.from, {
       eventTimings: response
     }).then(() => {
       emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
     });
   }
+
+  /**
+   * Handles information received for a "stackTrace" packet.
+   *
+   * @param {object} response the message received from the server.
+   */
+  async onStackTrace(response) {
+    let payload = await this.updateRequest(response.from, {
+      stacktrace: response.stacktrace
+    });
+    emit(EVENTS.RECEIVED_EVENT_STACKTRACE, response.from);
+    return payload.stacktrace;
+  }
 }
 
 /**
  * Guard 'emit' to avoid exception in non-window environment.
  */
 function emit(type, data) {
   if (typeof window != "undefined") {
     window.emit(type, data);
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -88,16 +88,19 @@ const EVENTS = {
   UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
   RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
 
   // When response content begins, updates and finishes receiving.
   STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
   UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
   RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
 
+  // When stack-trace finishes receiving.
+  RECEIVED_EVENT_STACKTRACE: "NetMonitor:NetworkEventUpdated:StackTrace",
+
   // Fired once the connection is established
   CONNECTED: "connected",
 
   // When request payload (HTTP details data) are fetched from the backend.
   PAYLOAD_READY: "NetMonitor:PayloadReady",
 };
 
 const UPDATE_PROPS = [
@@ -121,16 +124,17 @@ const UPDATE_PROPS = [
   "requestHeadersFromUploadStream",
   "requestCookies",
   "requestPostData",
   "responseHeaders",
   "responseCookies",
   "responseContent",
   "responseContentAvailable",
   "formDataSections",
+  "stacktrace",
 ];
 
 const PANELS = {
   COOKIES: "cookies",
   HEADERS: "headers",
   PARAMS: "params",
   RESPONSE: "response",
   SECURITY: "security",
--- a/devtools/client/netmonitor/test/browser_net_cause.js
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -84,46 +84,52 @@ add_task(function* () {
   // the initNetMonitor function clears the network request list after the
   // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
   // and only then load the real thing from CAUSE_URL - we want to catch
   // all the requests the page is making, not only the XHRs.
   // We can't use about:blank here, because initNetMonitor checks that the
   // page has actually made at least one request.
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
-  let { document, store, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire, connector } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   tab.linkedBrowser.loadURI(CAUSE_URL);
   yield wait;
 
+  // Fetch stack-trace data from the backend and wait till
+  // all packets are received.
+  let requests = getSortedRequests(store.getState());
+  yield Promise.all(requests.map(requestItem =>
+    connector.requestData(requestItem.id, "stackTrace")));
+
   is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
     "All the page events should be recorded.");
 
   EXPECTED_REQUESTS.forEach((spec, i) => {
     let { method, url, causeType, causeUri, stack } = spec;
 
     let requestItem = getSortedRequests(store.getState()).get(i);
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       requestItem,
       method,
       url,
       { cause: { type: causeType, loadingDocumentUri: causeUri } }
     );
 
-    let { stacktrace } = requestItem.cause;
+    let stacktrace = requestItem.stacktrace;
     let stackLen = stacktrace ? stacktrace.length : 0;
 
     if (stack) {
       ok(stacktrace, `Request #${i} has a stacktrace`);
       ok(stackLen > 0,
         `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
 
       // if "stack" is array, check the details about the top stack frames
--- a/devtools/client/netmonitor/test/browser_net_cause_redirect.js
+++ b/devtools/client/netmonitor/test/browser_net_cause_redirect.js
@@ -14,34 +14,40 @@ add_task(function* () {
     { status: 302, hasStack: true },
     // Serves HTTPS, sets the Strict-Transport-Security header, no stack
     { status: 200, hasStack: false },
     // Second request to HTTP redirects to HTTPS internally
     { status: 200, hasStack: true },
   ];
 
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
-  let { store, windowRequire } = monitor.panelWin;
+  let { store, windowRequire, connector } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   yield performRequests(2, HSTS_SJS);
   yield wait;
 
+  // Fetch stack-trace data from the backend and wait till
+  // all packets are received.
+  let requests = getSortedRequests(store.getState());
+  yield Promise.all(requests.map(requestItem =>
+    connector.requestData(requestItem.id, "stackTrace")));
+
   EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
     let item = getSortedRequests(store.getState()).get(i);
 
     is(item.status, status, `Request #${i} has the expected status`);
 
-    let { stacktrace } = item.cause;
+    let { stacktrace } = item;
     let stackLen = stacktrace ? stacktrace.length : 0;
 
     if (hasStack) {
       ok(stacktrace, `Request #${i} has a stacktrace`);
       ok(stackLen > 0, `Request #${i} has a stacktrace with ${stackLen} items`);
     } else {
       is(stackLen, 0, `Request #${i} has an empty stacktrace`);
     }
--- a/devtools/client/netmonitor/test/browser_net_frame.js
+++ b/devtools/client/netmonitor/test/browser_net_frame.js
@@ -153,32 +153,38 @@ add_task(function* () {
   // the initNetMonitor function clears the network request list after the
   // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
   // and only then load the real thing from TOP_URL - we want to catch
   // all the requests the page is making, not only the XHRs.
   // We can't use about:blank here, because initNetMonitor checks that the
   // page has actually made at least one request.
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
-  let { document, store, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire, connector } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   tab.linkedBrowser.loadURI(TOP_URL, null, null);
 
   yield waitForNetworkEvents(monitor, REQUEST_COUNT);
 
   is(store.getState().requests.requests.size, REQUEST_COUNT,
     "All the page events should be recorded.");
 
+  // Fetch stack-trace data from the backend and wait till
+  // all packets are received.
+  let requests = getSortedRequests(store.getState());
+  yield Promise.all(requests.map(requestItem =>
+    connector.requestData(requestItem.id, "stackTrace")));
+
   // While there is a defined order for requests in each document separately, the requests
   // from different documents may interleave in various ways that change per test run, so
   // there is not a single order when considering all the requests together.
   let currentTop = 0;
   let currentSub = 0;
   for (let i = 0; i < REQUEST_COUNT; i++) {
     let requestItem = getSortedRequests(store.getState()).get(i);
 
@@ -196,17 +202,17 @@ add_task(function* () {
       document,
       getDisplayedRequests(store.getState()),
       requestItem,
       method,
       url,
       { cause: { type: causeType, loadingDocumentUri: causeUri } }
     );
 
-    let { stacktrace } = requestItem.cause;
+    let { stacktrace } = requestItem;
     let stackLen = stacktrace ? stacktrace.length : 0;
 
     if (stack) {
       ok(stacktrace, `Request #${i} has a stacktrace`);
       ok(stackLen > 0,
         `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
 
       // if "stack" is array, check the details about the top stack frames
--- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js
+++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js
@@ -11,17 +11,17 @@
 const URL = EXAMPLE_URL.replace("http:", "https:");
 
 const TEST_URL = URL + "service-workers/status-codes.html";
 
 add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(TEST_URL, true);
   info("Starting test... ");
 
-  let { document, store, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire, connector } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
@@ -47,16 +47,22 @@ add_task(function* () {
 
   info("Performing requests...");
   let wait = waitForNetworkEvents(monitor, REQUEST_DATA.length);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
+  // Fetch stack-trace data from the backend and wait till
+  // all packets are received.
+  let requests = getSortedRequests(store.getState());
+  yield Promise.all(requests.map(requestItem =>
+    connector.requestData(requestItem.id, "stackTrace")));
+
   let requestItems = document.querySelectorAll(".request-list-item");
   for (let requestItem of requestItems) {
     requestItem.scrollIntoView();
     let requestsListStatus = requestItem.querySelector(".requests-list-status");
     EventUtils.sendMouseEvent({ type: "mouseover" }, requestsListStatus);
     yield waitUntil(() => requestsListStatus.title);
   }
 
@@ -69,17 +75,17 @@ add_task(function* () {
       document,
       getDisplayedRequests(store.getState()),
       item,
       request.method,
       request.uri,
       request.details
     );
 
-    let { stacktrace } = item.cause;
+    let { stacktrace } = item;
     let stackLen = stacktrace ? stacktrace.length : 0;
 
     ok(stacktrace, `Request #${index} has a stacktrace`);
     ok(stackLen >= request.stackFunctions.length,
       `Request #${index} has a stacktrace with enough (${stackLen}) items`);
 
     request.stackFunctions.forEach((functionName, j) => {
       is(stacktrace[j].functionName, functionName,
--- a/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
+++ b/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
@@ -18,17 +18,17 @@ add_task(async function () {
   store.dispatch(Actions.batchEnable(false));
 
   let waitForContentRequests = waitForNetworkEvents(monitor, 0, 2);
   await ContentTask.spawn(tab.linkedBrowser, {},
     () => content.wrappedJSObject.performRequests());
   await waitForContentRequests;
 
   info("Clicking stack-trace tab and waiting for stack-trace panel to open");
-  let wait = waitForDOM(document, "#stack-trace-panel");
+  let wait = waitForDOM(document, "#stack-trace-panel .frame-link", 4);
   // Click on the first request
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelector(".request-list-item"));
   // Open the stack-trace tab for that request
   document.getElementById("stack-trace-tab").click();
   await wait;
 
   let frameLinkNode = document.querySelector(".frame-link");
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -425,20 +425,18 @@ ResponsiveUI.prototype = {
     }
 
     this.destroyed = true;
 
     return true;
   }),
 
   connectToServer: Task.async(function* () {
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     this.client = new DebuggerClient(DebuggerServer.connectPipe());
     yield this.client.connect();
     let { tab } = yield this.client.getTab();
     this.emulationFront = EmulationFront(this.client, tab);
   }),
 
   handleEvent(event) {
     let { browserWindow, tab } = this;
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -2181,20 +2181,18 @@ ScratchpadWindow.prototype = Heritage.ex
   /**
    * Attach to this window.
    *
    * @return Promise
    *         The promise for the target for this window.
    */
   _attach: function SW__attach()
   {
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
 
     let client = new DebuggerClient(DebuggerServer.connectPipe());
     return client.connect()
       .then(() => client.getProcess())
       .then(aResponse => {
         return { form: aResponse.form, client: client };
       });
--- a/devtools/client/shadereditor/test/head.js
+++ b/devtools/client/shadereditor/test/head.js
@@ -221,20 +221,18 @@ function navigate(aTarget, aUrl, aWaitFo
 function reload(aTarget, aWaitForTargetEvent = "navigate") {
   executeSoon(() => aTarget.activeTab.reload());
   return once(aTarget, aWaitForTargetEvent);
 }
 
 function initBackend(aUrl) {
   info("Initializing a shader editor front.");
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   return Task.spawn(function* () {
     let tab = yield addTab(aUrl);
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
 
     let front = new WebGLFront(target.client, target.form);
--- a/devtools/client/shared/components/Tree.js
+++ b/devtools/client/shared/components/Tree.js
@@ -49,23 +49,23 @@ const NUMBER_OF_OFFSCREEN_ITEMS = 1;
  *       label: String,
  *       parent: Item or null,
  *       children: Array of child items,
  *       expanded: bool,
  *     }
  *
  * Here is how we could render that data with this component:
  *
- *     const MyTree = createClass({
- *       displayName: "MyTree",
- *
- *       propTypes: {
+ *     class MyTree extends Component {
+ *       static get propTypes() {
  *         // The root item of the tree, with the form described above.
- *         root: PropTypes.object.isRequired
- *       },
+ *         return {
+ *           root: PropTypes.object.isRequired
+ *         };
+ *       }
  *
  *       render() {
  *         return Tree({
  *           itemHeight: 20, // px
  *
  *           getRoots: () => [this.props.root],
  *
  *           getParent: item => item.parent,
@@ -91,17 +91,17 @@ const NUMBER_OF_OFFSCREEN_ITEMS = 1;
  *               dom.span({ className: "my-tree-item-label" }, item.label)
  *             );
  *           },
  *
  *           onExpand: item => dispatchExpandActionToRedux(item),
  *           onCollapse: item => dispatchCollapseActionToRedux(item),
  *         });
  *       }
- *     });
+ *     }
  */
 class Tree extends Component {
   static get propTypes() {
     return {
       // Required props
 
       // A function to get an item's parent, or null if it is a root.
       //
--- a/devtools/client/shared/test/test-actor-registry.js
+++ b/devtools/client/shared/test/test-actor-registry.js
@@ -61,20 +61,18 @@
   // Sometimes, we need the test actor before opening or without a toolbox then just
   // create a front for the given `tab`
   exports.getTestActorWithoutToolbox = Task.async(function* (tab) {
     let { DebuggerServer } = require("devtools/server/main");
     let { DebuggerClient } = require("devtools/shared/client/debugger-client");
 
     // We need to spawn a client instance,
     // but for that we have to first ensure a server is running
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     let client = new DebuggerClient(DebuggerServer.connectPipe());
 
     yield client.connect();
 
     // We also need to make sure the test actor is registered on the server.
     yield exports.registerTestActor(client);
 
     return getTestActor(client, tab);
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -156,20 +156,18 @@ function loadFrameScripts() {
 
 /**
  * Adds a new tab, and instantiate a WebAudiFront object.
  * This requires calling removeTab before the test ends.
  */
 function initBackend(aUrl) {
   info("Initializing a web audio editor front.");
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   return Task.spawn(function* () {
     let tab = yield addTab(aUrl);
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
 
     let front = new WebAudioFront(target.client, target.form);
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -183,24 +183,21 @@ HUD_SERVICE.prototype =
     }
 
     this._browserConsoleDefer = defer();
 
     function connect()
     {
       let deferred = defer();
 
-      if (!DebuggerServer.initialized) {
-        DebuggerServer.init();
-      }
-
       // Ensure that the root actor and the tab actors have been registered on the DebuggerServer,
       // so that the Browser Console can retrieve the console actors.
       // (See Bug 1416105 for rationale).
-      DebuggerServer.registerActors({ root: true, browser: false, tab: true });
+      DebuggerServer.init();
+      DebuggerServer.registerActors({ root: true, tab: true });
 
       DebuggerServer.allowChromeProcess = true;
 
       let client = new DebuggerClient(DebuggerServer.connectPipe());
       return client.connect()
         .then(() => client.getProcess())
         .then(aResponse => {
           // Use a TabActor in order to ensure calling `attach` to the ChromeActor
--- a/devtools/client/webconsole/new-console-output/actions/ui.js
+++ b/devtools/client/webconsole/new-console-output/actions/ui.js
@@ -20,27 +20,27 @@ const {
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 function filterBarToggle(show) {
   return (dispatch, getState) => {
     dispatch({
       type: FILTER_BAR_TOGGLE,
     });
     const uiState = getAllUi(getState());
-    Services.prefs.setBoolPref(PREFS.UI.FILTER_BAR, uiState.get("filterBarVisible"));
+    Services.prefs.setBoolPref(PREFS.UI.FILTER_BAR, uiState.filterBarVisible);
   };
 }
 
 function persistToggle(show) {
   return (dispatch, getState) => {
     dispatch({
       type: PERSIST_TOGGLE,
     });
     const uiState = getAllUi(getState());
-    Services.prefs.setBoolPref(PREFS.UI.PERSIST, uiState.get("persistLogs"));
+    Services.prefs.setBoolPref(PREFS.UI.PERSIST, uiState.persistLogs);
   };
 }
 
 function timestampsToggle(visible) {
   return {
     type: TIMESTAMPS_TOGGLE,
     visible,
   };
--- a/devtools/client/webconsole/new-console-output/components/GripMessageBody.js
+++ b/devtools/client/webconsole/new-console-output/components/GripMessageBody.js
@@ -32,16 +32,17 @@ GripMessageBody.propTypes = {
   grip: PropTypes.oneOfType([
     PropTypes.string,
     PropTypes.number,
     PropTypes.object,
   ]).isRequired,
   serviceContainer: PropTypes.shape({
     createElement: PropTypes.func.isRequired,
     hudProxy: PropTypes.object.isRequired,
+    onViewSourceInDebugger: PropTypes.func.isRequired,
   }),
   userProvidedStyle: PropTypes.string,
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
   type: PropTypes.string,
   helperType: PropTypes.string,
 };
 
@@ -97,16 +98,17 @@ function GripMessageBody(props) {
     createObjectClient: object =>
       new ObjectClient(serviceContainer.hudProxy.client, object),
     releaseActor: actor => {
       if (!actor || !serviceContainer.hudProxy.releaseActor) {
         return;
       }
       serviceContainer.hudProxy.releaseActor(actor);
     },
+    onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
     openLink: serviceContainer.openLink,
   };
 
   if (typeof grip === "string" || (grip && grip.type === "longString")) {
     Object.assign(objectInspectorProps, {
       useQuotes,
       escapeWhitespace,
       style: styleObject
--- a/devtools/client/webconsole/new-console-output/reducers/ui.js
+++ b/devtools/client/webconsole/new-console-output/reducers/ui.js
@@ -8,45 +8,44 @@
 const {
   FILTER_BAR_TOGGLE,
   INITIALIZE,
   PERSIST_TOGGLE,
   SELECT_NETWORK_MESSAGE_TAB,
   SIDEBAR_TOGGLE,
   TIMESTAMPS_TOGGLE,
 } = require("devtools/client/webconsole/new-console-output/constants");
-const Immutable = require("devtools/client/shared/vendor/immutable");
 
 const {
   PANELS,
 } = require("devtools/client/netmonitor/src/constants");
 
-const UiState = Immutable.Record({
+const UiState = (overrides) => Object.freeze(Object.assign({
   filterBarVisible: false,
   initialized: false,
   networkMessageActiveTabId: PANELS.HEADERS,
   persistLogs: false,
   sidebarVisible: false,
   timestampsVisible: true,
-});
+}, overrides));
 
-function ui(state = new UiState(), action) {
+function ui(state = UiState(), action) {
   switch (action.type) {
     case FILTER_BAR_TOGGLE:
-      return state.set("filterBarVisible", !state.filterBarVisible);
+      return Object.assign({}, state, {filterBarVisible: !state.filterBarVisible});
     case PERSIST_TOGGLE:
-      return state.set("persistLogs", !state.persistLogs);
+      return Object.assign({}, state, {persistLogs: !state.persistLogs});
     case TIMESTAMPS_TOGGLE:
-      return state.set("timestampsVisible", action.visible);
+      return Object.assign({}, state, {timestampsVisible: action.visible});
     case SELECT_NETWORK_MESSAGE_TAB:
-      return state.set("networkMessageActiveTabId", action.id);
+      return Object.assign({}, state, {networkMessageActiveTabId: action.id});
     case SIDEBAR_TOGGLE:
-      return state.set("sidebarVisible", !state.sidebarVisible);
+      return Object.assign({}, state, {sidebarVisible: !state.sidebarVisible});
     case INITIALIZE:
-      return state.set("initialized", true);
+      return Object.assign({}, state, {initialized: true});
   }
 
   return state;
 }
 
 module.exports = {
   UiState,
   ui,
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -51,17 +51,17 @@ function configureStore(hud, options = {
       warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
       info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
       debug: Services.prefs.getBoolPref(PREFS.FILTER.DEBUG),
       log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
       css: Services.prefs.getBoolPref(PREFS.FILTER.CSS),
       net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
       netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR),
     }),
-    ui: new UiState({
+    ui: UiState({
       filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
       networkMessageActiveTabId: "headers",
       persistLogs: Services.prefs.getBoolPref(PREFS.UI.PERSIST),
     })
   };
 
   return createStore(
     createRootReducer(),
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
@@ -40,39 +40,17 @@ stubPreparedMessages.set("GET request", 
     "scheme": "http",
     "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "isLocal": null
   },
   "method": "GET",
   "cause": {
     "type": "img",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-    "stacktrace": [
-      {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-        "lineNumber": 3,
-        "columnNumber": 1,
-        "functionName": "triggerPacket",
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js line 52 > eval",
-        "lineNumber": 8,
-        "columnNumber": 9,
-        "functionName": null,
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 53,
-        "columnNumber": 20,
-        "functionName": null,
-        "asyncCause": null
-      }
-    ]
+    "stacktraceAvailable": true
   }
 }));
 
 stubPreparedMessages.set("GET request update", new NetworkEventMessage({
   "id": "1",
   "actor": "server1.conn0.child1/netEvent30",
   "level": "log",
   "request": {
@@ -140,39 +118,17 @@ stubPreparedMessages.set("XHR GET reques
     "scheme": "http",
     "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "isLocal": null
   },
   "method": "GET",
   "cause": {
     "type": "xhr",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-    "stacktrace": [
-      {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-        "lineNumber": 4,
-        "columnNumber": 1,
-        "functionName": "triggerPacket",
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js line 52 > eval",
-        "lineNumber": 8,
-        "columnNumber": 9,
-        "functionName": null,
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 53,
-        "columnNumber": 20,
-        "functionName": null,
-        "asyncCause": null
-      }
-    ]
+    "stacktraceAvailable": true
   }
 }));
 
 stubPreparedMessages.set("XHR GET request update", new NetworkEventMessage({
   "id": "1",
   "actor": "server1.conn0.child1/netEvent31",
   "level": "log",
   "request": {
@@ -240,39 +196,17 @@ stubPreparedMessages.set("XHR POST reque
     "scheme": "http",
     "unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "isLocal": null
   },
   "method": "POST",
   "cause": {
     "type": "xhr",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-    "stacktrace": [
-      {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-        "lineNumber": 4,
-        "columnNumber": 1,
-        "functionName": "triggerPacket",
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js line 52 > eval",
-        "lineNumber": 8,
-        "columnNumber": 9,
-        "functionName": null,
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 53,
-        "columnNumber": 20,
-        "functionName": null,
-        "asyncCause": null
-      }
-    ]
+    "stacktraceAvailable": true
   }
 }));
 
 stubPreparedMessages.set("XHR POST request update", new NetworkEventMessage({
   "id": "1",
   "actor": "server1.conn0.child1/netEvent32",
   "level": "log",
   "request": {
@@ -324,39 +258,17 @@ stubPackets.set("GET request", {
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "GET"
   },
   "isXHR": false,
   "cause": {
     "type": "img",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-    "stacktrace": [
-      {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-        "lineNumber": 3,
-        "columnNumber": 1,
-        "functionName": "triggerPacket",
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js line 52 > eval",
-        "lineNumber": 8,
-        "columnNumber": 9,
-        "functionName": null,
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 53,
-        "columnNumber": 20,
-        "functionName": null,
-        "asyncCause": null
-      }
-    ]
+    "stacktraceAvailable": true
   },
   "response": {},
   "timings": {},
   "updates": [],
   "private": false,
   "from": "server1.conn0.child1/consoleActor2"
 });
 
@@ -397,39 +309,17 @@ stubPackets.set("XHR GET request", {
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "GET"
   },
   "isXHR": true,
   "cause": {
     "type": "xhr",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-    "stacktrace": [
-      {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-        "lineNumber": 4,
-        "columnNumber": 1,
-        "functionName": "triggerPacket",
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js line 52 > eval",
-        "lineNumber": 8,
-        "columnNumber": 9,
-        "functionName": null,
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 53,
-        "columnNumber": 20,
-        "functionName": null,
-        "asyncCause": null
-      }
-    ]
+    "stacktraceAvailable": true
   },
   "response": {},
   "timings": {},
   "updates": [],
   "private": false,
   "from": "server1.conn1.child1/consoleActor2"
 });
 
@@ -470,39 +360,17 @@ stubPackets.set("XHR POST request", {
   "request": {
     "url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
     "method": "POST"
   },
   "isXHR": true,
   "cause": {
     "type": "xhr",
     "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-    "stacktrace": [
-      {
-        "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
-        "lineNumber": 4,
-        "columnNumber": 1,
-        "functionName": "triggerPacket",
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js line 52 > eval",
-        "lineNumber": 8,
-        "columnNumber": 9,
-        "functionName": null,
-        "asyncCause": null
-      },
-      {
-        "filename": "resource://testing-common/content-task.js",
-        "lineNumber": 53,
-        "columnNumber": 20,
-        "functionName": null,
-        "asyncCause": null
-      }
-    ]
+    "stacktraceAvailable": true
   },
   "response": {},
   "timings": {},
   "updates": [],
   "private": false,
   "from": "server1.conn2.child1/consoleActor2"
 });
 
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -10,18 +10,18 @@ support-files =
   source-mapped.css
   source-mapped.css.map
   source-mapped.scss
   test_bug_1010953_cspro.html
   test_bug_1010953_cspro.html^headers^
   test_bug_1247459_violation.html
   test_bug_770099_violation.html
   test_bug_770099_violation.html^headers^
-  test_bug1045902_console_csp_ignore_reflected_xss_message.html
-  test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
+  test_console_csp_ignore_reflected_xss_message.html
+  test_console_csp_ignore_reflected_xss_message.html^headers^
   test_bug1092055_shouldwarn.html
   test_bug1092055_shouldwarn.js
   test_bug1092055_shouldwarn.js^headers^
   test_hpkp-invalid-headers.sjs
   test_hsts-invalid-headers.sjs
   test-autocomplete-in-stackframe.html
   test-batching.html
   test-bug_1050691_click_function_to_source.html
@@ -251,18 +251,17 @@ subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_webconsole_context_menu_copy_object.js]
 subsuite = clipboard
 [browser_webconsole_context_menu_open_url.js]
 [browser_webconsole_context_menu_store_as_global.js]
 [browser_webconsole_copy_link_location.js]
 skip-if = true #	Bug 1401944
 [browser_webconsole_csp_ignore_reflected_xss_message.js]
-skip-if = true # Bug 1408931
-# old console skip-if = (e10s && debug) || (e10s && os == 'win') # Bug 1221499 enabled these on windows
+skip-if = (e10s && debug) || (e10s && os == 'win') # Bug 1221499 enabled these on windows
 [browser_webconsole_cspro.js]
 skip-if = true # Bug 1408932
 # old console skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243967
 [browser_webconsole_ctrl_key_nav.js]
 skip-if = true # Bug 1408933
 # old console skip-if = os != "mac"
 [browser_webconsole_deactivateHUDForContext_unfocused_window.js]
 skip-if = true # Bug 1408934
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_csp_ignore_reflected_xss_message.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_csp_ignore_reflected_xss_message.js
@@ -1,52 +1,31 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that a file with an unsupported CSP directive ('reflected-xss filter')
-// displays the appropriate message to the console.
+// displays the appropriate message to the console. See Bug 1045902.
 
 "use strict";
 
 const EXPECTED_RESULT = "Not supporting directive \u2018reflected-xss\u2019. " +
                         "Directive and values will be ignored.";
-const TEST_FILE = "http://example.com/browser/devtools/client/webconsole/" +
-                  "test/test_bug1045902_console_csp_ignore_reflected_xss_" +
-                  "message.html";
-
-var hud = undefined;
-
-var TEST_URI = "data:text/html;charset=utf8,Web Console CSP ignoring " +
-               "reflected XSS (bug 1045902)";
+const TEST_FILE =
+  "http://example.com/browser/devtools/client/webconsole/new-console-output/test/" +
+  "mochitest/test_console_csp_ignore_reflected_xss_message.html";
 
-add_task(function* () {
-  let { browser } = yield loadTab(TEST_URI);
-
-  hud = yield openConsole();
+const TEST_URI =
+  "data:text/html;charset=utf8,Web Console CSP ignoring reflected XSS (bug 1045902)";
 
-  yield loadDocument(browser);
-  yield testViolationMessage();
-
-  hud = null;
-});
+const cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+  .getService(Ci.nsICacheStorageService);
 
-function loadDocument(browser) {
-  hud.jsterm.clearOutput();
-  browser.loadURI(TEST_FILE);
-  return BrowserTestUtils.browserLoaded(browser);
-}
-
-function testViolationMessage() {
-  let aOutputNode = hud.outputNode;
+add_task(async function () {
+  const hud = await openNewTabAndConsole(TEST_URI);
+  await loadDocument(TEST_FILE);
 
-  return waitForSuccess({
-    name: "Confirming that CSP logs messages to the console when " +
-          "\u2018reflected-xss\u2019 directive is used!",
-    validator: function () {
-      console.log(aOutputNode.textContent);
-      let success = false;
-      success = aOutputNode.textContent.indexOf(EXPECTED_RESULT) > -1;
-      return success;
-    }
-  });
-}
+  await waitFor(() => findMessage(hud, EXPECTED_RESULT, ".message.warn"));
+  ok(true, `CSP logs displayed in console when using "reflected-xss" directive`);
+
+  cache.clear();
+});
rename from devtools/client/webconsole/new-console-output/test/mochitest/test_bug1045902_console_csp_ignore_reflected_xss_message.html
rename to devtools/client/webconsole/new-console-output/test/mochitest/test_console_csp_ignore_reflected_xss_message.html
rename from devtools/client/webconsole/new-console-output/test/mochitest/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
rename to devtools/client/webconsole/new-console-output/test/mochitest/test_console_csp_ignore_reflected_xss_message.html^headers^
--- a/devtools/client/webide/modules/runtimes.js
+++ b/devtools/client/webide/modules/runtimes.js
@@ -432,20 +432,18 @@ WiFiRuntime.prototype = {
 };
 
 // For testing use only
 exports._WiFiRuntime = WiFiRuntime;
 
 var gLocalRuntime = {
   type: RuntimeTypes.LOCAL,
   connect: function (connection) {
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
     connection.host = null; // Force Pipe transport
     connection.port = null;
     connection.connect();
     return promise.resolve();
   },
   get id() {
     return "local";
--- a/devtools/client/webide/test/browser_tabs.js
+++ b/devtools/client/webide/test/browser_tabs.js
@@ -8,17 +8,17 @@ function test() {
   waitForExplicitFinish();
   requestCompleteLog();
 
   Task.spawn(function* () {
     // Since we test the connections set below, destroy the server in case it
     // was left open.
     DebuggerServer.destroy();
     DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
 
     let tab = yield addTab(TEST_URI);
 
     let win = yield openWebIDE();
     let docProject = getProjectDocument(win);
     let docRuntime = getRuntimeDocument(win);
 
     yield connectToLocal(win, docRuntime);
--- a/devtools/client/webide/test/test_autoconnect_runtime.html
+++ b/devtools/client/webide/test/test_autoconnect_runtime.html
@@ -14,20 +14,18 @@
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function*() {
-          if (!DebuggerServer.initialized) {
-            DebuggerServer.init();
-            DebuggerServer.addBrowserActors();
-          }
+          DebuggerServer.init();
+          DebuggerServer.registerAllActors();
 
           let win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
 
           let fakeRuntime = {
             type: "USB",
             connect: function(connection) {
               is(connection, win.AppManager.connection, "connection is valid");
--- a/devtools/client/webide/test/test_autoselect_project.html
+++ b/devtools/client/webide/test/test_autoselect_project.html
@@ -14,20 +14,18 @@
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          if (!DebuggerServer.initialized) {
-            DebuggerServer.init();
-            DebuggerServer.addBrowserActors();
-          }
+          DebuggerServer.init();
+          DebuggerServer.registerAllActors();
 
           let win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
 
           let panelNode = docRuntime.querySelector("#runtime-panel");
           let items = panelNode.querySelectorAll(".runtime-panel-item-other");
           is(items.length, 2, "Found 2 runtime buttons");
--- a/devtools/client/webide/test/test_device_preferences.html
+++ b/devtools/client/webide/test/test_device_preferences.html
@@ -14,20 +14,18 @@
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          if (!DebuggerServer.initialized) {
-            DebuggerServer.init();
-            DebuggerServer.addBrowserActors();
-          }
+          DebuggerServer.init();
+          DebuggerServer.registerAllActors();
 
           let win = yield openWebIDE();
 
           let prefIframe = win.document.querySelector("#deck-panel-devicepreferences");
           let docRuntime = getRuntimeDocument(win);
 
           win.AppManager.update("runtime-list");
 
--- a/devtools/client/webide/test/test_device_runtime.html
+++ b/devtools/client/webide/test/test_device_runtime.html
@@ -14,20 +14,18 @@
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          if (!DebuggerServer.initialized) {
-            DebuggerServer.init();
-            DebuggerServer.addBrowserActors();
-          }
+          DebuggerServer.init();
+          DebuggerServer.registerAllActors();
 
           let win = yield openWebIDE();
 
           let detailsIframe = win.document.querySelector("#deck-panel-runtimedetails");
 
           yield connectToLocalRuntime(win);
 
           let details = win.document.querySelector("#cmd_showRuntimeDetails");
--- a/devtools/client/webide/test/test_runtime.html
+++ b/devtools/client/webide/test/test_runtime.html
@@ -34,20 +34,18 @@
           function isPlayActive() {
             return !win.document.querySelector("#cmd_play").hasAttribute("disabled");
           }
 
           function isStopActive() {
             return !win.document.querySelector("#cmd_stop").hasAttribute("disabled");
           }
 
-          if (!DebuggerServer.initialized) {
-            DebuggerServer.init();
-            DebuggerServer.addBrowserActors();
-          }
+          DebuggerServer.init();
+          DebuggerServer.registerAllActors();
 
           win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
           let winProject = getProjectWindow(win);
 
           let packagedAppLocation = getTestFilePath("app");
 
--- a/devtools/client/webide/test/test_toolbox.html
+++ b/devtools/client/webide/test/test_toolbox.html
@@ -26,20 +26,18 @@
               yield closeWebIDE(win);
             }
             DebuggerServer.destroy();
             yield removeAllProjects();
           });
         });
 
         Task.spawn(function*() {
-          if (!DebuggerServer.initialized) {
-            DebuggerServer.init();
-            DebuggerServer.addBrowserActors();
-          }
+          DebuggerServer.init();
+          DebuggerServer.registerAllActors();
 
           win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
 
           win.AppManager.update("runtime-list");
 
           let deferred = new Promise(resolve => {
--- a/devtools/docs/backend/actor-registration.md
+++ b/devtools/docs/backend/actor-registration.md
@@ -25,17 +25,17 @@ To register a global actor:
 ```
 DebuggerServer.registerModule("devtools/server/actors/addons", {
   prefix: "addons",
   constructor: "AddonsActor",
   type: { global: true }
 });
 ```
 
-If you are adding a new built-in devtools actor, you should be registering it using `DebuggerServer.registerModule` in `addBrowserActors` or `addTabActors` in `/devtools/server/main.js`.
+If you are adding a new built-in devtools actor, you should be registering it using `DebuggerServer.registerModule` in `_addBrowserActors` or `addTabActors` in `/devtools/server/main.js`.
 
 If you are adding a new actor from an add-on, you should call `DebuggerServer.registerModule` directly from your add-on code.
 
 ## A note about lazy registration
 
 The `DebuggerServer` loads and creates all of the actors lazily to keep the initial memory usage down (which is extremely important on lower end devices).
 
 It becomes especially important when debugging apps on b2g or pages with e10s when there are more than one process, because that's when we need to spawn a `DebuggerServer` per process (it may not be immediately obvious that the server in the main process is mostly only here for piping messages to the actors in the child process).
--- a/devtools/docs/backend/client-api.md
+++ b/devtools/docs/backend/client-api.md
@@ -7,20 +7,18 @@ DevTools has a client module that allows
 In order to communicate, a client and a server instance must be created and a protocol connection must be established. The connection can be either over a TCP socket or an nsIPipe. The `start` function displayed below establishes an nsIPipe-backed connection:
 
 ```javascript
 Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
 Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
 
 function start() {
   // Start the server.
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   // Listen to an nsIPipe
   let transport = DebuggerServer.connectPipe();
 
   // Start the client.
   client = new DebuggerClient(transport);
 
   // Attach listeners for client events.
@@ -36,20 +34,18 @@ function start() {
 If a TCP socket is required, the function should be split in two parts, a server-side and a client-side, like this:
 
 ```javascript
 Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
 Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
 
 function startServer() {
   // Start the server.
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   // For an nsIServerSocket we do this:
   DebuggerServer.openListener(2929); // A connection on port 2929.
 }
 
 async function startClient() {
   let transport = await DebuggerClient.socketConnect({ host: "localhost", port: 2929 });
 
@@ -161,20 +157,18 @@ Here is the source code for a complete d
 Components.utils.import("resource://gre/modules/devtools/dbg-server.jsm");
 Components.utils.import("resource://gre/modules/devtools/dbg-client.jsm");
 
 let client;
 let threadClient;
 
 function startDebugger() {
   // Start the server.
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
   // Listen to an nsIPipe
   let transport = DebuggerServer.connectPipe();
   // For an nsIServerSocket we do this:
   // DebuggerServer.openListener(port);
   // ...and this at the client:
   // let transport = debuggerSocketConnect(host, port);
 
   // Start the client.
--- a/devtools/docs/backend/protocol.js.md
+++ b/devtools/docs/backend/protocol.js.md
@@ -46,17 +46,17 @@ The actor implementation would go somewh
       sayHello: function () {
        return "hello";
       },
     });
 
     // You also need to export the actor class in your module for discovery.
     exports.HelloActor = HelloActor;
 
-To activate your actor, register it in the `addBrowserActors` method in `server/main.js`.
+To activate your actor, register it in the `_addBrowserActors` method in `server/main.js`.
 The registration code would look something like this:
 
     this.registerModule("devtools/server/actors/hello-world", {
       prefix: "hello",
       constructor: "HelloActor",
       type: { global: true }
     });
 
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1931,16 +1931,17 @@ function NetworkEventActor(webConsoleAct
 
   this._response = {
     headers: [],
     cookies: [],
     content: {},
   };
 
   this._timings = {};
+  this._stackTrace = {};
 
   // Keep track of LongStringActors owned by this NetworkEventActor.
   this._longStringActors = new Set();
 }
 
 NetworkEventActor.prototype =
 {
   _request: null,
@@ -2003,16 +2004,24 @@ NetworkEventActor.prototype =
    */
   init: function (networkEvent) {
     this._startedDateTime = networkEvent.startedDateTime;
     this._isXHR = networkEvent.isXHR;
     this._cause = networkEvent.cause;
     this._fromCache = networkEvent.fromCache;
     this._fromServiceWorker = networkEvent.fromServiceWorker;
 
+    // Stack trace info isn't sent automatically. The client
+    // needs to request it explicitly using getStackTrace
+    // packet.
+    this._stackTrace = networkEvent.cause.stacktrace;
+    delete networkEvent.cause.stacktrace;
+    networkEvent.cause.stacktraceAvailable =
+      !!(this._stackTrace && this._stackTrace.length);
+
     for (let prop of ["method", "url", "httpVersion", "headersSize"]) {
       this._request[prop] = networkEvent[prop];
     }
 
     this._discardRequestBody = networkEvent.discardRequestBody;
     this._discardResponseBody = networkEvent.discardResponseBody;
     this._private = networkEvent.private;
   },
@@ -2124,16 +2133,29 @@ NetworkEventActor.prototype =
     return {
       from: this.actorID,
       timings: this._timings,
       totalTime: this._totalTime,
       offsets: this._offsets
     };
   },
 
+  /**
+   * The "getStackTrace" packet type handler.
+   *
+   * @return object
+   *         The response packet - stack trace.
+   */
+  onGetStackTrace: function () {
+    return {
+      from: this.actorID,
+      stacktrace: this._stackTrace,
+    };
+  },
+
   /** ****************************************************************
    * Listeners for new network event data coming from NetworkMonitor.
    ******************************************************************/
 
   /**
    * Add network request headers.
    *
    * @param array headers
@@ -2372,9 +2394,10 @@ NetworkEventActor.prototype.requestTypes
   "getRequestHeaders": NetworkEventActor.prototype.onGetRequestHeaders,
   "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies,
   "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData,
   "getResponseHeaders": NetworkEventActor.prototype.onGetResponseHeaders,
   "getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies,
   "getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
   "getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
   "getSecurityInfo": NetworkEventActor.prototype.onGetSecurityInfo,
+  "getStackTrace": NetworkEventActor.prototype.onGetStackTrace,
 };
--- a/devtools/server/child.js
+++ b/devtools/server/child.js
@@ -13,23 +13,21 @@ try {
   (function () {
     const Cu = Components.utils;
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
     const DevToolsUtils = require("devtools/shared/DevToolsUtils");
     const { dumpn } = DevToolsUtils;
     const { DebuggerServer, ActorPool } = require("devtools/server/main");
 
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-    }
+    DebuggerServer.init();
     // We want a special server without any root actor and only tab actors.
     // We are going to spawn a ContentActor instance in the next few lines,
     // it is going to act like a root actor without being one.
-    DebuggerServer.registerActors({ root: false, browser: false, tab: true });
+    DebuggerServer.registerActors({ tab: true });
 
     let connections = new Map();
 
     let onConnect = DevToolsUtils.makeInfallible(function (msg) {
       removeMessageListener("debug:connect", onConnect);
 
       let mm = msg.target;
       let prefix = msg.data.prefix;
--- a/devtools/server/content-server.jsm
+++ b/devtools/server/content-server.jsm
@@ -24,23 +24,21 @@ function setupServer(mm) {
 
   // Init a custom, invisible DebuggerServer, in order to not pollute the
   // debugger with all devtools modules, nor break the debugger itself with
   // using it in the same process.
   gLoader = new DevToolsLoader();
   gLoader.invisibleToDebugger = true;
   let { DebuggerServer } = gLoader.require("devtools/server/main");
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-  }
+  DebuggerServer.init();
   // For browser content toolbox, we do need a regular root actor and all tab
   // actors, but don't need all the "browser actors" that are only useful when
   // debugging the parent process via the browser toolbox.
-  DebuggerServer.registerActors({ browser: false, root: true, tab: true });
+  DebuggerServer.registerActors({ root: true, tab: true });
 
   // Clean up things when the client disconnects
   mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
     mm.removeMessageListener("debug:content-process-destroy", onDestroy);
 
     Cu.unblockThreadedExecution();
 
     DebuggerServer.destroy();
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -244,52 +244,51 @@ var DebuggerServer = {
 
     if (!this.rootlessServer && !this.createRootActor) {
       throw new Error("Use DebuggerServer.addActors() to add a root actor " +
                       "implementation.");
     }
   },
 
   /**
-   * Register all type of actors. Only register the one that are not already
+   * Register different type of actors. Only register the one that are not already
    * registered.
    *
    * @param root boolean
    *        Registers the root actor from webbrowser module, which is used to
    *        connect to and fetch any other actor.
    * @param browser boolean
    *        Registers all the parent process actors useful for debugging the
    *        runtime itself, like preferences and addons actors.
    * @param tab boolean
    *        Registers all the tab actors like console, script, ... all useful
    *        for debugging a target context.
-   * @param windowType string
-   *        "windowtype" attribute of the main chrome windows. Used by some
-   *        actors to retrieve them.
    */
-  registerActors({ root = true, browser = true, tab = true,
-                   windowType = null }) {
-    if (windowType) {
-      this.chromeWindowType = windowType;
-    }
-
+  registerActors({ root, browser, tab }) {
     if (browser) {
-      this.addBrowserActors(this.chromeWindowType);
+      this._addBrowserActors();
     }
 
     if (root) {
       this.registerModule("devtools/server/actors/webbrowser");
     }
 
     if (tab) {
-      this.addTabActors();
+      this._addTabActors();
     }
   },
 
   /**
+   * Register all possible actors for this DebuggerServer.
+   */
+  registerAllActors() {
+    this.registerActors({ root: true, browser: true, tab: true });
+  },
+
+  /**
    * Load a subscript into the debugging global.
    *
    * @param url string A url that will be loaded as a subscript into the
    *        debugging global.  The user must load at least one script
    *        that implements a createRootActor() function to create the
    *        server's root actor.
    */
   addActors(url) {
@@ -412,40 +411,28 @@ var DebuggerServer = {
     delete gRegisteredModules[id];
   },
 
   /**
    * Install Firefox-specific actors.
    *
    * /!\ Be careful when adding a new actor, especially global actors.
    * Any new global actor will be exposed and returned by the root actor.
-   *
-   * That's the reason why tab actors aren't loaded on demand via
-   * restrictPrivileges=true, to prevent exposing them on b2g parent process's
-   * root actor.
    */
-  addBrowserActors(windowType = null, restrictPrivileges = false) {
-    if (windowType) {
-      this.chromeWindowType = windowType;
-    }
-    this.registerModule("devtools/server/actors/webbrowser");
-
-    if (!restrictPrivileges) {
-      this.addTabActors();
-      this.registerModule("devtools/server/actors/preference", {
-        prefix: "preference",
-        constructor: "PreferenceActor",
-        type: { global: true }
-      });
-      this.registerModule("devtools/server/actors/actor-registry", {
-        prefix: "actorRegistry",
-        constructor: "ActorRegistryActor",
-        type: { global: true }
-      });
-    }
+  _addBrowserActors() {
+    this.registerModule("devtools/server/actors/preference", {
+      prefix: "preference",
+      constructor: "PreferenceActor",
+      type: { global: true }
+    });
+    this.registerModule("devtools/server/actors/actor-registry", {
+      prefix: "actorRegistry",
+      constructor: "ActorRegistryActor",
+      type: { global: true }
+    });
     this.registerModule("devtools/server/actors/addons", {
       prefix: "addons",
       constructor: "AddonsActor",
       type: { global: true }
     });
     this.registerModule("devtools/server/actors/device", {
       prefix: "device",
       constructor: "DeviceActor",
@@ -456,17 +443,17 @@ var DebuggerServer = {
       constructor: "HeapSnapshotFileActor",
       type: { global: true }
     });
   },
 
   /**
    * Install tab actors.
    */
-  addTabActors() {
+  _addTabActors() {
     this.registerModule("devtools/server/actors/webconsole", {
       prefix: "console",
       constructor: "WebConsoleActor",
       type: { tab: true }
     });
     this.registerModule("devtools/server/actors/inspector", {
       prefix: "inspector",
       constructor: "InspectorActor",
--- a/devtools/server/tests/browser/browser_register_actor.js
+++ b/devtools/server/tests/browser/browser_register_actor.js
@@ -2,20 +2,18 @@
 
 var gClient;
 
 function test() {
   waitForExplicitFinish();
   let {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
   let actorURL = "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/hello-actor.js";
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect()
     .then(() => gClient.listTabs())
     .then(response => {
       let options = {
         prefix: "helloActor",
         constructor: "HelloActor",
--- a/devtools/server/tests/browser/head.js
+++ b/devtools/server/tests/browser/head.js
@@ -100,17 +100,17 @@ function initDebuggerServer() {
   try {
     // Sometimes debugger server does not get destroyed correctly by previous
     // tests.
     DebuggerServer.destroy();
   } catch (e) {
     info(`DebuggerServer destroy error: ${e}\n${e.stack}`);
   }
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 }
 
 /**
  * Connect a debugger client.
  * @param {DebuggerClient}
  * @return {Promise} Resolves to the selected tabActor form when the client is
  * connected.
  */
--- a/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/devtools/server/tests/mochitest/inspector-helpers.js
@@ -20,17 +20,17 @@ const {_documentWalker} = require("devto
 // Always log packets when running tests.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(function () {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
 if (!DebuggerServer.initialized) {
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
   SimpleTest.registerCleanupFunction(function () {
     DebuggerServer.destroy();
   });
 }
 
 var gAttachCleanups = [];
 
 SimpleTest.registerCleanupFunction(function () {
--- a/devtools/server/tests/mochitest/memory-helpers.js
+++ b/devtools/server/tests/mochitest/memory-helpers.js
@@ -15,17 +15,17 @@ const { MemoryFront } = require("devtool
 // Always log packets when running tests.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(function () {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
 function startServerAndGetSelectedTabMemory() {
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
   let client = new DebuggerClient(DebuggerServer.connectPipe());
 
   return client.connect()
     .then(() => client.listTabs())
     .then(response => {
       let form = response.tabs[response.selected];
       let memory = MemoryFront(client, form, response);
 
--- a/devtools/server/tests/mochitest/test_connectToChild.html
+++ b/devtools/server/tests/mochitest/test_connectToChild.html
@@ -44,19 +44,17 @@ function runTests() {
   // Register a test actor in the child process so that we can know if and when
   // this fake actor is destroyed.
   mm.loadFrameScript("data:text/javascript,new " + function FrameScriptScope() {
     /* eslint-disable no-shadow */
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     const { DebuggerServer } = require("devtools/server/main");
     /* eslint-enable no-shadow */
 
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-    }
+    DebuggerServer.init();
 
     function TestActor() {
       dump("instanciate test actor\n");
     }
     TestActor.prototype = {
       actorPrefix: "test",
 
       destroy: function () {
@@ -68,21 +66,19 @@ function runTests() {
     };
     TestActor.prototype.requestTypes = {
       "hello": TestActor.prototype.hello
     };
     DebuggerServer.addTabActor(TestActor, "testActor");
   }, false);
 
   // Instantiate a minimal server
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-  }
+  DebuggerServer.init();
   if (!DebuggerServer.createRootActor) {
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
   }
 
   function firstClient() {
     // Fake a first connection to an iframe
     let transport = DebuggerServer.connectPipe();
     let conn = transport._serverConnection;
     let client = new DebuggerClient(transport);
     DebuggerServer.connectToChild(conn, iframe).then(actor => {
--- a/devtools/server/tests/mochitest/test_connection-manager.html
+++ b/devtools/server/tests/mochitest/test_connection-manager.html
@@ -18,20 +18,18 @@ window.onload = function () {
   SimpleTest.waitForExplicitFinish();
 
   const Cu = Components.utils;
 
   const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
   const {DebuggerServer} = require("devtools/server/main");
   const Services = require("Services");
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   const {
     ConnectionManager,
     Connection
   } = require("devtools/shared/client/connection-manager");
 
   let orgCount = ConnectionManager.connections.length;
 
--- a/devtools/server/tests/mochitest/test_device.html
+++ b/devtools/server/tests/mochitest/test_device.html
@@ -22,20 +22,18 @@ window.onload = function () {
   const {DebuggerClient} = require("devtools/shared/client/debugger-client");
   const {DebuggerServer} = require("devtools/server/main");
   const Services = require("Services");
 
   SimpleTest.waitForExplicitFinish();
 
   const {getDeviceFront} = require("devtools/shared/fronts/device");
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function onConnect() {
     client.listTabs(function onListTabs(response) {
       let d = getDeviceFront(client, response);
 
       let desc;
       let appInfo = Services.appinfo;
--- a/devtools/server/tests/mochitest/test_framerate_01.html
+++ b/devtools/server/tests/mochitest/test_framerate_01.html
@@ -62,20 +62,18 @@ window.onload = function () {
 
       frameCount = 0;
       prevTime = currTime;
     }
 
     return timeline;
   }
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function onConnect() {
     client.listTabs(function onListTabs(response) {
       let form = response.tabs[response.selected];
       let front = FramerateFront(client, form);
 
       window.setTimeout(() => {
--- a/devtools/server/tests/mochitest/test_framerate_02.html
+++ b/devtools/server/tests/mochitest/test_framerate_02.html
@@ -62,20 +62,18 @@ window.onload = function () {
 
       frameCount = 0;
       prevTime = currTime;
     }
 
     return timeline;
   }
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function onConnect() {
     client.listTabs(function onListTabs(response) {
       let form = response.tabs[response.selected];
       let front = FramerateFront(client, form);
 
       front.stopRecording().then(rawData => {
--- a/devtools/server/tests/mochitest/test_framerate_03.html
+++ b/devtools/server/tests/mochitest/test_framerate_03.html
@@ -30,20 +30,18 @@ window.onload = function () {
 
   SimpleTest.waitForExplicitFinish();
 
   const {FramerateFront} = require("devtools/shared/fronts/framerate");
   const START_TICK = 2000;
   const STOP_TICK = 3000;
   const TOTAL_TIME = 5000;
 
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function onConnect() {
     client.listTabs(function onListTabs(response) {
       let form = response.tabs[response.selected];
       let front = FramerateFront(client, form);
 
       front.startRecording().then(() => {
--- a/devtools/server/tests/mochitest/test_framerate_05.html
+++ b/devtools/server/tests/mochitest/test_framerate_05.html
@@ -28,17 +28,17 @@ window.onload = function () {
     Services.prefs.clearUserPref("devtools.debugger.log");
   });
 
   SimpleTest.waitForExplicitFinish();
 
   const {FramerateFront} = require("devtools/shared/fronts/framerate");
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function onConnect() {
     client.listTabs(function onListTabs(response) {
       let form = response.tabs[response.selected];
       let front = FramerateFront(client, form);
 
       front.startRecording().then(() => {
--- a/devtools/server/tests/mochitest/test_getProcess.html
+++ b/devtools/server/tests/mochitest/test_getProcess.html
@@ -33,22 +33,20 @@ window.onload = function () {
       // Allows creating a branch new process when creation the iframe
       ["dom.ipc.processCount", 10],
     ]
   }, runTests);
 };
 
 function runTests() {
   // Instantiate a minimal server
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-  }
+  DebuggerServer.init();
   DebuggerServer.allowChromeProcess = true;
   if (!DebuggerServer.createRootActor) {
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
   }
 
   let client, iframe, processCount;
 
   function connect() {
     // Fake a first connection to the content process
     let transport = DebuggerServer.connectPipe();
     client = new DebuggerClient(transport);
--- a/devtools/server/tests/mochitest/test_preference.html
+++ b/devtools/server/tests/mochitest/test_preference.html
@@ -23,17 +23,17 @@ function runTests() {
   let {DebuggerServer} = require("devtools/server/main");
   let Services = require("Services");
 
   SimpleTest.waitForExplicitFinish();
 
   let {getPreferenceFront} = require("devtools/shared/fronts/preference");
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function onConnect() {
     client.listTabs(function onListTabs(response) {
       let p = getPreferenceFront(client, response);
 
       let prefs = {};
 
--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
+++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
@@ -37,21 +37,19 @@ window.onload = function () {
 
 function runTests() {
   // Create a minimal iframe with a message manager
   let iframe = document.createElement("iframe");
   iframe.mozbrowser = true;
   document.body.appendChild(iframe);
 
   // Instantiate a minimal server
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-  }
+  DebuggerServer.init();
   if (!DebuggerServer.createRootActor) {
-    DebuggerServer.addBrowserActors();
+    DebuggerServer.registerAllActors();
   }
 
   // Fake a connection to an iframe
   let transport = DebuggerServer.connectPipe();
   let conn = transport._serverConnection;
   let client = new DebuggerClient(transport);
 
   // Wait for a response from setupInChild
--- a/devtools/server/tests/mochitest/webconsole-helpers.js
+++ b/devtools/server/tests/mochitest/webconsole-helpers.js
@@ -12,17 +12,17 @@ const Services = require("Services");
 // Always log packets when running tests.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(function () {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
 if (!DebuggerServer.initialized) {
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
   SimpleTest.registerCleanupFunction(function () {
     DebuggerServer.destroy();
   });
 }
 
 /**
  * Open a tab, load the url, find the tab with the debugger server,
  * and attach the console to it.
--- a/devtools/server/tests/mochitest/webextension-helpers.js
+++ b/devtools/server/tests/mochitest/webextension-helpers.js
@@ -17,17 +17,17 @@ const {flushJarCache} = require("resourc
 const {Services} = require("resource://gre/modules/Services.jsm");
 
 loader.lazyImporter(this, "ExtensionParent", "resource://gre/modules/ExtensionParent.jsm");
 loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 // Initialize a minimal DebuggerServer and connect to the webextension addon actor.
 if (!DebuggerServer.initialized) {
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
   SimpleTest.registerCleanupFunction(function () {
     DebuggerServer.destroy();
   });
 }
 
 SimpleTest.registerCleanupFunction(function () {
   const {hiddenXULWindow} = ExtensionParent.DebugUtils;
   const debugBrowserMapSize = ExtensionParent.DebugUtils.debugBrowserPromises.size;
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -415,17 +415,17 @@ function initTestDebuggerServer(server =
 }
 
 /**
  * Initialize the testing debugger server with a tab whose title is |title|.
  */
 function startTestDebuggerServer(title, server = DebuggerServer) {
   initTestDebuggerServer(server);
   addTestGlobal(title);
-  DebuggerServer.addTabActors();
+  DebuggerServer.registerActors({ tab: true });
 
   let transport = DebuggerServer.connectPipe();
   let client = new DebuggerClient(transport);
 
   return connect(client).then(() => client);
 }
 
 function finishClient(client) {
@@ -433,20 +433,18 @@ function finishClient(client) {
     DebuggerServer.destroy();
     do_test_finished();
   });
 }
 
 // Create a server, connect to it and fetch tab actors for the parent process;
 // pass |callback| the debugger client and tab actor form with all actor IDs.
 function get_chrome_actors(callback) {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect()
     .then(() => client.getProcess())
     .then(response => {
       callback(client, response.form);
     });
--- a/devtools/server/tests/unit/test_actor-registry-actor.js
+++ b/devtools/server/tests/unit/test_actor-registry-actor.js
@@ -13,17 +13,17 @@ var gActorFront;
 var gOldPref;
 
 const { ActorRegistryFront } = require("devtools/shared/fronts/actor-registry");
 
 function run_test() {
   gOldPref = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
   Services.prefs.setBoolPref("devtools.debugger.forbid-certified-apps", false);
   initTestDebuggerServer();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect().then(getRegistry);
   do_test_pending();
 }
 
 function getRegistry() {
   gClient.listTabs((response) => {
     gRegistryFront = ActorRegistryFront(gClient, response);
--- a/devtools/server/tests/unit/test_add_actors.js
+++ b/devtools/server/tests/unit/test_add_actors.js
@@ -13,17 +13,17 @@ var gActors;
  * in order to add actors after initialization but rather can add actors anytime
  * regardless of the object's state.
  */
 function run_test() {
   DebuggerServer.addActors("resource://test/pre_init_global_actors.js");
   DebuggerServer.addActors("resource://test/pre_init_tab_actors.js");
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   DebuggerServer.addActors("resource://test/post_init_global_actors.js");
   DebuggerServer.addActors("resource://test/post_init_tab_actors.js");
 
   add_test(init);
   add_test(test_pre_init_global_actor);
   add_test(test_pre_init_tab_actor);
   add_test(test_post_init_global_actor);
--- a/devtools/server/tests/unit/test_client_request.js
+++ b/devtools/server/tests/unit/test_client_request.js
@@ -25,17 +25,17 @@ TestActor.prototype.requestTypes = {
   "hello": TestActor.prototype.hello,
   "error": TestActor.prototype.error
 };
 
 function run_test() {
   DebuggerServer.addGlobalActor(TestActor);
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   add_test(init);
   add_test(test_client_request_callback);
   add_test(test_client_request_promise);
   add_test(test_client_request_promise_error);
   add_test(test_client_request_event_emitter);
   add_test(test_close_client_while_sending_requests);
   add_test(test_client_request_after_close);
--- a/devtools/server/tests/unit/test_eventlooplag_actor.js
+++ b/devtools/server/tests/unit/test_eventlooplag_actor.js
@@ -6,17 +6,17 @@
  */
 
 "use strict";
 
 function run_test() {
   let {EventLoopLagFront} = require("devtools/shared/fronts/eventlooplag");
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   // As seen in EventTracer.cpp
   let threshold = 20;
   let interval = 10;
 
   let front;
   let client = new DebuggerClient(DebuggerServer.connectPipe());
 
--- a/devtools/server/tests/unit/test_registerClient.js
+++ b/devtools/server/tests/unit/test_registerClient.js
@@ -49,17 +49,17 @@ TestClient.prototype = {
     onDone();
   }
 };
 
 function run_test() {
   DebuggerServer.addGlobalActor(TestActor);
 
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   add_test(init);
   add_test(test_client_events);
   add_test(close_client);
   run_next_test();
 }
 
 function init() {
--- a/devtools/server/tests/unit/test_register_actor.js
+++ b/devtools/server/tests/unit/test_register_actor.js
@@ -13,17 +13,17 @@ function check_actors(expect) {
               DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor2"));
   do_check_eq(expect,
               DebuggerServer.globalActorFactories.hasOwnProperty("registeredActor1"));
 }
 
 function run_test() {
   // Allow incoming connections.
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   add_test(test_deprecated_api);
   add_test(test_lazy_api);
   add_test(cleanup);
   run_next_test();
 }
 
 function test_deprecated_api() {
--- a/devtools/server/tests/unit/test_requestTypes.js
+++ b/devtools/server/tests/unit/test_requestTypes.js
@@ -18,17 +18,17 @@ function test_requestTypes_request(clien
     client.close().then(() => {
       do_test_finished();
     });
   });
 }
 
 function run_test() {
   DebuggerServer.init();
-  DebuggerServer.addBrowserActors();
+  DebuggerServer.registerAllActors();
 
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   client.connect().then(function () {
     test_requestTypes_request(client);
   });
 
   do_test_pending();
 }
--- a/devtools/shared/gcli/commands/listen.js
+++ b/devtools/shared/gcli/commands/listen.js
@@ -22,17 +22,17 @@ XPCOMUtils.defineLazyGetter(this, "debug
   // devtools.  This allows us to safely use the tools against even the
   // actors and DebuggingServer itself, especially since we can mark
   // serverLoader as invisible to the debugger (unlike the usual loader
   // settings).
   let serverLoader = new DevToolsLoader();
   serverLoader.invisibleToDebugger = true;
   let { DebuggerServer: debuggerServer } = serverLoader.require("devtools/server/main");
   debuggerServer.init();
-  debuggerServer.addBrowserActors();
+  debuggerServer.registerAllActors();
   debuggerServer.allowChromeProcess = !l10n.hiddenByChromePref();
   return debuggerServer;
 });
 
 exports.items = [
   {
     item: "command",
     runAt: "client",
--- a/devtools/shared/security/tests/chrome/test_websocket-transport.html
+++ b/devtools/shared/security/tests/chrome/test_websocket-transport.html
@@ -21,20 +21,18 @@ window.onload = function () {
   Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false);
 
   SimpleTest.registerCleanupFunction(() => {
     Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
     Services.prefs.clearUserPref("devtools.debugger.prompt-connection");
   });
 
   add_task(function* () {
-    if (!DebuggerServer.initialized) {
-      DebuggerServer.init();
-      DebuggerServer.addBrowserActors();
-    }
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
 
     is(DebuggerServer.listeningSockets, 0, "0 listening sockets");
 
     let listener = DebuggerServer.createListener();
     ok(listener, "Socket listener created");
     listener.portOrPath = -1;
     listener.webSocket = true;
     yield listener.open();
--- a/devtools/shared/webconsole/client.js
+++ b/devtools/shared/webconsole/client.js
@@ -562,16 +562,34 @@ WebConsoleClient.prototype = {
     let packet = {
       to: actor,
       type: "getSecurityInfo",
     };
     return this._client.request(packet, onResponse);
   },
 
   /**
+   * Retrieve the stack-trace information for the given NetworkEventActor.
+   *
+   * @param string actor
+   *        The NetworkEventActor ID.
+   * @param function onResponse
+   *        The function invoked when the stack-trace is received.
+   * @return request
+   *         Request object that implements both Promise and EventEmitter interfaces
+   */
+  getStackTrace: function (actor, onResponse) {
+    let packet = {
+      to: actor,
+      type: "getStackTrace",
+    };
+    return this._client.request(packet, onResponse);
+  },
+
+  /**
    * Send a HTTP request with the given data.
    *
    * @param string data
    *        The details of the HTTP request.
    * @param function onResponse
    *        The function invoked when the response is received.
    * @return request
    *         Request object that implements both Promise and EventEmitter interfaces
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -20,20 +20,18 @@ const {DebuggerClient} = require("devtoo
 const ObjectClient = require("devtools/shared/client/object-client");
 const Services = require("Services");
 
 function initCommon() {
   // Services.prefs.setBoolPref("devtools.debugger.log", true);
 }
 
 function initDebuggerServer() {
-  if (!DebuggerServer.initialized) {
-    DebuggerServer.init();
-    DebuggerServer.addBrowserActors();
-  }
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
   DebuggerServer.allowChromeProcess = true;
 }
 
 function connectToDebugger() {
   initCommon();
   initDebuggerServer();
 
   let transport = DebuggerServer.connectPipe();
rename from devtools/client/framework/about-devtools-toolbox.js
rename to devtools/shim/aboutdevtoolstoolbox-registration.js
--- a/devtools/client/framework/about-devtools-toolbox.js
+++ b/devtools/shim/aboutdevtoolstoolbox-registration.js
@@ -2,62 +2,42 @@
  * 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/. */
 
 "use strict";
 
 // Register about:devtools-toolbox which allows to open a devtools toolbox
 // in a Firefox tab or a custom html iframe in browser.html
 
-const { Ci, Cu, Cm, components } = require("chrome");
-const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
-const Services = require("Services");
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
 const { nsIAboutModule } = Ci;
 
-function AboutURL() {}
+function AboutDevtoolsToolbox() {}
 
-AboutURL.prototype = {
+AboutDevtoolsToolbox.prototype = {
   uri: Services.io.newURI("chrome://devtools/content/framework/toolbox.xul"),
   classDescription: "about:devtools-toolbox",
-  classID: components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
+  classID: Components.ID("11342911-3135-45a8-8d71-737a2b0ad469"),
   contractID: "@mozilla.org/network/protocol/about;1?what=devtools-toolbox",
 
   QueryInterface: XPCOMUtils.generateQI([nsIAboutModule]),
 
-  newChannel: function (aURI, aLoadInfo) {
-    let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, aLoadInfo);
+  newChannel: function (uri, loadInfo) {
+    let chan = Services.io.newChannelFromURIWithLoadInfo(this.uri, loadInfo);
     chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
-    chan.originalURI = aURI;
     return chan;
   },
 
-  getURIFlags: function (aURI) {
+  getURIFlags: function (uri) {
     return nsIAboutModule.ALLOW_SCRIPT |
            nsIAboutModule.ENABLE_INDEXED_DB |
            nsIAboutModule.HIDE_FROM_ABOUTABOUT;
   }
 };
 
-AboutURL.createInstance = function (outer, iid) {
-  if (outer) {
-    throw Cr.NS_ERROR_NO_AGGREGATION;
-  }
-  return new AboutURL();
-};
-
-exports.register = function () {
-  if (registrar.isCIDRegistered(AboutURL.prototype.classID)) {
-    console.error("Trying to register " + AboutURL.prototype.classDescription +
-                  " more than once.");
-  } else {
-    registrar.registerFactory(AboutURL.prototype.classID,
-                       AboutURL.prototype.classDescription,
-                       AboutURL.prototype.contractID,
-                       AboutURL);
-  }
-};
-
-exports.unregister = function () {
-  if (registrar.isCIDRegistered(AboutURL.prototype.classID)) {
-    registrar.unregisterFactory(AboutURL.prototype.classID, AboutURL);
-  }
-};
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
+  AboutDevtoolsToolbox
+]);
new file mode 100644
--- /dev/null
+++ b/devtools/shim/aboutdevtoolstoolbox.manifest
@@ -0,0 +1,2 @@
+component {11342911-3135-45a8-8d71-737a2b0ad469} aboutdevtoolstoolbox-registration.js
+contract @mozilla.org/network/protocol/about;1?what=devtools-toolbox {11342911-3135-45a8-8d71-737a2b0ad469}
\ No newline at end of file
--- a/devtools/shim/devtools-startup.js
+++ b/devtools/shim/devtools-startup.js
@@ -764,17 +764,17 @@ DevToolsStartup.prototype = {
       // actors and DebuggingServer itself, especially since we can mark
       // serverLoader as invisible to the debugger (unlike the usual loader
       // settings).
       let serverLoader = new DevToolsLoader();
       serverLoader.invisibleToDebugger = true;
       let { DebuggerServer: debuggerServer } =
         serverLoader.require("devtools/server/main");
       debuggerServer.init();
-      debuggerServer.addBrowserActors();
+      debuggerServer.registerAllActors();
       debuggerServer.allowChromeProcess = true;
 
       let listener = debuggerServer.createListener();
       listener.portOrPath = portOrPath;
       listener.webSocket = webSocket;
       listener.open();
       dump("Started debugger server on " + portOrPath + "\n");
     } catch (e) {
--- a/devtools/shim/moz.build
+++ b/devtools/shim/moz.build
@@ -10,16 +10,18 @@ JS_PREFERENCE_PP_FILES += [
     'devtools-startup-prefs.js',
 ]
 
 # Register the about:debugging page only for 'addon' and 'all' builds.
 if CONFIG['MOZ_DEVTOOLS'] != 'server':
     EXTRA_COMPONENTS += [
         'aboutdebugging-registration.js',
         'aboutdebugging.manifest',
+        'aboutdevtoolstoolbox-registration.js',
+        'aboutdevtoolstoolbox.manifest',
         'devtools-startup.js',
         'devtools-startup.manifest',
     ]
 
     DIRS += [
       'aboutdevtools',
       'locales',
     ]
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2813,47 +2813,42 @@ Element::SetAttrAndNotify(int32_t aNames
   if (aComposedDocument || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     RefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
     }
   }
 
   if (CustomElementRegistry::IsCustomElementEnabled()) {
-    if (CustomElementData* data = GetCustomElementData()) {
-      if (CustomElementDefinition* definition =
-            nsContentUtils::GetElementDefinitionIfObservingAttr(this,
-                                                                data->GetCustomElementType(),
-                                                                aName)) {
-        MOZ_ASSERT(data->mState == CustomElementData::State::eCustom,
-                   "AttributeChanged callback should fire only if "
-                   "custom element state is custom");
-        RefPtr<nsAtom> oldValueAtom;
-        if (oldValue) {
-          oldValueAtom = oldValue->GetAsAtom();
-        } else {
-          // If there is no old value, get the value of the uninitialized
-          // attribute that was swapped with aParsedValue.
-          oldValueAtom = aParsedValue.GetAsAtom();
-        }
-        RefPtr<nsAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
-        nsAutoString ns;
-        nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
-
-        LifecycleCallbackArgs args = {
-          nsDependentAtomString(aName),
-          aModType == nsIDOMMutationEvent::ADDITION ?
-            VoidString() : nsDependentAtomString(oldValueAtom),
-          nsDependentAtomString(newValueAtom),
-          (ns.IsEmpty() ? VoidString() : ns)
-        };
-
-        nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
-          this, &args, nullptr, definition);
+    CustomElementDefinition* definition = GetCustomElementDefinition();
+    // Only custom element which is in `custom` state could get the
+    // CustomElementDefinition.
+    if (definition && definition->IsInObservedAttributeList(aName)) {
+      RefPtr<nsAtom> oldValueAtom;
+      if (oldValue) {
+        oldValueAtom = oldValue->GetAsAtom();
+      } else {
+        // If there is no old value, get the value of the uninitialized
+        // attribute that was swapped with aParsedValue.
+        oldValueAtom = aParsedValue.GetAsAtom();
       }
+      RefPtr<nsAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
+      nsAutoString ns;
+      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
+
+      LifecycleCallbackArgs args = {
+        nsDependentAtomString(aName),
+        aModType == nsIDOMMutationEvent::ADDITION ?
+          VoidString() : nsDependentAtomString(oldValueAtom),
+        nsDependentAtomString(newValueAtom),
+        (ns.IsEmpty() ? VoidString() : ns)
+      };
+
+      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+        this, &args, nullptr, definition);
     }
   }
 
   if (aCallAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
                       aSubjectPrincipal, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2986,16 +2981,45 @@ Element::PostIdMaybeChange(int32_t aName
   if (aValue && !aValue->IsEmptyString()) {
     SetHasID();
     AddToIdTable(aValue->GetAtomValue());
   } else {
     ClearHasID();
   }
 }
 
+nsresult
+Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
+                                const nsAttrValueOrString& aValue,
+                                bool aNotify)
+{
+  if (CustomElementRegistry::IsCustomElementEnabled()) {
+    // Only custom element which is in `custom` state could get the
+    // CustomElementDefinition.
+    CustomElementDefinition* definition = GetCustomElementDefinition();
+    if (definition && definition->IsInObservedAttributeList(aName)) {
+      nsAutoString ns;
+      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
+
+      nsAutoString value(aValue.String());
+      LifecycleCallbackArgs args = {
+        nsDependentAtomString(aName),
+        value,
+        value,
+        (ns.IsEmpty() ? VoidString() : ns)
+      };
+
+      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+        this, &args, nullptr, definition);
+    }
+  }
+
+  return NS_OK;
+}
+
 EventListenerManager*
 Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,
                                         bool* aDefer)
 {
   *aDefer = true;
   return GetOrCreateListenerManager();
 }
 
@@ -3121,38 +3145,33 @@ Element::UnsetAttr(int32_t aNameSpaceID,
   if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
     RefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
     }
   }
 
   if (CustomElementRegistry::IsCustomElementEnabled()) {
-    if (CustomElementData* data = GetCustomElementData()) {
-      if (CustomElementDefinition* definition =
-            nsContentUtils::GetElementDefinitionIfObservingAttr(this,
-                                                                data->GetCustomElementType(),
-                                                                aName)) {
-        MOZ_ASSERT(data->mState == CustomElementData::State::eCustom,
-                   "AttributeChanged callback should fire only if "
-                   "custom element state is custom");
-        nsAutoString ns;
-        nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
-
-        RefPtr<nsAtom> oldValueAtom = oldValue.GetAsAtom();
-        LifecycleCallbackArgs args = {
-          nsDependentAtomString(aName),
-          nsDependentAtomString(oldValueAtom),
-          VoidString(),
-          (ns.IsEmpty() ? VoidString() : ns)
-        };
-
-        nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
-          this, &args, nullptr, definition);
-      }
+    CustomElementDefinition* definition = GetCustomElementDefinition();
+    // Only custom element which is in `custom` state could get the
+    // CustomElementDefinition.
+    if (definition && definition->IsInObservedAttributeList(aName)) {
+      nsAutoString ns;
+      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
+
+      RefPtr<nsAtom> oldValueAtom = oldValue.GetAsAtom();
+      LifecycleCallbackArgs args = {
+        nsDependentAtomString(aName),
+        nsDependentAtomString(oldValueAtom),
+        VoidString(),
+        (ns.IsEmpty() ? VoidString() : ns)
+      };
+
+      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+        this, &args, nullptr, definition);
     }
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   UpdateState(aNotify);
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1705,20 +1705,17 @@ protected:
    * @param aValue the value it's being set to represented as either a string or
    *        a parsed nsAttrValue.
    * @param aNotify Whether we plan to notify document observers.
    */
   // Note that this is inlined so that when subclasses call it it gets
   // inlined.  Those calls don't go through a vtable.
   virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
                                           const nsAttrValueOrString& aValue,
-                                          bool aNotify)
-  {
-    return NS_OK;
-  }
+                                          bool aNotify);
 
   /**
    * Hook to allow subclasses to produce a different EventListenerManager if
    * needed for attachment of attribute-defined handlers
    */
   virtual EventListenerManager*
     GetEventListenerManagerForAttr(nsAtom* aAttrName, bool* aDefer);
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10121,33 +10121,16 @@ nsContentUtils::UnregisterUnresolvedElem
   RefPtr<CustomElementRegistry> registry(window->CustomElements());
   if (!registry) {
     return;
   }
 
   registry->UnregisterUnresolvedElement(aElement, typeAtom);
 }
 
-/* static */ CustomElementDefinition*
-nsContentUtils::GetElementDefinitionIfObservingAttr(Element* aCustomElement,
-                                                    nsAtom* aExtensionType,
-                                                    nsAtom* aAttrName)
-{
-  CustomElementDefinition* definition =
-    aCustomElement->GetCustomElementDefinition();
-
-  // Custom element not defined yet or attribute is not in the observed
-  // attribute list.
-  if (!definition || !definition->IsInObservedAttributeList(aAttrName)) {
-    return nullptr;
-  }
-
-  return definition;
-}
-
 /* static */ void
 nsContentUtils::EnqueueUpgradeReaction(Element* aElement,
                                        CustomElementDefinition* aDefinition)
 {
   MOZ_ASSERT(aElement);
 
   nsIDocument* doc = aElement->OwnerDoc();
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3027,21 +3027,16 @@ public:
     LookupCustomElementDefinition(nsIDocument* aDoc,
                                   const nsAString& aLocalName,
                                   uint32_t aNameSpaceID,
                                   nsAtom* aTypeAtom);
 
   static void RegisterUnresolvedElement(Element* aElement, nsAtom* aTypeName);
   static void UnregisterUnresolvedElement(Element* aElement);
 
-  static mozilla::dom::CustomElementDefinition*
-  GetElementDefinitionIfObservingAttr(Element* aCustomElement,
-                                      nsAtom* aExtensionType,
-                                      nsAtom* aAttrName);
-
   static void EnqueueUpgradeReaction(Element* aElement,
                                      mozilla::dom::CustomElementDefinition* aDefinition);
 
   static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                        Element* aCustomElement,
                                        mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
                                        mozilla::dom::LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs = nullptr,
                                        mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -229,58 +229,16 @@ public:
 
 private:
   // We keep the first parsed frame around for static info access, the
   // previously parsed frame for debugging and the currently parsed frame.
   Frame mFirstFrame;
   Frame mFrame;
 };
 
-
-// Return the AAC Profile Level Indication based upon sample rate and channels
-// Information based upon table 1.10 from ISO/IEC 14496-3:2005(E)
-static int8_t
-ProfileLevelIndication(const Frame& frame)
-{
-  const FrameHeader& header = frame.Header();
-  MOZ_ASSERT(header.IsValid());
-
-  if (!header.IsValid()) {
-    return 0;
-  }
-
-  const int channels = header.mChannels;
-  const int sampleRate = header.mSampleRate;
-
-  if (channels <= 2) {
-    if (sampleRate <= 24000) {
-      // AAC Profile  L1
-      return 0x28;
-    }
-    else if (sampleRate <= 48000) {
-      // AAC Profile  L2
-      return 0x29;
-    }
-  }
-  else if (channels <= 5) {
-    if (sampleRate <= 48000) {
-      // AAC Profile  L4
-      return 0x2A;
-    }
-    else if (sampleRate <= 96000) {
-      // AAC Profile  L5
-      return 0x2B;
-    }
-  }
-
-  // TODO: Should this be 0xFE for 'no audio profile specified'?
-  return 0;
-}
-
-
 // Initialize the AAC AudioSpecificConfig.
 // Only handles two-byte version for AAC-LC.
 static void
 InitAudioSpecificConfig(const Frame& frame,
                         MediaByteBuffer* aBuffer)
 {
   const FrameHeader& header = frame.Header();
   MOZ_ASSERT(header.IsValid());
@@ -401,25 +359,20 @@ ADTSTrackDemuxer::Init()
   mInfo->mChannels = mChannels;
   mInfo->mBitDepth = 16;
   mInfo->mDuration = Duration();
 
   // AAC Specific information
   mInfo->mMimeType = "audio/mp4a-latm";
 
   // Configure AAC codec-specific values.
-
-  // According to
-  // https://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx,
-  // wAudioProfileLevelIndication, which is passed mInfo->mProfile, is
-  // a value from Table 1.12 -- audioProfileLevelIndication values, ISO/IEC 14496-3.
-  mInfo->mProfile = ProfileLevelIndication(mParser->FirstFrame());
-  // For AAC, mExtendedProfile contains the audioObjectType from Table
-  // 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC
-  mInfo->mExtendedProfile = mParser->FirstFrame().Header().mObjectType;
+  // For AAC, mProfile and mExtendedProfile contain the audioObjectType from
+  // Table 1.3 -- Audio Profile definition, ISO/IEC 14496-3. Eg. 2 == AAC LC
+  mInfo->mProfile = mInfo->mExtendedProfile =
+    mParser->FirstFrame().Header().mObjectType;
   InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig);
 
   ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64
           "}",
           mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth,
           mInfo->mDuration.ToMicroseconds());
 
   return mSamplesPerSecond && mChannels;
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -170,9 +170,9 @@ 2. Sometimes autoland tip has changed en
    has an env var you can set to do this). In theory you can get the same
    result by resolving the conflict manually but Cargo.lock files are usually not
    trivial to merge by hand. If it's just the third_party/rust dir that has conflicts
    you can delete it and run |mach vendor rust| again to repopulate it.
 
 -------------------------------------------------------------------------------
 
 The version of WebRender currently in the tree is:
-81cfbcf0763205f25329adb9b2ff75d1cd56e3f1
+b7b07562fda338fcb2faff66ce01aafb6235fbcf
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -932,57 +932,37 @@ GLContextGLX::FindFBConfigForWindow(Disp
         NS_WARNING("[GLX] XGetWindowAttributes() failed");
         return false;
     }
     const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
 #ifdef DEBUG
     printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
 #endif
 
-    if (aWebRender) {
-        for (int i = 0; i < numConfigs; i++) {
-            int visid = X11None;
-            sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
-            if (!visid) {
-                continue;
-            }
-
+    for (int i = 0; i < numConfigs; i++) {
+        int visid = X11None;
+        sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
+        if (!visid) {
+            continue;
+        }
+        if (aWebRender || sGLXLibrary.IsATI()) {
             int depth;
             Visual* visual;
             FindVisualAndDepth(display, visid, &visual, &depth);
             if (depth == windowAttrs.depth &&
                 AreCompatibleVisuals(windowAttrs.visual, visual)) {
                 *out_config = cfgs[i];
                 *out_visid = visid;
                 return true;
             }
-        }
-    } else {
-        for (int i = 0; i < numConfigs; i++) {
-            int visid = X11None;
-            sGLXLibrary.fGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
-            if (!visid) {
-                continue;
-            }
-            if (sGLXLibrary.IsATI()) {
-                int depth;
-                Visual* visual;
-                FindVisualAndDepth(display, visid, &visual, &depth);
-                if (depth == windowAttrs.depth &&
-                    AreCompatibleVisuals(windowAttrs.visual, visual)) {
-                    *out_config = cfgs[i];
-                    *out_visid = visid;
-                    return true;
-                }
-            } else {
-                if (windowVisualID == static_cast<VisualID>(visid)) {
-                    *out_config = cfgs[i];
-                    *out_visid = visid;
-                    return true;
-                }
+        } else {
+            if (windowVisualID == static_cast<VisualID>(visid)) {
+                *out_config = cfgs[i];
+                *out_visid = visid;
+                return true;
             }
         }
     }
 
     NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
     return false;
 }
 
--- a/gfx/src/CompositorHitTestInfo.h
+++ b/gfx/src/CompositorHitTestInfo.h
@@ -12,17 +12,17 @@
 namespace mozilla {
 namespace gfx {
 
 // This set of flags is used to figure out what information a frame has
 // that is relevant to hit-testing in the compositor. The flags are
 // intentionally set up so that if all of them are 0 the item is effectively
 // invisible to hit-testing, and no information for this frame needs to be
 // sent to the compositor.
-enum class CompositorHitTestInfo : uint8_t {
+enum class CompositorHitTestInfo : uint16_t {
   // Shortcut for checking that none of the flags are set
   eInvisibleToHitTest = 0,
 
   // The frame participates in hit-testing
   eVisibleToHitTest = 1 << 0,
   // The frame requires main-thread handling for events
   eDispatchToContent = 1 << 1,
 
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -42,10 +42,10 @@ servo-glutin = "0.13"     # for the exam
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.3", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.4"
-core-graphics = "0.12.2"
+core-graphics = "0.12.3"
 core-text = { version = "8.0", default-features = false }
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -30,17 +30,17 @@ struct App {
 }
 
 impl Example for App {
     fn render(
         &mut self,
         _api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _resources: &mut ResourceUpdates,
-        _layout_size: LayoutSize,
+        _framebuffer_size: DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         // Create a 200x200 stacking context with an animated transform property.
         let bounds = (0, 0).to(200, 200);
         let complex_clip = ComplexClipRegion {
             rect: bounds,
             radii: BorderRadius::uniform(50.0),
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -180,26 +180,29 @@ fn main() {
     boilerplate::main_wrapper(&mut app, None);
 }
 
 struct App {
     touch_state: TouchState,
 }
 
 impl Example for App {
+    // Make this the only example to test all shaders for compile errors.
+    const PRECACHE_SHADERS: bool = true;
+
     fn render(
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         resources: &mut ResourceUpdates,
-        layout_size: LayoutSize,
+        _: DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
-        let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
+        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
         let info = LayoutPrimitiveInfo::new(bounds);
         builder.push_stacking_context(
             &info,
             ScrollPolicy::Scrollable,
             None,
             TransformStyle::Flat,
             None,
             MixBlendMode::Normal,
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -12,18 +12,18 @@ mod boilerplate;
 
 use boilerplate::{Example, HandyDandyRectBuilder};
 use rayon::Configuration as ThreadPoolConfig;
 use rayon::ThreadPool;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::sync::Arc;
 use std::sync::mpsc::{channel, Receiver, Sender};
-use webrender::api::{self, DeviceUintRect, DisplayListBuilder, DocumentId, LayoutSize, PipelineId,
-                     RenderApi, ResourceUpdates};
+use webrender::api::{self,
+    DisplayListBuilder, DocumentId, PipelineId, RenderApi, ResourceUpdates};
 
 // This example shows how to implement a very basic BlobImageRenderer that can only render
 // a checkerboard pattern.
 
 // The deserialized command list internally used by this example is just a color.
 type ImageRenderingCommands = api::ColorU;
 
 // Serialize/deserialze the blob.
@@ -140,17 +140,17 @@ impl CheckerboardRenderer {
 }
 
 impl api::BlobImageRenderer for CheckerboardRenderer {
     fn add(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _: Option<api::TileSize>) {
         self.image_cmds
             .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
-    fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _dirty_rect: Option<DeviceUintRect>) {
+    fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _dirty_rect: Option<api::DeviceUintRect>) {
         // Here, updating is just replacing the current version of the commands with
         // the new one (no incremental updates).
         self.image_cmds
             .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
     }
 
     fn delete(&mut self, key: api::ImageKey) {
         self.image_cmds.remove(&key);
@@ -222,17 +222,17 @@ impl api::BlobImageRenderer for Checkerb
 struct App {}
 
 impl Example for App {
     fn render(
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         resources: &mut ResourceUpdates,
-        layout_size: LayoutSize,
+        _framebuffer_size: api::DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         let blob_img1 = api.generate_image_key();
         resources.add_image(
             blob_img1,
             api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
             api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
@@ -242,17 +242,17 @@ impl Example for App {
         let blob_img2 = api.generate_image_key();
         resources.add_image(
             blob_img2,
             api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
             api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
             None,
         );
 
-        let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), layout_size);
+        let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
         let info = api::LayoutPrimitiveInfo::new(bounds);
         builder.push_stacking_context(
             &info,
             api::ScrollPolicy::Scrollable,
             None,
             api::TransformStyle::Flat,
             None,
             api::MixBlendMode::Normal,
@@ -274,25 +274,16 @@ impl Example for App {
             api::LayoutSize::new(200.0, 200.0),
             api::LayoutSize::new(0.0, 0.0),
             api::ImageRendering::Auto,
             blob_img2,
         );
 
         builder.pop_stacking_context();
     }
-
-    fn on_event(
-        &mut self,
-        _event: glutin::Event,
-        _api: &RenderApi,
-        _document_id: DocumentId,
-    ) -> bool {
-        false
-    }
 }
 
 fn main() {
     let worker_config =
         ThreadPoolConfig::new().thread_name(|idx| format!("WebRender:Worker#{}", idx));
 
     let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
 
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate env_logger;
+extern crate euclid;
 
 use gleam::gl;
 use glutin;
 use std::env;
 use std::path::PathBuf;
 use webrender;
 use webrender::api::*;
 
@@ -23,24 +24,23 @@ impl Notifier {
 
 impl RenderNotifier for Notifier {
     fn clone(&self) -> Box<RenderNotifier> {
         Box::new(Notifier {
             window_proxy: self.window_proxy.clone(),
         })
     }
 
-    fn new_frame_ready(&self) {
+    fn wake_up(&self) {
         #[cfg(not(target_os = "android"))]
         self.window_proxy.wakeup_event_loop();
     }
 
-    fn new_scroll_frame_ready(&self, _composite_needed: bool) {
-        #[cfg(not(target_os = "android"))]
-        self.window_proxy.wakeup_event_loop();
+    fn new_document_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
+        self.wake_up();
     }
 }
 
 pub trait HandyDandyRectBuilder {
     fn to(&self, x2: i32, y2: i32) -> LayoutRect;
     fn by(&self, w: i32, h: i32) -> LayoutRect;
 }
 // Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
@@ -57,50 +57,56 @@ impl HandyDandyRectBuilder for (i32, i32
         LayoutRect::new(
             LayoutPoint::new(self.0 as f32, self.1 as f32),
             LayoutSize::new(w as f32, h as f32),
         )
     }
 }
 
 pub trait Example {
+    const TITLE: &'static str = "WebRender Sample App";
+    const PRECACHE_SHADERS: bool = false;
     fn render(
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         resources: &mut ResourceUpdates,
-        layout_size: LayoutSize,
+        framebuffer_size: DeviceUintSize,
         pipeline_id: PipelineId,
         document_id: DocumentId,
     );
-    fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool;
-    fn get_external_image_handler(&self) -> Option<Box<webrender::ExternalImageHandler>> {
-        None
+    fn on_event(&mut self, glutin::Event, &RenderApi, DocumentId) -> bool {
+        false
     }
-    fn get_output_image_handler(
+    fn get_image_handlers(
         &mut self,
         _gl: &gl::Gl,
-    ) -> Option<Box<webrender::OutputImageHandler>> {
-        None
+    ) -> (Option<Box<webrender::ExternalImageHandler>>, 
+          Option<Box<webrender::OutputImageHandler>>) {
+        (None, None)
     }
-    fn draw_custom(&self, _gl: &gl::Gl) {}
+    fn draw_custom(&self, _gl: &gl::Gl) {
+    }
 }
 
-pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOptions>) {
+pub fn main_wrapper<E: Example>(
+    example: &mut E,
+    options: Option<webrender::RendererOptions>,
+) {
     env_logger::init().unwrap();
 
     let args: Vec<String> = env::args().collect();
     let res_path = if args.len() > 1 {
         Some(PathBuf::from(&args[1]))
     } else {
         None
     };
 
     let window = glutin::WindowBuilder::new()
-        .with_title("WebRender Sample App")
+        .with_title(E::TITLE)
         .with_multitouch()
         .with_gl(glutin::GlRequest::GlThenGles {
             opengl_version: (3, 2),
             opengles_version: (3, 0),
         })
         .build()
         .unwrap();
 
@@ -114,143 +120,142 @@ pub fn main_wrapper(example: &mut Exampl
         },
         gl::GlType::Gles => unsafe {
             gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
         },
     };
 
     println!("OpenGL version {}", gl.get_string(gl::VERSION));
     println!("Shader resource path: {:?}", res_path);
+    let device_pixel_ratio = window.hidpi_factor();
+    println!("Device pixel ratio: {}", device_pixel_ratio);
 
-    let (width, height) = window.get_inner_size_pixels().unwrap();
-
+    println!("Loading shaders...");
     let opts = webrender::RendererOptions {
         resource_override_path: res_path,
         debug: true,
-        precache_shaders: true,
-        device_pixel_ratio: window.hidpi_factor(),
+        precache_shaders: E::PRECACHE_SHADERS,
+        device_pixel_ratio,
+        clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
         ..options.unwrap_or(webrender::RendererOptions::default())
     };
 
-    let size = DeviceUintSize::new(width, height);
+    let framebuffer_size = {
+        let (width, height) = window.get_inner_size_pixels().unwrap();
+        DeviceUintSize::new(width, height)
+    };
     let notifier = Box::new(Notifier::new(window.create_window_proxy()));
     let (mut renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
     let api = sender.create_api();
-    let document_id = api.add_document(size);
+    let document_id = api.add_document(framebuffer_size, 0);
 
-    if let Some(external_image_handler) = example.get_external_image_handler() {
-        renderer.set_external_image_handler(external_image_handler);
-    }
-    if let Some(output_image_handler) = example.get_output_image_handler(&*gl) {
+    let (external, output) = example.get_image_handlers(&*gl);
+
+    if let Some(output_image_handler) = output {
         renderer.set_output_image_handler(output_image_handler);
     }
 
-    let epoch = Epoch(0);
-    let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
+    if let Some(external_image_handler) = external {
+        renderer.set_external_image_handler(external_image_handler);
+    }
 
+    let epoch = Epoch(0);
     let pipeline_id = PipelineId(0, 0);
-    let layout_size = LayoutSize::new(width as f32, height as f32);
+    let layout_size = framebuffer_size.to_f32() / euclid::ScaleFactor::new(device_pixel_ratio);
     let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
     let mut resources = ResourceUpdates::new();
 
     example.render(
         &api,
         &mut builder,
         &mut resources,
-        layout_size,
+        framebuffer_size,
         pipeline_id,
         document_id,
     );
     api.set_display_list(
         document_id,
         epoch,
-        Some(root_background_color),
-        LayoutSize::new(width as f32, height as f32),
+        None,
+        layout_size,
         builder.finalize(),
         true,
         resources,
     );
     api.set_root_pipeline(document_id, pipeline_id);
     api.generate_frame(document_id, None);
 
+    println!("Entering event loop");
     'outer: for event in window.wait_events() {
         let mut events = Vec::new();
         events.push(event);
-
-        for event in window.poll_events() {
-            events.push(event);
-        }
+        events.extend(window.poll_events());
 
         for event in events {
             match event {
                 glutin::Event::Closed |
                 glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => break 'outer,
 
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::P),
                 ) => {
-                    let mut flags = renderer.get_debug_flags();
-                    flags.toggle(webrender::DebugFlags::PROFILER_DBG);
-                    renderer.set_debug_flags(flags);
+                    renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::O),
                 ) => {
-                    let mut flags = renderer.get_debug_flags();
-                    flags.toggle(webrender::DebugFlags::RENDER_TARGET_DBG);
-                    renderer.set_debug_flags(flags);
+                    renderer.toggle_debug_flags(webrender::DebugFlags::RENDER_TARGET_DBG);
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::I),
                 ) => {
-                    let mut flags = renderer.get_debug_flags();
-                    flags.toggle(webrender::DebugFlags::TEXTURE_CACHE_DBG);
-                    renderer.set_debug_flags(flags);
+                    renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG);
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::B),
                 ) => {
-                    let mut flags = renderer.get_debug_flags();
-                    flags.toggle(webrender::DebugFlags::ALPHA_PRIM_DBG);
-                    renderer.set_debug_flags(flags);
+                    renderer.toggle_debug_flags(webrender::DebugFlags::ALPHA_PRIM_DBG);
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::Q),
                 ) => {
-                    renderer.toggle_queries_enabled();
+                    renderer.toggle_debug_flags(webrender::DebugFlags::GPU_TIME_QUERIES
+                        | webrender::DebugFlags::GPU_SAMPLE_QUERIES);
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::Key1),
                 ) => {
-                    api.set_window_parameters(document_id,
-                        size,
-                        DeviceUintRect::new(DeviceUintPoint::zero(), size),
+                    api.set_window_parameters(
+                        document_id,
+                        framebuffer_size,
+                        DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
                         1.0
                     );
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::Key2),
                 ) => {
-                    api.set_window_parameters(document_id,
-                        size,
-                        DeviceUintRect::new(DeviceUintPoint::zero(), size),
+                    api.set_window_parameters(
+                        document_id,
+                        framebuffer_size,
+                        DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
                         2.0
                     );
                 }
                 glutin::Event::KeyboardInput(
                     glutin::ElementState::Pressed,
                     _,
                     Some(glutin::VirtualKeyCode::M),
                 ) => {
@@ -259,34 +264,34 @@ pub fn main_wrapper(example: &mut Exampl
                 _ => if example.on_event(event, &api, document_id) {
                     let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
                     let mut resources = ResourceUpdates::new();
 
                     example.render(
                         &api,
                         &mut builder,
                         &mut resources,
-                        layout_size,
+                        framebuffer_size,
                         pipeline_id,
                         document_id,
                     );
                     api.set_display_list(
                         document_id,
                         epoch,
-                        Some(root_background_color),
-                        LayoutSize::new(width as f32, height as f32),
+                        None,
+                        layout_size,
                         builder.finalize(),
                         true,
                         resources,
                     );
                     api.generate_frame(document_id, None);
                 },
             }
         }
 
         renderer.update();
-        renderer.render(DeviceUintSize::new(width, height)).unwrap();
+        renderer.render(framebuffer_size).unwrap();
         example.draw_custom(&*gl);
         window.swap_buffers().ok();
     }
 
     renderer.deinit();
 }
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/examples/document.rs
@@ -0,0 +1,150 @@
+/* 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/. */
+
+extern crate euclid;
+extern crate gleam;
+extern crate glutin;
+extern crate webrender;
+
+#[path = "common/boilerplate.rs"]
+mod boilerplate;
+
+use boilerplate::Example;
+use euclid::ScaleFactor;
+use webrender::api::*;
+
+// This example creates multiple documents overlapping each other with
+// specified layer indices.
+
+struct Document {
+    id: DocumentId,
+    pipeline_id: PipelineId,
+    content_rect: LayoutRect,
+    color: ColorF,
+}
+
+struct App {
+    documents: Vec<Document>,
+}
+
+impl App {
+    fn init(
+        &mut self,
+        api: &RenderApi,
+        framebuffer_size: DeviceUintSize,
+        device_pixel_ratio: f32,
+    ) {
+        let init_data = vec![
+            (
+                PipelineId(1, 0),
+                -1,
+                ColorF::new(0.0, 1.0, 0.0, 1.0),
+                DeviceUintPoint::new(0, 0),
+            ),
+            (
+                PipelineId(2, 0),
+                -2,
+                ColorF::new(1.0, 1.0, 0.0, 1.0),
+                DeviceUintPoint::new(200, 0),
+            ),
+            (
+                PipelineId(3, 0),
+                -3,
+                ColorF::new(1.0, 0.0, 0.0, 1.0),
+                DeviceUintPoint::new(200, 200),
+            ),
+            (
+                PipelineId(4, 0),
+                -4,
+                ColorF::new(1.0, 0.0, 1.0, 1.0),
+                DeviceUintPoint::new(0, 200),
+            ),
+        ];
+
+        for (pipeline_id, layer, color, offset) in init_data {
+            let size = DeviceUintSize::new(250, 250);
+            let bounds = DeviceUintRect::new(offset, size);
+
+            let document_id = api.add_document(size, layer);
+            api.set_window_parameters(document_id,
+                framebuffer_size,
+                bounds,
+                1.0
+            );
+            api.set_root_pipeline(document_id, pipeline_id);
+
+            self.documents.push(Document {
+                id: document_id,
+                pipeline_id,
+                content_rect: bounds.to_f32() / ScaleFactor::new(device_pixel_ratio),
+                color,
+            });
+        }
+    }
+}
+
+impl Example for App {
+    fn render(
+        &mut self,
+        api: &RenderApi,
+        base_builder: &mut DisplayListBuilder,
+        _: &mut ResourceUpdates,
+        framebuffer_size: DeviceUintSize,
+        _: PipelineId,
+        _: DocumentId,
+    ) {
+        if self.documents.is_empty() {
+            let device_pixel_ratio = framebuffer_size.width as f32 /
+                base_builder.content_size().width;
+            // this is the first run, hack around the boilerplate,
+            // which assumes an example only needs one document
+            self.init(api, framebuffer_size, device_pixel_ratio);
+        }
+
+        for doc in &self.documents {
+            let mut builder = DisplayListBuilder::new(
+                doc.pipeline_id,
+                doc.content_rect.size,
+            );
+            let local_rect = LayoutRect::new(
+                LayoutPoint::zero(),
+                doc.content_rect.size,
+            );
+
+            builder.push_stacking_context(
+                &LayoutPrimitiveInfo::new(doc.content_rect),
+                ScrollPolicy::Fixed,
+                None,
+                TransformStyle::Flat,
+                None,
+                MixBlendMode::Normal,
+                Vec::new(),
+            );
+            builder.push_rect(
+                &LayoutPrimitiveInfo::new(local_rect),
+                doc.color,
+            );
+            builder.pop_stacking_context();
+
+            api.set_display_list(
+                doc.id,
+                Epoch(0),
+                None,
+                doc.content_rect.size,
+                builder.finalize(),
+                true,
+                ResourceUpdates::new(),
+            );
+
+            api.generate_frame(doc.id, None);
+        }
+    }
+}
+
+fn main() {
+    let mut app = App {
+        documents: Vec::new(),
+    };
+    boilerplate::main_wrapper(&mut app, None);
+}
--- a/gfx/webrender/examples/frame_output.rs
+++ b/gfx/webrender/examples/frame_output.rs
@@ -1,169 +1,185 @@
 /* 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/. */
 
+extern crate euclid;
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
 
 #[path = "common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::{Example, HandyDandyRectBuilder};
 use gleam::gl;
 use webrender::api::*;
+use euclid::ScaleFactor;
 
 // This example demonstrates using the frame output feature to copy
 // the output of a WR framebuffer to a custom texture.
 
-const VS: &str = "#version 130
-    in vec2 aPos;out vec2 vUv;
-    void main() { vUv = aPos; gl_Position = vec4(aPos, 0.0, 1.0); }
-";
-const FS: &str = "#version 130
-    out vec4 oFragColor;
-    in vec2 vUv;
-    uniform sampler2D s;
-    void main() { oFragColor = texture(s, vUv); }
-";
+#[derive(Debug)]
+struct Document {
+    id: DocumentId,
+    pipeline_id: PipelineId,
+    content_rect: LayoutRect,
+    color: ColorF,
+}
+
 
 struct App {
-    iframe_pipeline_id: Option<PipelineId>,
-    texture_id: gl::GLuint,
+    external_image_key: Option<ImageKey>,
+    output_document: Option<Document>
 }
 
 struct OutputHandler {
-    texture_id: gl::GLuint,
+    texture_id: gl::GLuint
 }
 
-impl OutputHandler {
-    fn new(texture_id: gl::GLuint) -> OutputHandler {
-        OutputHandler { texture_id }
-    }
+struct ExternalHandler {
+    texture_id: gl::GLuint
 }
 
 impl webrender::OutputImageHandler for OutputHandler {
     fn lock(&mut self, _id: PipelineId) -> Option<(u32, DeviceIntSize)> {
-        Some((self.texture_id, DeviceIntSize::new(100, 100)))
+        Some((self.texture_id, DeviceIntSize::new(500, 500)))
     }
 
     fn unlock(&mut self, _id: PipelineId) {}
 }
 
-impl Example for App {
-    fn render(
+impl webrender::ExternalImageHandler for ExternalHandler {
+    fn lock(&mut self, _key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage {
+        webrender::ExternalImage {
+            u0: 0.0,
+            v0: 0.0,
+            u1: 1.0,
+            v1: 1.0,
+            source: webrender::ExternalImageSource::NativeTexture(self.texture_id),
+        }
+    }
+    fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
+}
+
+impl App {
+    fn init_output_document(
         &mut self,
         api: &RenderApi,
-        builder: &mut DisplayListBuilder,
-        _resources: &mut ResourceUpdates,
-        _layout_size: LayoutSize,
-        _pipeline_id: PipelineId,
-        document_id: DocumentId,
+        framebuffer_size: DeviceUintSize,
+        device_pixel_ratio: f32,
     ) {
-        // Build the iframe display list on first render.
-        if self.iframe_pipeline_id.is_none() {
-            let epoch = Epoch(0);
-            let root_background_color = ColorF::new(0.0, 1.0, 0.0, 1.0);
-
-            let iframe_pipeline_id = PipelineId(0, 1);
-            let layout_size = LayoutSize::new(100.0, 100.0);
-            let mut builder = DisplayListBuilder::new(iframe_pipeline_id, layout_size);
-            let resources = ResourceUpdates::new();
+        // Generate the external image key that will be used to render the output document to the root document.
+        self.external_image_key = Some(api.generate_image_key());
+        let mut resources = ResourceUpdates::new();
+        resources.add_image(
+            self.external_image_key.unwrap(),
+            ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true),
+            ImageData::External(ExternalImageData {
+                id: ExternalImageId(0),
+                channel_index: 0,
+                image_type: ExternalImageType::Texture2DHandle
+            }),
+            None,
+        );
 
-            let bounds = (0, 0).to(50, 50);
-            let info = LayoutPrimitiveInfo::new(bounds);
-            builder.push_stacking_context(
-                &info,
-                ScrollPolicy::Scrollable,
-                None,
-                TransformStyle::Flat,
-                None,
-                MixBlendMode::Normal,
-                Vec::new(),
-            );
+        let pipeline_id = PipelineId(1, 0);
+        let layer = 1;
+        let color = ColorF::new(1., 1., 0., 1.);
+        let bounds = DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size);
+        let document_id = api.add_document(framebuffer_size, layer);
 
-            builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
-            builder.pop_stacking_context();
+        api.set_root_pipeline(document_id, pipeline_id);
 
-            api.set_display_list(
-                document_id,
-                epoch,
-                Some(root_background_color),
-                layout_size,
-                builder.finalize(),
-                true,
-                resources,
-            );
+        let document = Document {
+            id: document_id,
+            pipeline_id,
+            content_rect: bounds.to_f32() / ScaleFactor::new(device_pixel_ratio),
+            color,
+        };
 
-            self.iframe_pipeline_id = Some(iframe_pipeline_id);
-            api.enable_frame_output(document_id, iframe_pipeline_id, true);
-        }
+        let info = LayoutPrimitiveInfo::new(document.content_rect);
+        let mut builder = DisplayListBuilder::new(
+            document.pipeline_id,
+            document.content_rect.size,
+        );
 
-        let bounds = (100, 100).to(200, 200);
-        let info = LayoutPrimitiveInfo::new(bounds);
         builder.push_stacking_context(
             &info,
             ScrollPolicy::Scrollable,
             None,
             TransformStyle::Flat,
             None,
             MixBlendMode::Normal,
             Vec::new(),
         );
 
-        builder.push_iframe(&info, self.iframe_pipeline_id.unwrap());
+        builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
+        builder.pop_stacking_context();
+
+        api.enable_frame_output(document.id, document.pipeline_id, true);
+        api.set_display_list(
+            document.id,
+            Epoch(0),
+            Some(document.color),
+            document.content_rect.size,
+            builder.finalize(),
+            true,
+            resources,
+        );
+        
+        api.generate_frame(document.id, None);
+        self.output_document = Some(document);
+    }
+}
+
+impl Example for App {
+    fn render(
+        &mut self,
+        api: &RenderApi,
+        builder: &mut DisplayListBuilder,
+        _resources: &mut ResourceUpdates,
+        framebuffer_size: DeviceUintSize,
+        _pipeline_id: PipelineId,
+        _document_id: DocumentId,
+    ) {
+        if self.output_document.is_none(){
+            let device_pixel_ratio = framebuffer_size.width as f32 /
+                builder.content_size().width;
+            self.init_output_document(api, DeviceUintSize::new(200, 200), device_pixel_ratio);
+        }
+
+        let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
+        builder.push_stacking_context(
+            &info,
+            ScrollPolicy::Scrollable,
+            None,
+            TransformStyle::Flat,
+            None,
+            MixBlendMode::Normal,
+            Vec::new(),
+        );
+
+        builder.push_image(
+            &info,
+            info.rect.size,
+            LayoutSize::zero(),
+            ImageRendering::Auto,
+            self.external_image_key.unwrap()
+        );
 
         builder.pop_stacking_context();
     }
 
-    fn draw_custom(&self, gl: &gl::Gl) {
-        let vbo = gl.gen_buffers(1)[0];
-        let vao = gl.gen_vertex_arrays(1)[0];
-
-        let pid = create_program(gl);
-
-        let vertices: [f32; 12] = [0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0];
-
-        gl.active_texture(gl::TEXTURE0);
-        gl.bind_texture(gl::TEXTURE_2D, self.texture_id);
-
-        gl.use_program(pid);
-        let sampler = gl.get_uniform_location(pid, "s");
-        debug_assert!(sampler != -1);
-        gl.uniform_1i(sampler, 0);
-
-        gl.bind_buffer(gl::ARRAY_BUFFER, vbo);
-        gl::buffer_data(gl, gl::ARRAY_BUFFER, &vertices, gl::STATIC_DRAW);
-
-        gl.bind_vertex_array(vao);
-        gl.enable_vertex_attrib_array(0);
-        gl.vertex_attrib_pointer(0, 2, gl::FLOAT, false, 8, 0);
-
-        gl.draw_arrays(gl::TRIANGLES, 0, 6);
-
-        gl.delete_vertex_arrays(&[vao]);
-        gl.delete_buffers(&[vbo]);
-        gl.delete_program(pid);
-    }
-
-    fn on_event(
-        &mut self,
-        _event: glutin::Event,
-        _api: &RenderApi,
-        _document_id: DocumentId,
-    ) -> bool {
-        false
-    }
-
-    fn get_output_image_handler(
+    fn get_image_handlers(
         &mut self,
         gl: &gl::Gl,
-    ) -> Option<Box<webrender::OutputImageHandler>> {
+    ) -> (Option<Box<webrender::ExternalImageHandler>>, 
+          Option<Box<webrender::OutputImageHandler>>) {
         let texture_id = gl.gen_textures(1)[0];
 
         gl.bind_texture(gl::TEXTURE_2D, texture_id);
         gl.tex_parameter_i(
             gl::TEXTURE_2D,
             gl::TEXTURE_MAG_FILTER,
             gl::LINEAR as gl::GLint,
         );
@@ -190,55 +206,23 @@ impl Example for App {
             100,
             0,
             gl::BGRA,
             gl::UNSIGNED_BYTE,
             None,
         );
         gl.bind_texture(gl::TEXTURE_2D, 0);
 
-        self.texture_id = texture_id;
-        Some(Box::new(OutputHandler::new(texture_id)))
+        (   
+            Some(Box::new(ExternalHandler { texture_id })), 
+            Some(Box::new(OutputHandler { texture_id }))
+        )
     }
 }
 
 fn main() {
     let mut app = App {
-        iframe_pipeline_id: None,
-        texture_id: 0,
+        external_image_key: None,
+        output_document: None
     };
+
     boilerplate::main_wrapper(&mut app, None);
 }
-
-pub fn compile_shader(gl: &gl::Gl, shader_type: gl::GLenum, source: &str) -> gl::GLuint {
-    let id = gl.create_shader(shader_type);
-    gl.shader_source(id, &[source.as_bytes()]);
-    gl.compile_shader(id);
-    let log = gl.get_shader_info_log(id);
-    if gl.get_shader_iv(id, gl::COMPILE_STATUS) == (0 as gl::GLint) {
-        panic!("{:?} {}", source, log);
-    }
-    id
-}
-
-pub fn create_program(gl: &gl::Gl) -> gl::GLuint {
-    let vs_id = compile_shader(gl, gl::VERTEX_SHADER, VS);
-    let fs_id = compile_shader(gl, gl::FRAGMENT_SHADER, FS);
-
-    let pid = gl.create_program();
-    gl.attach_shader(pid, vs_id);
-    gl.attach_shader(pid, fs_id);
-
-    gl.bind_attrib_location(pid, 0, "aPos");
-    gl.link_program(pid);
-
-    gl.detach_shader(pid, vs_id);
-    gl.detach_shader(pid, fs_id);
-    gl.delete_shader(vs_id);
-    gl.delete_shader(fs_id);
-
-    if gl.get_program_iv(pid, gl::LINK_STATUS) == (0 as gl::GLint) {
-        let error_log = gl.get_program_info_log(pid);
-        panic!("{}", error_log);
-    }
-
-    pid
-}
--- a/gfx/webrender/examples/iframe.rs
+++ b/gfx/webrender/examples/iframe.rs
@@ -19,17 +19,17 @@ use webrender::api::*;
 struct App {}
 
 impl Example for App {
     fn render(
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _resources: &mut ResourceUpdates,
-        _layout_size: LayoutSize,
+        _framebuffer_size: DeviceUintSize,
         pipeline_id: PipelineId,
         document_id: DocumentId,
     ) {
         // All the sub_* things are for the nested pipeline
         let sub_size = DeviceUintSize::new(100, 100);
         let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
 
         let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
@@ -70,23 +70,14 @@ impl Example for App {
             MixBlendMode::Normal,
             Vec::new(),
         );
         // red rect under the iframe: if this is visible, things have gone wrong
         builder.push_rect(&info, ColorF::new(1.0, 0.0, 0.0, 1.0));
         builder.push_iframe(&info, sub_pipeline_id);
         builder.pop_stacking_context();
     }
-
-    fn on_event(
-        &mut self,
-        _event: glutin::Event,
-        _api: &RenderApi,
-        _document_id: DocumentId,
-    ) -> bool {
-        false
-    }
 }
 
 fn main() {
     let mut app = App {};
     boilerplate::main_wrapper(&mut app, None);
 }
--- a/gfx/webrender/examples/image_resize.rs
+++ b/gfx/webrender/examples/image_resize.rs
@@ -19,17 +19,17 @@ struct App {
 }
 
 impl Example for App {
     fn render(
         &mut self,
         _api: &RenderApi,
         builder: &mut DisplayListBuilder,
         resources: &mut ResourceUpdates,
-        _layout_size: LayoutSize,
+        _framebuffer_size: DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32);
         resources.add_image(
             self.image_key,
             image_descriptor,
             image_data,
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -19,21 +19,23 @@ struct App {
 }
 
 impl Example for App {
     fn render(
         &mut self,
         _api: &RenderApi,
         builder: &mut DisplayListBuilder,
         _resources: &mut ResourceUpdates,
-        layout_size: LayoutSize,
+        _framebuffer_size: DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
-        let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), layout_size));
+        let info = LayoutPrimitiveInfo::new(
+            LayoutRect::new(LayoutPoint::zero(), builder.content_size())
+        );
         builder.push_stacking_context(
             &info,
             ScrollPolicy::Scrollable,
             None,
             TransformStyle::Flat,
             None,
             MixBlendMode::Normal,
             Vec::new(),
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ b/gfx/webrender/examples/texture_cache_stress.rs
@@ -5,16 +5,17 @@
 extern crate gleam;
 extern crate glutin;
 extern crate webrender;
 
 #[path = "common/boilerplate.rs"]
 mod boilerplate;
 
 use boilerplate::{Example, HandyDandyRectBuilder};
+use gleam::gl;
 use std::mem;
 use webrender::api::*;
 
 struct ImageGenerator {
     patterns: [[u8; 3]; 6],
     next_pattern: usize,
     current_image: Vec<u8>,
 }
@@ -81,17 +82,17 @@ struct App {
 }
 
 impl Example for App {
     fn render(
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         resources: &mut ResourceUpdates,
-        _layout_size: LayoutSize,
+        _framebuffer_size: DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
         let bounds = (0, 0).to(512, 512);
         let info = LayoutPrimitiveInfo::new(bounds);
         builder.push_stacking_context(
             &info,
             ScrollPolicy::Scrollable,
@@ -278,18 +279,22 @@ impl Example for App {
                 return true;
             }
             _ => {}
         }
 
         false
     }
 
-    fn get_external_image_handler(&self) -> Option<Box<webrender::ExternalImageHandler>> {
-        Some(Box::new(ImageGenerator::new()))
+    fn get_image_handlers(
+        &mut self,
+        _gl: &gl::Gl,
+    ) -> (Option<Box<webrender::ExternalImageHandler>>, 
+          Option<Box<webrender::OutputImageHandler>>) {
+        (Some(Box::new(ImageGenerator::new())), None)
     }
 }
 
 fn main() {
     let mut app = App {
         image_key: None,
         stress_keys: Vec::new(),
         image_generator: ImageGenerator::new(),
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -165,21 +165,21 @@ struct App {
 }
 
 impl Example for App {
     fn render(
         &mut self,
         api: &RenderApi,
         builder: &mut DisplayListBuilder,
         resources: &mut ResourceUpdates,
-        layout_size: LayoutSize,
+        _framebuffer_size: DeviceUintSize,
         _pipeline_id: PipelineId,
         _document_id: DocumentId,
     ) {
-        let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
+        let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
         let info = LayoutPrimitiveInfo::new(bounds);
         builder.push_stacking_context(
             &info,
             ScrollPolicy::Scrollable,
             None,
             TransformStyle::Flat,
             None,
             MixBlendMode::Normal,
--- a/gfx/webrender/res/cs_clip_image.glsl
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -43,18 +43,17 @@ void main(void) {
     // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
     vec4 inner_rect = vec4(res.uv_rect.xy, res.uv_rect.zw);
     vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
-    float alpha = 1.f;
-    vec2 local_pos = init_transform_fs(vPos, alpha);
+    float alpha = init_transform_fs(vPos.xy / vPos.z);
 
     bool repeat_mask = false; //TODO
     vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskUv.xy) :
         clamp(vClipMaskUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0));
     vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy,
         vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
     float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8
 
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -88,18 +88,18 @@ void main(void) {
     vClipCenter_Radius_BL = vec4(clip_rect.p0.x + r_bl.x,
                                  clip_rect.p1.y - r_bl.y,
                                  r_bl);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
-    float alpha = 1.f;
-    vec2 local_pos = init_transform_fs(vPos, alpha);
+    vec2 local_pos = vPos.xy / vPos.z;
+    float alpha = init_transform_fs(local_pos);
 
     float aa_range = compute_aa_range(local_pos);
 
     float clip_alpha = rounded_rect(local_pos,
                                     vClipCenter_Radius_TL,
                                     vClipCenter_Radius_TR,
                                     vClipCenter_Radius_BR,
                                     vClipCenter_Radius_BL,
--- a/gfx/webrender/res/cs_text_run.glsl
+++ b/gfx/webrender/res/cs_text_run.glsl
@@ -20,37 +20,31 @@ void main(void) {
     int resource_address = prim.user_data1;
 
     Glyph glyph = fetch_glyph(prim.specific_prim_address,
                               glyph_index,
                               text.subpx_dir);
 
     GlyphResource res = fetch_glyph_resource(resource_address);
 
-    // Glyphs size is already in device-pixels.
+    // Glyph size is already in device-pixels.
     // The render task origin is in device-pixels. Offset that by
     // the glyph offset, relative to its primitive bounding rect.
-    vec2 size = (res.uv_rect.zw - res.uv_rect.xy) * res.scale;
-    vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
-    vec2 origin = prim.task.common_data.task_rect.p0 +
-                  uDevicePixelRatio * (local_pos - prim.task.content_origin);
-    vec4 local_rect = vec4(origin, size);
+    vec2 glyph_size = res.uv_rect.zw - res.uv_rect.xy;
+    vec2 glyph_pos = res.offset + glyph_size * aPosition.xy;
+    vec2 local_pos = prim.task.common_data.task_rect.p0 + glyph_pos * res.scale +
+                     uDevicePixelRatio * (glyph.offset - prim.task.content_origin);
+    gl_Position = uTransform * vec4(local_pos, 0.0, 1.0);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
-    vec2 pos = mix(local_rect.xy,
-                   local_rect.xy + local_rect.zw,
-                   aPosition.xy);
-
     vUv = vec3(mix(st0, st1, aPosition.xy), res.layer);
     vColor = prim.task.color;
-
-    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float a = texture(sColor0, vUv).a;
     oFragColor = vColor * a;
 }
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -547,17 +547,16 @@ vec4 get_layer_pos(vec2 pos, Layer layer
     // get the normal to the layer plane
     vec3 n = transpose(mat3(layer.inv_transform)) * vec3(0.0, 0.0, 1.0);
     return untransform(pos, n, a, layer.inv_transform);
 }
 
 // Compute a snapping offset in world space (adjusted to pixel ratio),
 // given local position on the layer and a snap rectangle.
 vec2 compute_snap_offset(vec2 local_pos,
-                         RectWithSize local_clip_rect,
                          Layer layer,
                          RectWithSize snap_rect) {
     // Ensure that the snap rect is at *least* one device pixel in size.
     // TODO(gw): It's not clear to me that this is "correct". Specifically,
     //           how should it interact with sub-pixel snap rects when there
     //           is a layer transform with scale present? But it does fix
     //           the test cases we have in Servo that are failing without it
     //           and seem better than not having this at all.
@@ -592,19 +591,19 @@ VertexInfo write_vertex(RectWithSize ins
 
     // Select the corner of the local rect that we are processing.
     vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
 
     // Clamp to the two local clip rects.
     vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), layer.local_clip_rect);
 
     /// Compute the snapping offset.
-    vec2 snap_offset = compute_snap_offset(clamped_local_pos, local_clip_rect, layer, snap_rect);
+    vec2 snap_offset = compute_snap_offset(clamped_local_pos, layer, snap_rect);
 
-    // Transform the current vertex to the world cpace.
+    // Transform the current vertex to world space.
     vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0);
 
     // Convert the world positions to device pixel space.
     vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
 
     // Apply offsets for the render task to get correct screen location.
     vec2 final_pos = device_pos + snap_offset -
                      task.content_origin +
@@ -613,21 +612,16 @@ VertexInfo write_vertex(RectWithSize ins
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
     VertexInfo vi = VertexInfo(clamped_local_pos, device_pos);
     return vi;
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 
-struct TransformVertexInfo {
-    vec3 local_pos;
-    vec2 screen_pos;
-};
-
 float cross2(vec2 v0, vec2 v1) {
     return v0.x * v1.y - v0.y * v1.x;
 }
 
 // Return intersection of line (p0,p1) and line (p2,p3)
 vec2 intersect_lines(vec2 p0, vec2 p1, vec2 p2, vec2 p3) {
     vec2 d0 = p0 - p1;
     vec2 d1 = p2 - p3;
@@ -637,111 +631,77 @@ vec2 intersect_lines(vec2 p0, vec2 p1, v
 
     float d = cross2(d0, d1);
     float nx = s0 * d1.x - d0.x * s1;
     float ny = s0 * d1.y - d0.y * s1;
 
     return vec2(nx / d, ny / d);
 }
 
-TransformVertexInfo write_transform_vertex(RectWithSize instance_rect,
-                                           RectWithSize local_clip_rect,
-                                           vec4 clip_edge_mask,
-                                           float z,
-                                           Layer layer,
-                                           PictureTask task) {
+VertexInfo write_transform_vertex(RectWithSize instance_rect,
+                                  RectWithSize local_clip_rect,
+                                  vec4 clip_edge_mask,
+                                  float z,
+                                  Layer layer,
+                                  PictureTask task) {
+    // Calculate a clip rect from local clip + layer clip.
+    RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect);
+    clip_rect.p0 = clamp_rect(clip_rect.p0, layer.local_clip_rect);
+    clip_rect.p1 = clamp_rect(clip_rect.p1, layer.local_clip_rect);
+
+    // Calculate a clip rect from local_rect + local clip + layer clip.
     RectWithEndpoint local_rect = to_rect_with_endpoint(instance_rect);
-    RectWithSize clip_rect;
-    clip_rect.p0 = clamp_rect(local_clip_rect.p0, layer.local_clip_rect);
-    clip_rect.size = clamp_rect(local_clip_rect.p0 + local_clip_rect.size, layer.local_clip_rect) - clip_rect.p0;
-
-    vec2 current_local_pos, prev_local_pos, next_local_pos;
-
-    // Clamp to the two local clip rects.
-    local_rect.p0 = clamp_rect(local_rect.p0, clip_rect);
-    local_rect.p1 = clamp_rect(local_rect.p1, clip_rect);
+    local_rect.p0 = clamp(local_rect.p0, clip_rect.p0, clip_rect.p1);
+    local_rect.p1 = clamp(local_rect.p1, clip_rect.p0, clip_rect.p1);
 
-    // Select the current vertex and the previous/next vertices,
-    // based on the vertex ID that is known based on the instance rect.
-    switch (gl_VertexID) {
-        case 0:
-            current_local_pos = vec2(local_rect.p0.x, local_rect.p0.y);
-            next_local_pos = vec2(local_rect.p0.x, local_rect.p1.y);
-            prev_local_pos = vec2(local_rect.p1.x, local_rect.p0.y);
-            break;
-        case 1:
-            current_local_pos = vec2(local_rect.p1.x, local_rect.p0.y);
-            next_local_pos = vec2(local_rect.p0.x, local_rect.p0.y);
-            prev_local_pos = vec2(local_rect.p1.x, local_rect.p1.y);
-            break;
-        case 2:
-            current_local_pos = vec2(local_rect.p0.x, local_rect.p1.y);
-            prev_local_pos = vec2(local_rect.p0.x, local_rect.p0.y);
-            next_local_pos = vec2(local_rect.p1.x, local_rect.p1.y);
-            break;
-        case 3:
-            current_local_pos = vec2(local_rect.p1.x, local_rect.p1.y);
-            prev_local_pos = vec2(local_rect.p0.x, local_rect.p1.y);
-            next_local_pos = vec2(local_rect.p1.x, local_rect.p0.y);
-            break;
-    }
+    // As this is a transform shader, extrude by 2 (local space) pixels
+    // in each direction. This gives enough space around the edge to
+    // apply distance anti-aliasing. Technically, it:
+    // (a) slightly over-estimates the number of required pixels in the simple case.
+    // (b) might not provide enough edge in edge case perspective projections.
+    // However, it's fast and simple. If / when we ever run into issues, we
+    // can do some math on the projection matrix to work out a variable
+    // amount to extrude.
+    float extrude_distance = 2.0;
+    instance_rect.p0 -= vec2(extrude_distance);
+    instance_rect.size += vec2(2.0 * extrude_distance);
 
-    // Transform them to world space
-    vec4 current_world_pos = layer.transform * vec4(current_local_pos, 0.0, 1.0);
-    vec4 prev_world_pos = layer.transform * vec4(prev_local_pos, 0.0, 1.0);
-    vec4 next_world_pos = layer.transform * vec4(next_local_pos, 0.0, 1.0);
+    // Select the corner of the local rect that we are processing.
+    vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
 
-    // Convert to device space
-    vec2 current_device_pos = uDevicePixelRatio * current_world_pos.xy / current_world_pos.w;
-    vec2 prev_device_pos = uDevicePixelRatio * prev_world_pos.xy / prev_world_pos.w;
-    vec2 next_device_pos = uDevicePixelRatio * next_world_pos.xy / next_world_pos.w;
+    // Transform the current vertex to the world cpace.
+    vec4 world_pos = layer.transform * vec4(local_pos, 0.0, 1.0);
 
-    // Get the normals of each of the vectors between the current and next/prev vertices.
-    const float amount = 2.0;
-    vec2 dir_prev = normalize(current_device_pos - prev_device_pos);
-    vec2 dir_next = normalize(current_device_pos - next_device_pos);
-    vec2 norm_prev = vec2(-dir_prev.y,  dir_prev.x);
-    vec2 norm_next = vec2( dir_next.y, -dir_next.x);
+    // Convert the world positions to device pixel space.
+    vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
 
-    // Push those lines out along the normal by a specific amount of device pixels.
-    vec2 adjusted_prev_p0 = current_device_pos + norm_prev * amount;
-    vec2 adjusted_prev_p1 = prev_device_pos + norm_prev * amount;
-    vec2 adjusted_next_p0 = current_device_pos + norm_next * amount;
-    vec2 adjusted_next_p1 = next_device_pos + norm_next * amount;
-
-    // Intersect those adjusted lines to find the actual vertex position.
-    vec2 device_pos = intersect_lines(adjusted_prev_p0,
-                                      adjusted_prev_p1,
-                                      adjusted_next_p0,
-                                      adjusted_next_p1);
-
-    vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer);
-
-    // Apply offsets for the render task to get correct screen location.
-    vec2 final_pos = device_pos - //Note: `snap_rect` is not used
-                     task.content_origin +
-                     task.common_data.task_rect.p0;
-
-
-    gl_Position = uTransform * vec4(final_pos, z, 1.0);
+    // We want the world space coords to be perspective divided by W.
+    // We also want that to apply to any interpolators. However, we
+    // want a constant Z across the primitive, since we're using it
+    // for draw ordering - so scale by the W coord to ensure this.
+    vec4 final_pos = vec4(world_pos.xy + task.common_data.task_rect.p0 - task.content_origin,
+                          z * world_pos.w,
+                          world_pos.w);
+    gl_Position = uTransform * final_pos;
 
     vLocalBounds = mix(
-        vec4(clip_rect.p0, clip_rect.p0 + clip_rect.size),
+        vec4(clip_rect.p0, clip_rect.p1),
         vec4(local_rect.p0, local_rect.p1),
         clip_edge_mask
     );
 
-    return TransformVertexInfo(layer_pos.xyw, device_pos);
+    VertexInfo vi = VertexInfo(local_pos, device_pos);
+    return vi;
 }
 
-TransformVertexInfo write_transform_vertex_primitive(Primitive prim) {
+VertexInfo write_transform_vertex_primitive(Primitive prim) {
     return write_transform_vertex(
         prim.local_rect,
         prim.local_clip_rect,
-        vec4(0.0),
+        vec4(1.0),
         prim.z,
         prim.layer,
         prim.task
     );
 }
 
 #endif //WR_FEATURE_TRANSFORM
 
@@ -850,30 +810,29 @@ float distance_aa(float aa_range, float 
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) {
     vec2 d = max(p0 - pos, pos - p1);
     return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
 }
 
-vec2 init_transform_fs(vec3 local_pos, out float fragment_alpha) {
-    fragment_alpha = 1.0;
-    vec2 pos = local_pos.xy / local_pos.z;
-
-    // Now get the actual signed distance.
-    float d = signed_distance_rect(pos, vLocalBounds.xy, vLocalBounds.zw);
+float init_transform_fs(vec2 local_pos) {
+    // Get signed distance from local rect bounds.
+    float d = signed_distance_rect(
+        local_pos,
+        vLocalBounds.xy,
+        vLocalBounds.zw
+    );
 
     // Find the appropriate distance to apply the AA smoothstep over.
-    float aa_range = compute_aa_range(pos.xy);
+    float aa_range = compute_aa_range(local_pos);
 
     // Only apply AA to fragments outside the signed distance field.
-    fragment_alpha = distance_aa(aa_range, d);
-
-    return pos;
+    return distance_aa(aa_range, d);
 }
 #endif //WR_FEATURE_TRANSFORM
 
 float do_clip() {
     // anything outside of the mask is considered transparent
     bvec4 inside = lessThanEqual(
         vec4(vClipMaskUvBounds.xy, vClipMaskUv.xy),
         vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw));
--- a/gfx/webrender/res/ps_border_corner.glsl
+++ b/gfx/webrender/res/ps_border_corner.glsl
@@ -19,21 +19,17 @@ flat varying vec2 vClipSign;
 flat varying vec4 vEdgeDistance;
 flat varying float vSDFSelect;
 
 flat varying float vIsBorderRadiusLessThanBorderWidth;
 
 // Border style
 flat varying float vAlphaSelect;
 
-#ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
-#else
 varying vec2 vLocalPos;
-#endif
 
 #ifdef WR_VERTEX_SHADER
 // Matches BorderCornerSide enum in border.rs
 #define SIDE_BOTH       0
 #define SIDE_FIRST      1
 #define SIDE_SECOND     2
 
 vec2 get_radii(vec2 radius, vec2 invalid) {
@@ -297,22 +293,22 @@ void main(void) {
 
     write_color(color0, color1, style, color_delta, prim.user_data1);
 
     RectWithSize segment_rect;
     segment_rect.p0 = p0;
     segment_rect.size = p1 - p0;
 
 #ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(segment_rect,
-                                                    prim.local_clip_rect,
-                                                    vec4(1.0),
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task);
+    VertexInfo vi = write_transform_vertex(segment_rect,
+                                           prim.local_clip_rect,
+                                           vec4(1.0),
+                                           prim.z,
+                                           prim.layer,
+                                           prim.task);
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
                                  prim.local_rect);
 #endif
@@ -321,35 +317,32 @@ void main(void) {
     write_clip(vi.screen_pos, prim.clip_area);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float alpha = 1.0;
 #ifdef WR_FEATURE_TRANSFORM
-    alpha = 0.0;
-    vec2 local_pos = init_transform_fs(vLocalPos, alpha);
-#else
-    vec2 local_pos = vLocalPos;
+    alpha = init_transform_fs(vLocalPos);
 #endif
 
     alpha *= do_clip();
 
-    float aa_range = compute_aa_range(local_pos);
+    float aa_range = compute_aa_range(vLocalPos);
 
     float distance_for_color;
     float color_mix_factor;
 
     // Only apply the clip AA if inside the clip region. This is
     // necessary for correctness when the border width is greater
     // than the border radius.
     if (vIsBorderRadiusLessThanBorderWidth == 0.0 ||
-        all(lessThan(local_pos * vClipSign, vClipCenter * vClipSign))) {
-        vec2 p = local_pos - vClipCenter;
+        all(lessThan(vLocalPos * vClipSign, vClipCenter * vClipSign))) {
+        vec2 p = vLocalPos - vClipCenter;
 
         // The coordinate system is snapped to pixel boundaries. To sample the distance,
         // however, we are interested in the center of the pixels which introduces an
         // error of half a pixel towards the exterior of the curve (See issue #1750).
         // This error is corrected by offsetting the distance by half a device pixel.
         // This not entirely correct: it leaves an error that varries between
         // 0 and (sqrt(2) - 1)/2 = 0.2 pixels but it is hardly noticeable and is better
         // than the constant sqrt(2)/2 px error without the correction.
@@ -377,18 +370,18 @@ void main(void) {
         // Get the groove/ridge mix factor.
         color_mix_factor = distance_aa(aa_range, d2);
     } else {
         // Handle the case where the fragment is outside the clip
         // region in a corner. This occurs when border width is
         // greater than border radius.
 
         // Get linear distances along horizontal and vertical edges.
-        vec2 d0 = vClipSign.xx * (local_pos.xx - vEdgeDistance.xz);
-        vec2 d1 = vClipSign.yy * (local_pos.yy - vEdgeDistance.yw);
+        vec2 d0 = vClipSign.xx * (vLocalPos.xx - vEdgeDistance.xz);
+        vec2 d1 = vClipSign.yy * (vLocalPos.yy - vEdgeDistance.yw);
         // Apply union to get the outer edge signed distance.
         float da = min(d0.x, d1.x);
         // Apply intersection to get the inner edge signed distance.
         float db = max(-d0.y, -d1.y);
         // Apply union to get both edges.
         float d = min(da, db);
         // Select fragment on/off based on signed distance.
         // No AA here, since we know we're on a straight edge
@@ -401,15 +394,15 @@ void main(void) {
     }
 
     // Mix inner/outer color.
     vec4 color0 = mix(vColor00, vColor01, color_mix_factor);
     vec4 color1 = mix(vColor10, vColor11, color_mix_factor);
 
     // Select color based on side of line. Get distance from the
     // reference line, and then apply AA along the edge.
-    float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, local_pos);
+    float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, vLocalPos);
     float m = distance_aa(aa_range, -ld);
     vec4 color = mix(color0, color1, m);
 
     oFragColor = color * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_border_edge.glsl
+++ b/gfx/webrender/res/ps_border_edge.glsl
@@ -7,21 +7,17 @@
 flat varying vec4 vColor0;
 flat varying vec4 vColor1;
 flat varying vec2 vEdgeDistance;
 flat varying float vAxisSelect;
 flat varying float vAlphaSelect;
 flat varying vec4 vClipParams;
 flat varying float vClipSelect;
 
-#ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
-#else
 varying vec2 vLocalPos;
-#endif
 
 #ifdef WR_VERTEX_SHADER
 void write_edge_distance(float p0,
                          float original_width,
                          float adjusted_width,
                          float style,
                          float axis_select,
                          float sign_adjust) {
@@ -215,22 +211,22 @@ void main(void) {
         }
     }
 
     write_alpha_select(style);
     write_color0(color, style, color_flip);
     write_color1(color, style, color_flip);
 
 #ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(segment_rect,
-                                                    prim.local_clip_rect,
-                                                    vec4(1.0),
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task);
+    VertexInfo vi = write_transform_vertex(segment_rect,
+                                           prim.local_clip_rect,
+                                           vec4(1.0),
+                                           prim.z,
+                                           prim.layer,
+                                           prim.task);
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
                                  prim.local_rect);
 #endif
@@ -239,34 +235,31 @@ void main(void) {
     write_clip(vi.screen_pos, prim.clip_area);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float alpha = 1.0;
 #ifdef WR_FEATURE_TRANSFORM
-    alpha = 0.0;
-    vec2 local_pos = init_transform_fs(vLocalPos, alpha);
-#else
-    vec2 local_pos = vLocalPos;
+    alpha = init_transform_fs(vLocalPos);
 #endif
 
     alpha *= do_clip();
 
     // Find the appropriate distance to apply the step over.
-    float aa_range = compute_aa_range(local_pos);
+    float aa_range = compute_aa_range(vLocalPos);
 
     // Applies the math necessary to draw a style: double
     // border. In the case of a solid border, the vertex
     // shader sets interpolator values that make this have
     // no effect.
 
     // Select the x/y coord, depending on which axis this edge is.
-    vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
+    vec2 pos = mix(vLocalPos.xy, vLocalPos.yx, vAxisSelect);
 
     // Get signed distance from each of the inner edges.
     float d0 = pos.x - vEdgeDistance.x;
     float d1 = vEdgeDistance.y - pos.x;
 
     // SDF union to select both outer edges.
     float d = min(d0, d1);
 
--- a/gfx/webrender/res/ps_gradient.glsl
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -1,21 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include shared,prim_shared
 
 varying vec4 vColor;
 
-#ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
-#else
-varying vec2 vPos;
-#endif
+varying vec2 vLocalPos;
 
 #ifdef WR_VERTEX_SHADER
 void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.specific_prim_address);
 
     vec4 abs_start_end_point = gradient.start_end_point + prim.local_rect.p0.xyxy;
 
@@ -66,48 +62,46 @@ void main(void) {
 
         // Adjust the stop colors by how much they were clamped
         vec2 adjusted_offset = (g01_y_clamped - g01_y.xx) / (g01_y.y - g01_y.x);
         adjusted_color_g0 = mix(g0.color, g1.color, adjusted_offset.x);
         adjusted_color_g1 = mix(g0.color, g1.color, adjusted_offset.y);
     }
 
 #ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(segment_rect,
-                                                    prim.local_clip_rect,
-                                                    vec4(1.0),
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task);
+    VertexInfo vi = write_transform_vertex(segment_rect,
+                                           prim.local_clip_rect,
+                                           vec4(1.0),
+                                           prim.z,
+                                           prim.layer,
+                                           prim.task);
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
                                  prim.local_rect);
 
     vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size;
-    vPos = vi.local_pos;
+    vLocalPos = vi.local_pos;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vColor = mix(adjusted_color_g0, adjusted_color_g1, dot(f, axis));
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
-    float alpha = 0.0;
-    vec2 local_pos = init_transform_fs(vLocalPos, alpha);
+    float alpha = init_transform_fs(vLocalPos);
 #else
     float alpha = 1.0;
-    vec2 local_pos = vPos;
 #endif
 
     alpha *= do_clip();
     oFragColor = dither(vColor * alpha);
 }
 #endif
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -9,31 +9,30 @@
 // check GL_TEXTURE_RECTANGLE.
 flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
 flat varying vec2 vTextureSize;   // Size of the image in the texture atlas.
 flat varying vec2 vTileSpacing;   // Amount of space between tiled instances of this image.
 flat varying vec4 vStRect;        // Rectangle of valid texture rect.
 flat varying float vLayer;
 
 #ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
 flat varying vec4 vLocalRect;
-#else
+#endif
+
 varying vec2 vLocalPos;
-#endif
 flat varying vec2 vStretchSize;
 
 #ifdef WR_VERTEX_SHADER
 void main(void) {
     Primitive prim = load_primitive();
     Image image = fetch_image(prim.specific_prim_address);
     ImageResource res = fetch_image_resource(prim.user_data0);
 
 #ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex_primitive(prim);
+    VertexInfo vi = write_transform_vertex_primitive(prim);
     vLocalPos = vi.local_pos;
     vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.p0 + prim.local_rect.size);
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
@@ -76,23 +75,22 @@ void main(void) {
     vec2 half_texel = vec2(0.5) / texture_size_normalization_factor;
     vStRect = vec4(min(st0, st1) + half_texel, max(st0, st1) - half_texel);
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
-    float alpha = 0.0;
-    vec2 pos = init_transform_fs(vLocalPos, alpha);
+    float alpha = init_transform_fs(vLocalPos);
 
     // We clamp the texture coordinate calculation here to the local rectangle boundaries,
     // which makes the edge of the texture stretch instead of repeat.
-    vec2 upper_bound_mask = step(vLocalRect.zw, pos);
-    vec2 relative_pos_in_rect = clamp(pos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
+    vec2 upper_bound_mask = step(vLocalRect.zw, vLocalPos);
+    vec2 relative_pos_in_rect = clamp(vLocalPos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
 #else
     float alpha = 1.0;
     vec2 relative_pos_in_rect = vLocalPos;
     vec2 upper_bound_mask = vec2(0.0);
 #endif
 
     alpha *= do_clip();
 
--- a/gfx/webrender/res/ps_line.glsl
+++ b/gfx/webrender/res/ps_line.glsl
@@ -5,21 +5,17 @@
 #include shared,prim_shared
 
 varying vec4 vColor;
 flat varying int vStyle;
 flat varying float vAxisSelect;
 flat varying vec4 vParams;
 flat varying vec2 vLocalOrigin;
 
-#ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
-#else
 varying vec2 vLocalPos;
-#endif
 
 #ifdef WR_VERTEX_SHADER
 #define LINE_ORIENTATION_VERTICAL       0
 #define LINE_ORIENTATION_HORIZONTAL     1
 
 struct Line {
     vec4 color;
     float wavyLineThickness;
@@ -108,17 +104,17 @@ void main(void) {
                     prim.local_rect.p0 + prim.local_rect.size,
                     aPosition.xy);
 
     gl_Position = uTransform * vec4(device_pos, 0.0, 1.0);
 #else
     vColor = line.color;
 
     #ifdef WR_FEATURE_TRANSFORM
-        TransformVertexInfo vi = write_transform_vertex_primitive(prim);
+        VertexInfo vi = write_transform_vertex_primitive(prim);
     #else
         VertexInfo vi = write_vertex(prim.local_rect,
                                      prim.local_clip_rect,
                                      prim.z,
                                      prim.layer,
                                      prim.task,
                                      prim.local_rect);
     #endif
@@ -162,24 +158,22 @@ vec2 get_distance_vector(vec2 b0, vec2 b
 // Approximate distance from point to quadratic bezier.
 float approx_distance(vec2 p, vec2 b0, vec2 b1, vec2 b2) {
     return length(get_distance_vector(b0 - p, b1 - p, b2 - p));
 }
 
 void main(void) {
     float alpha = 1.0;
 
+    vec2 local_pos = vLocalPos;
+
 #ifdef WR_FEATURE_CACHE
-    vec2 local_pos = vLocalPos;
 #else
     #ifdef WR_FEATURE_TRANSFORM
-        alpha = 0.0;
-        vec2 local_pos = init_transform_fs(vLocalPos, alpha);
-    #else
-        vec2 local_pos = vLocalPos;
+        alpha = init_transform_fs(vLocalPos);
     #endif
 
         alpha *= do_clip();
 #endif
 
     // Find the appropriate distance to apply the step over.
     float aa_range = compute_aa_range(local_pos);
 
--- a/gfx/webrender/res/ps_rectangle.glsl
+++ b/gfx/webrender/res/ps_rectangle.glsl
@@ -2,31 +2,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include shared,prim_shared
 
 varying vec4 vColor;
 
 #ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
+varying vec2 vLocalPos;
 #endif
 
 #ifdef WR_VERTEX_SHADER
 void main(void) {
     Primitive prim = load_primitive();
     Rectangle rect = fetch_rectangle(prim.specific_prim_address);
     vColor = rect.color;
 #ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
-                                                    prim.local_clip_rect,
-                                                    rect.edge_aa_segment_mask,
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task);
+    VertexInfo vi = write_transform_vertex(prim.local_rect,
+                                           prim.local_clip_rect,
+                                           rect.edge_aa_segment_mask,
+                                           prim.z,
+                                           prim.layer,
+                                           prim.task);
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
                                  prim.local_rect);
@@ -37,18 +37,17 @@ void main(void) {
 #endif
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     float alpha = 1.0;
 #ifdef WR_FEATURE_TRANSFORM
-    alpha = 0.0;
-    init_transform_fs(vLocalPos, alpha);
+    alpha = init_transform_fs(vLocalPos);
 #endif
 
 #ifdef WR_FEATURE_CLIP
     alpha *= do_clip();
 #endif
     oFragColor = vColor * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -3,67 +3,109 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include shared,prim_shared
 
 flat varying vec4 vColor;
 varying vec3 vUv;
 flat varying vec4 vUvBorder;
 
-#ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
-#endif
-
 #ifdef WR_VERTEX_SHADER
 
 #define MODE_ALPHA              0
 #define MODE_SUBPX_CONST_COLOR  1
 #define MODE_SUBPX_PASS0        2
 #define MODE_SUBPX_PASS1        3
 #define MODE_SUBPX_BG_PASS0     4
 #define MODE_SUBPX_BG_PASS1     5
 #define MODE_SUBPX_BG_PASS2     6
 #define MODE_COLOR_BITMAP       7
 
+VertexInfo write_text_vertex(vec2 local_pos,
+                             RectWithSize local_clip_rect,
+                             float z,
+                             Layer layer,
+                             PictureTask task,
+                             RectWithSize snap_rect) {
+    // Clamp to the two local clip rects.
+    vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect), layer.local_clip_rect);
+
+    // Transform the current vertex to world space.
+    vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0);
+
+    // Convert the world positions to device pixel space.
+    vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
+
+    // Apply offsets for the render task to get correct screen location.
+    vec2 final_pos = device_pos -
+                     task.content_origin +
+                     task.common_data.task_rect.p0;
+
+#ifdef WR_FEATURE_GLYPH_TRANSFORM
+    // For transformed subpixels, we just need to align the glyph origin to a device pixel.
+    // Only check the layer transform's translation since the scales and axes match.
+    vec2 world_snap_p0 = snap_rect.p0 + layer.transform[3].xy * uDevicePixelRatio;
+    final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0;
+#elif !defined(WR_FEATURE_TRANSFORM)
+    // Compute the snapping offset only if the layer transform is axis-aligned.
+    final_pos += compute_snap_offset(clamped_local_pos, layer, snap_rect);
+#endif
+
+    gl_Position = uTransform * vec4(final_pos, z, 1.0);
+
+    VertexInfo vi = VertexInfo(clamped_local_pos, device_pos);
+    return vi;
+}
+
 void main(void) {
     Primitive prim = load_primitive();
     TextRun text = fetch_text_run(prim.specific_prim_address);
 
     int glyph_index = prim.user_data0;
     int resource_address = prim.user_data1;
 
     Glyph glyph = fetch_glyph(prim.specific_prim_address,
                               glyph_index,
                               text.subpx_dir);
     GlyphResource res = fetch_glyph_resource(resource_address);
 
-    vec2 local_pos = glyph.offset +
-                     text.offset +
-                     vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
+#ifdef WR_FEATURE_GLYPH_TRANSFORM
+    // Transform from local space to glyph space.
+    mat2 transform = mat2(prim.layer.transform) * uDevicePixelRatio;
 
-    RectWithSize local_rect = RectWithSize(local_pos,
-                                           (res.uv_rect.zw - res.uv_rect.xy) * res.scale / uDevicePixelRatio);
+    // Compute the glyph rect in glyph space.
+    RectWithSize glyph_rect = RectWithSize(res.offset + transform * (text.offset + glyph.offset),
+                                           res.uv_rect.zw - res.uv_rect.xy);
+
+    // Select the corner of the glyph rect that we are processing.
+    // Transform it from glyph space into local space.
+    vec2 local_pos = inverse(transform) * (glyph_rect.p0 + glyph_rect.size * aPosition.xy);
+#else
+    // Scale from glyph space to local space.
+    float scale = res.scale / uDevicePixelRatio;
 
-#ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex(local_rect,
-                                                    prim.local_clip_rect,
-                                                    vec4(0.0),
-                                                    prim.z,
-                                                    prim.layer,
-                                                    prim.task);
-    vLocalPos = vi.local_pos;
-    vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size;
+    // Compute the glyph rect in local space.
+    RectWithSize glyph_rect = RectWithSize(scale * res.offset + text.offset + glyph.offset,
+                                           scale * (res.uv_rect.zw - res.uv_rect.xy));
+
+    // Select the corner of the glyph rect that we are processing.
+    vec2 local_pos = glyph_rect.p0 + glyph_rect.size * aPosition.xy;
+#endif
+
+    VertexInfo vi = write_text_vertex(local_pos,
+                                      prim.local_clip_rect,
+                                      prim.z,
+                                      prim.layer,
+                                      prim.task,
+                                      glyph_rect);
+
+#ifdef WR_FEATURE_GLYPH_TRANSFORM
+    vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size;
 #else
-    VertexInfo vi = write_vertex(local_rect,
-                                 prim.local_clip_rect,
-                                 prim.z,
-                                 prim.layer,
-                                 prim.task,
-                                 local_rect);
-    vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
+    vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
 #ifdef WR_FEATURE_SUBPX_BG_PASS1
     vColor = vec4(text.color.a) * text.bg_color;
 #else
     switch (uMode) {
@@ -93,21 +135,17 @@ void main(void) {
 }
 #endif
 
 #ifdef WR_FRAGMENT_SHADER
 void main(void) {
     vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z);
     vec4 mask = texture(sColor0, tc);
 
-    float alpha = 1.0;
-#ifdef WR_FEATURE_TRANSFORM
-    init_transform_fs(vLocalPos, alpha);
-#endif
-    alpha *= do_clip();
+    float alpha = do_clip();
 
 #ifdef WR_FEATURE_SUBPX_BG_PASS1
     mask.rgb = vec3(mask.a) - mask.rgb;
 #endif
 
     oFragColor = vColor * mask * alpha;
 }
 #endif
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -13,36 +13,35 @@ flat varying vec2 vTextureOffsetV; // Of
 flat varying vec2 vTextureSizeY;   // Size of the y plane in the texture atlas.
 flat varying vec2 vTextureSizeUv;  // Size of the u and v planes in the texture atlas.
 flat varying vec2 vStretchSize;
 flat varying vec2 vHalfTexelY;     // Normalized length of the half of a Y texel.
 flat varying vec2 vHalfTexelUv;    // Normalized length of the half of u and v texels.
 flat varying vec3 vLayers;
 
 #ifdef WR_FEATURE_TRANSFORM
-varying vec3 vLocalPos;
 flat varying vec4 vLocalRect;
-#else
+#endif
+
 varying vec2 vLocalPos;
-#endif
 
 #ifdef WR_VERTEX_SHADER
 struct YuvImage {
     vec2 size;
 };
 
 YuvImage fetch_yuv_image(int address) {
     vec4 data = fetch_from_resource_cache_1(address);
     return YuvImage(data.xy);
 }
 
 void main(void) {
     Primitive prim = load_primitive();
 #ifdef WR_FEATURE_TRANSFORM
-    TransformVertexInfo vi = write_transform_vertex_primitive(prim);
+    VertexInfo vi = write_transform_vertex_primitive(prim);
     vLocalPos = vi.local_pos;
     vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.p0 + prim.local_rect.size);
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
@@ -141,22 +140,21 @@ const mat3 YuvColorMatrix = mat3(
     1.16438,  1.16438,  1.16438,
     0.0    , -0.21325,  2.11240,
     1.79274, -0.53291,  0.0
 );
 #endif
 
 void main(void) {
 #ifdef WR_FEATURE_TRANSFORM
-    float alpha = 0.0;
-    vec2 pos = init_transform_fs(vLocalPos, alpha);
+    float alpha = init_transform_fs(vLocalPos);
 
     // We clamp the texture coordinate calculation here to the local rectangle boundaries,
     // which makes the edge of the texture stretch instead of repeat.
-    vec2 relative_pos_in_rect = clamp(pos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
+    vec2 relative_pos_in_rect = clamp(vLocalPos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
 #else
     float alpha = 1.0;;
     vec2 relative_pos_in_rect = vLocalPos;
 #endif
 
     alpha *= do_clip();
 
     // We clamp the texture coordinates to the half-pixel offset from the borders
--- a/gfx/webrender/src/box_shadow.rs
+++ b/gfx/webrender/src/box_shadow.rs
@@ -12,30 +12,34 @@ use prim_store::{PrimitiveContainer, Rec
 use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive};
 use picture::PicturePrimitive;
 use util::RectHelpers;
 use render_task::MAX_BLUR_STD_DEVIATION;
 
 // The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
 pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
 
+// Maximum blur radius.
+// Taken from https://searchfox.org/mozilla-central/rev/c633ffa4c4611f202ca11270dcddb7b29edddff8/layout/painting/nsCSSRendering.cpp#4412
+pub const MAX_BLUR_RADIUS : f32 = 300.;
+
 // The amount of padding added to the border corner drawn in the box shadow
 // mask. This ensures that we get a few pixels past the corner that can be
 // blurred without being affected by the border radius.
 pub const MASK_CORNER_PADDING: f32 = 4.0;
 
 impl FrameBuilder {
     pub fn add_box_shadow(
         &mut self,
         pipeline_id: PipelineId,
         clip_and_scroll: ClipAndScrollInfo,
         prim_info: &LayerPrimitiveInfo,
         box_offset: &LayerVector2D,
         color: &ColorF,
-        blur_radius: f32,
+        mut blur_radius: f32,
         spread_radius: f32,
         border_radius: BorderRadius,
         clip_mode: BoxShadowClipMode,
     ) {
         if color.a == 0.0 {
             return;
         }
 
@@ -43,16 +47,17 @@ impl FrameBuilder {
             BoxShadowClipMode::Outset => {
                 (spread_radius, ClipMode::Clip)
             }
             BoxShadowClipMode::Inset => {
                 (-spread_radius, ClipMode::ClipOut)
             }
         };
 
+        blur_radius = f32::min(blur_radius, MAX_BLUR_RADIUS);
         let shadow_radius = adjust_border_radius_for_box_shadow(
             border_radius,
             spread_amount,
         );
         let shadow_rect = prim_info.rect
             .translate(box_offset)
             .inflate(spread_amount, spread_amount);
 
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat};
 use debug_font_data;
 use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, VAO};
 use device::{TextureFilter, TextureTarget, VertexAttribute, VertexAttributeKind, VertexUsageHint};
 use euclid::{Point2D, Rect, Size2D, Transform3D};
 use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
-use internal_types::RenderTargetMode;
 use std::f32;
 
 #[derive(Debug, Copy, Clone)]
 enum DebugSampler {
     Font,
 }
 
 impl Into<TextureSlot> for DebugSampler {
@@ -119,17 +118,17 @@ impl DebugRenderer {
 
         let mut font_texture = device.create_texture(TextureTarget::Array);
         device.init_texture(
             &mut font_texture,
             debug_font_data::BMP_WIDTH,
             debug_font_data::BMP_HEIGHT,
             ImageFormat::A8,
             TextureFilter::Linear,
-            RenderTargetMode::None,
+            None,
             1,
             Some(&debug_font_data::FONT_BITMAP),
         );
 
         DebugRenderer {
             font_vertices: Vec::new(),
             font_indices: Vec::new(),
             line_vertices: Vec::new(),
--- a/gfx/webrender/src/debug_server.rs
+++ b/gfx/webrender/src/debug_server.rs
@@ -185,29 +185,17 @@ impl PassList {
 
     pub fn add(&mut self, pass: Pass) {
         self.passes.push(pass);
     }
 }
 
 #[derive(Serialize)]
 pub struct Pass {
-    targets: Vec<Target>,
-}
-
-impl Pass {
-    pub fn new() -> Pass {
-        Pass {
-            targets: Vec::new(),
-        }
-    }
-
-    pub fn add(&mut self, target: Target) {
-        self.targets.push(target);
-    }
+    pub targets: Vec<Target>,
 }
 
 #[derive(Serialize)]
 pub struct Target {
     kind: &'static str,
     batches: Vec<Batch>,
 }
 
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -2,18 +2,18 @@
  * 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/. */
 
 use super::shader_source;
 use api::{ColorF, ImageFormat};
 use api::{DeviceIntRect, DeviceUintSize};
 use euclid::Transform3D;
 use gleam::gl;
-use internal_types::RenderTargetMode;
-use internal_types::FastHashMap;
+use internal_types::{FastHashMap, RenderTargetInfo};
+use std::cell::RefCell;
 use std::fs::File;
 use std::io::Read;
 use std::iter::repeat;
 use std::mem;
 use std::ops::Add;
 use std::path::PathBuf;
 use std::ptr;
 use std::rc::Rc;
@@ -148,26 +148,30 @@ fn get_shader_source(shader_name: &str, 
     shader_source::SHADERS
         .get(shader_name)
         .map(|s| s.to_string())
 }
 
 // Parse a shader string for imports. Imports are recursively processed, and
 // prepended to the list of outputs.
 fn parse_shader_source(source: String, base_path: &Option<PathBuf>, output: &mut String) {
-    for line in source.lines() {
+    output.push_str(SHADER_LINE_MARKER);
+
+    for (line_num, line) in source.lines().enumerate() {
         if line.starts_with(SHADER_IMPORT) {
             let imports = line[SHADER_IMPORT.len() ..].split(",");
 
             // For each import, get the source, and recurse.
             for import in imports {
                 if let Some(include) = get_shader_source(import, base_path) {
                     parse_shader_source(include, base_path, output);
                 }
             }
+
+            output.push_str(&format!("#line {}\n", line_num+1));
         } else {
             output.push_str(line);
             output.push_str("\n");
         }
     }
 }
 
 pub fn build_shader_strings(
@@ -199,19 +203,17 @@ pub fn build_shader_strings(
 
     // Parse the main .glsl file, including any imports
     // and append them to the list of sources.
     let mut shared_result = String::new();
     if let Some(shared_source) = get_shader_source(base_filename, override_path) {
         parse_shader_source(shared_source, override_path, &mut shared_result);
     }
 
-    vs_source.push_str(SHADER_LINE_MARKER);
     vs_source.push_str(&shared_result);
-    fs_source.push_str(SHADER_LINE_MARKER);
     fs_source.push_str(&shared_result);
 
     // Append legacy (.vs and .fs) files if they exist.
     // TODO(gw): Once all shaders are ported to just use the
     //           .glsl file, we can remove this code.
     let vs_name = format!("{}.vs", base_filename);
     if let Some(old_vs_source) = get_shader_source(&vs_name, override_path) {
         vs_source.push_str(SHADER_LINE_MARKER);
@@ -386,17 +388,17 @@ pub struct Texture {
     id: gl::GLuint,
     target: gl::GLuint,
     layer_count: i32,
     format: ImageFormat,
     width: u32,
     height: u32,
 
     filter: TextureFilter,
-    mode: RenderTargetMode,
+    render_target: Option<RenderTargetInfo>,
     fbo_ids: Vec<FBOId>,
     depth_rb: Option<RBOId>,
 }
 
 impl Texture {
     pub fn get_dimensions(&self) -> DeviceUintSize {
         DeviceUintSize::new(self.width, self.height)
     }
@@ -414,16 +416,20 @@ impl Texture {
             ImageFormat::A8 => 1,
             ImageFormat::RGB8 => 3,
             ImageFormat::BGRA8 => 4,
             ImageFormat::RG8 => 2,
             ImageFormat::RGBAF32 => 16,
             ImageFormat::Invalid => unreachable!(),
         }
     }
+
+    pub fn has_depth(&self) -> bool {
+        self.depth_rb.is_some()
+    }
 }
 
 impl Drop for Texture {
     fn drop(&mut self) {
         debug_assert!(thread::panicking() || self.id == 0);
     }
 }
 
@@ -482,67 +488,57 @@ pub struct RBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct VBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 struct IBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug)]
-struct ProgramSources {
+pub struct ProgramSources {
     renderer_name: String,
     vs_source: String,
     fs_source: String,
 }
 
 impl ProgramSources {
     fn new(renderer_name: String, vs_source: String, fs_source: String) -> Self {
         ProgramSources {
             renderer_name,
             vs_source,
             fs_source,
         }
     }
 }
 
-struct ProgramBinary {
+pub struct ProgramBinary {
     binary: Vec<u8>,
     format: gl::GLenum,
 }
 
 impl ProgramBinary {
     fn new(binary: Vec<u8>, format: gl::GLenum) -> Self {
         ProgramBinary {
             binary,
             format
         }
     }
 }
 
 pub struct ProgramCache {
-    binaries: FastHashMap<ProgramSources, ProgramBinary>,
+    pub binaries: RefCell<FastHashMap<ProgramSources, ProgramBinary>>,
 }
 
 impl ProgramCache {
-    pub fn new() -> Self {
-        ProgramCache {
-            binaries: FastHashMap::default(),
-        }
-    }
-
-    fn get(&self, sources: &ProgramSources) -> Option<&ProgramBinary> {
-      self.binaries.get(&sources)
-    }
-
-    fn contains(&self, sources: &ProgramSources) -> bool {
-      self.binaries.contains_key(&sources)
-    }
-
-    fn insert(&mut self, sources: ProgramSources, binary: ProgramBinary) {
-      self.binaries.insert(sources, binary);
+    pub fn new() -> Rc<Self> {
+        Rc::new(
+            ProgramCache {
+                binaries: RefCell::new(FastHashMap::default()),
+            }
+        )
     }
 }
 
 #[derive(Debug, Copy, Clone)]
 pub enum VertexUsageHint {
     Static,
     Dynamic,
     Stream,
@@ -572,53 +568,54 @@ pub struct Capabilities {
 }
 
 #[derive(Clone, Debug)]
 pub enum ShaderError {
     Compilation(String, String), // name, error mssage
     Link(String, String),        // name, error message
 }
 
-pub struct Device<'a> {
+pub struct Device {
     gl: Rc<gl::Gl>,
     // device state
     bound_textures: [gl::GLuint; 16],
     bound_program: gl::GLuint,
     bound_vao: gl::GLuint,
     bound_pbo: gl::GLuint,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
     default_read_fbo: gl::GLuint,
     default_draw_fbo: gl::GLuint,
-    device_pixel_ratio: f32,
+
+    pub device_pixel_ratio: f32,
 
     // HW or API capabilties
     capabilities: Capabilities,
 
     // debug
     inside_frame: bool,
 
     // resources
     resource_override_path: Option<PathBuf>,
 
     max_texture_size: u32,
     renderer_name: String,
-    cached_programs: Option<&'a mut ProgramCache>,
+    cached_programs: Option<Rc<ProgramCache>>,
 
     // Frame counter. This is used to map between CPU
     // frames and GPU frames.
     frame_id: FrameId,
 }
 
-impl<'a> Device<'a> {
+impl Device {
     pub fn new(
         gl: Rc<gl::Gl>,
         resource_override_path: Option<PathBuf>,
         _file_changed_handler: Box<FileWatcherHandler>,
-        cached_programs: Option<&mut ProgramCache>,
+        cached_programs: Option<Rc<ProgramCache>>,
     ) -> Device {
         let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32;
         let renderer_name = gl.get_string(gl::RENDERER);
 
         Device {
             gl,
             resource_override_path,
             // This is initialized to 1 by default, but it is set
@@ -649,17 +646,17 @@ impl<'a> Device<'a> {
     pub fn gl(&self) -> &gl::Gl {
         &*self.gl
     }
 
     pub fn rc_gl(&self) -> &Rc<gl::Gl> {
         &self.gl
     }
 
-    pub fn update_program_cache(&mut self, cached_programs: &'a mut ProgramCache) {
+    pub fn update_program_cache(&mut self, cached_programs: Rc<ProgramCache>) {
         self.cached_programs = Some(cached_programs);
     }
 
     pub fn max_texture_size(&self) -> u32 {
         self.max_texture_size
     }
 
     pub fn get_capabilities(&self) -> &Capabilities {
@@ -691,20 +688,19 @@ impl<'a> Device<'a> {
         } else {
             if !log.is_empty() {
                 println!("Warnings detected on shader: {:?}\n{}", name, log);
             }
             Ok(id)
         }
     }
 
-    pub fn begin_frame(&mut self, device_pixel_ratio: f32) -> FrameId {
+    pub fn begin_frame(&mut self) -> FrameId {
         debug_assert!(!self.inside_frame);
         self.inside_frame = true;
-        self.device_pixel_ratio = device_pixel_ratio;
 
         // Retrive the currently set FBO.
         let default_read_fbo = self.gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING);
         self.default_read_fbo = default_read_fbo as gl::GLuint;
         let default_draw_fbo = self.gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING);
         self.default_draw_fbo = default_draw_fbo as gl::GLuint;
 
         // Texture state
@@ -798,28 +794,27 @@ impl<'a> Device<'a> {
             self.bound_draw_fbo = fbo_id;
             fbo_id.bind(self.gl(), FBOTarget::Draw);
         }
 
         if let Some(dimensions) = dimensions {
             self.gl.viewport(
                 0,
                 0,
-                dimensions.width as gl::GLint,
-                dimensions.height as gl::GLint,
+                dimensions.width as _,
+                dimensions.height as _,
             );
         }
     }
 
     pub fn create_fbo_for_external_texture(&mut self, texture_id: u32) -> FBOId {
         let fbo = FBOId(self.gl.gen_framebuffers(1)[0]);
         self.bind_external_draw_target(fbo);
-        self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo.0);
         self.gl.framebuffer_texture_2d(
-            gl::FRAMEBUFFER,
+            gl::DRAW_FRAMEBUFFER,
             gl::COLOR_ATTACHMENT0,
             gl::TEXTURE_2D,
             texture_id,
             0,
         );
         fbo
     }
 
@@ -849,17 +844,17 @@ impl<'a> Device<'a> {
         Texture {
             id: self.gl.gen_textures(1)[0],
             target: target.to_gl_target(),
             width: 0,
             height: 0,
             layer_count: 0,
             format: ImageFormat::Invalid,
             filter: TextureFilter::Nearest,
-            mode: RenderTargetMode::None,
+            render_target: None,
             fbo_ids: vec![],
             depth_rb: None,
         }
     }
 
     fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
         let filter = match filter {
             TextureFilter::Nearest => gl::NEAREST,
@@ -879,42 +874,43 @@ impl<'a> Device<'a> {
 
     pub fn init_texture(
         &mut self,
         texture: &mut Texture,
         width: u32,
         height: u32,
         format: ImageFormat,
         filter: TextureFilter,
-        mode: RenderTargetMode,
+        render_target: Option<RenderTargetInfo>,
         layer_count: i32,
         pixels: Option<&[u8]>,
     ) {
         debug_assert!(self.inside_frame);
 
         let resized = texture.width != width || texture.height != height;
 
         texture.format = format;
         texture.width = width;
         texture.height = height;
         texture.filter = filter;
         texture.layer_count = layer_count;
-        texture.mode = mode;
+        texture.render_target = render_target;
 
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(self.gl(), format);
         let type_ = gl_type_for_texture_format(format);
 
         self.bind_texture(DEFAULT_TEXTURE, texture);
         self.set_texture_parameters(texture.target, filter);
 
-        match mode {
-            RenderTargetMode::RenderTarget => {
-                self.update_texture_storage(texture, resized);
+        match render_target {
+            Some(info) => {
+                assert!(pixels.is_none());
+                self.update_texture_storage(texture, &info, resized);
             }
-            RenderTargetMode::None => {
+            None => {
                 let expanded_data: Vec<u8>;
                 let actual_pixels = if pixels.is_some() && format == ImageFormat::A8 &&
                     cfg!(any(target_arch = "arm", target_arch = "aarch64"))
                 {
                     expanded_data = pixels
                         .unwrap()
                         .iter()
                         .flat_map(|&byte| repeat(byte).take(4))
@@ -953,100 +949,109 @@ impl<'a> Device<'a> {
                         );
                     }
                     _ => panic!("BUG: Unexpected texture target!"),
                 }
             }
         }
     }
 
-    /// Updates the texture storage for the texture, creating
-    /// FBOs as required.
-    fn update_texture_storage(&mut self, texture: &mut Texture, resized: bool) {
+    /// Updates the texture storage for the texture, creating FBOs as required.
+    fn update_texture_storage(
+        &mut self,
+        texture: &mut Texture,
+        rt_info: &RenderTargetInfo,
+        is_resized: bool,
+    ) {
         assert!(texture.layer_count > 0);
         assert_eq!(texture.target, gl::TEXTURE_2D_ARRAY);
 
         let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32;
-        // If the texture is already the required size skip.
-        if needed_layer_count == 0 && !resized {
-            return;
-        }
+        let allocate_color = needed_layer_count != 0 || is_resized;
 
-        let (internal_format, gl_format) =
-            gl_texture_formats_for_image_format(&*self.gl, texture.format);
-        let type_ = gl_type_for_texture_format(texture.format);
+        if allocate_color {
+            let (internal_format, gl_format) =
+                gl_texture_formats_for_image_format(&*self.gl, texture.format);
+            let type_ = gl_type_for_texture_format(texture.format);
 
-        self.gl.tex_image_3d(
-            texture.target,
-            0,
-            internal_format as gl::GLint,
-            texture.width as gl::GLint,
-            texture.height as gl::GLint,
-            texture.layer_count,
-            0,
-            gl_format,
-            type_,
-            None,
-        );
+            self.gl.tex_image_3d(
+                texture.target,
+                0,
+                internal_format as gl::GLint,
+                texture.width as gl::GLint,
+                texture.height as gl::GLint,
+                texture.layer_count,
+                0,
+                gl_format,
+                type_,
+                None,
+            );
+        }
 
         if needed_layer_count > 0 {
             // Create more framebuffers to fill the gap
             let new_fbos = self.gl.gen_framebuffers(needed_layer_count);
             texture
                 .fbo_ids
                 .extend(new_fbos.into_iter().map(FBOId));
         } else if needed_layer_count < 0 {
             // Remove extra framebuffers
             for old in texture.fbo_ids.drain(texture.layer_count as usize ..) {
                 self.gl.delete_framebuffers(&[old.0]);
             }
         }
 
-        let (depth_rb, depth_alloc) = match texture.depth_rb {
-            Some(rbo) => (rbo.0, resized),
-            None => {
+        let (mut depth_rb, allocate_depth) = match texture.depth_rb {
+            Some(rbo) => (rbo.0, is_resized || !rt_info.has_depth),
+            None if rt_info.has_depth => {
                 let renderbuffer_ids = self.gl.gen_renderbuffers(1);
                 let depth_rb = renderbuffer_ids[0];
                 texture.depth_rb = Some(RBOId(depth_rb));
                 (depth_rb, true)
-            }
+            },
+            None => (0, false),
         };
 
-        if depth_alloc {
-            self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
-            self.gl.renderbuffer_storage(
-                gl::RENDERBUFFER,
-                gl::DEPTH_COMPONENT24,
-                texture.width as gl::GLsizei,
-                texture.height as gl::GLsizei,
-            );
+        if allocate_depth {
+            if rt_info.has_depth {
+                self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
+                self.gl.renderbuffer_storage(
+                    gl::RENDERBUFFER,
+                    gl::DEPTH_COMPONENT24,
+                    texture.width as gl::GLsizei,
+                    texture.height as gl::GLsizei,
+                );
+            } else {
+                self.gl.delete_renderbuffers(&[depth_rb]);
+                depth_rb = 0;
+                texture.depth_rb = None;
+            }
         }
 
-        for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() {
-            self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0);
-            self.gl.framebuffer_texture_layer(
-                gl::FRAMEBUFFER,
-                gl::COLOR_ATTACHMENT0,
-                texture.id,
-                0,
-                fbo_index as gl::GLint,
-            );
-            self.gl.framebuffer_renderbuffer(
-                gl::FRAMEBUFFER,
-                gl::DEPTH_ATTACHMENT,
-                gl::RENDERBUFFER,
-                depth_rb,
-            );
+        if allocate_color || allocate_depth {
+            for (fbo_index, &fbo_id) in texture.fbo_ids.iter().enumerate() {
+                self.bind_external_draw_target(fbo_id);
+                self.gl.framebuffer_texture_layer(
+                    gl::DRAW_FRAMEBUFFER,
+                    gl::COLOR_ATTACHMENT0,
+                    texture.id,
+                    0,
+                    fbo_index as gl::GLint,
+                );
+                self.gl.framebuffer_renderbuffer(
+                    gl::DRAW_FRAMEBUFFER,
+                    gl::DEPTH_ATTACHMENT,
+                    gl::RENDERBUFFER,
+                    depth_rb,
+                );
+            }
+            // restore the previous FBO
+            let bound_fbo = self.bound_draw_fbo;
+            self.bind_external_draw_target(bound_fbo);
         }
-
-        // TODO(gw): Hack! Modify the code above to use the normal binding interfaces the device exposes.
-        self.gl
-            .bind_framebuffer(gl::READ_FRAMEBUFFER, self.bound_read_fbo.0);
-        self.gl
-            .bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.bound_draw_fbo.0);
     }
 
     pub fn blit_render_target(&mut self, src_rect: DeviceIntRect, dest_rect: DeviceIntRect) {
         debug_assert!(self.inside_frame);
 
         self.gl.blit_framebuffer(
             src_rect.origin.x,
             src_rect.origin.y,
@@ -1155,17 +1160,17 @@ impl<'a> Device<'a> {
         let sources = ProgramSources::new(self.renderer_name.clone(), vs_source, fs_source);
 
         // Create program
         let pid = self.gl.create_program();
 
         let mut loaded = false;
 
         if let Some(ref cached_programs) = self.cached_programs {
-            if let Some(binary) = cached_programs.get(&sources)
+            if let Some(binary) = cached_programs.binaries.borrow().get(&sources)
             {
                 self.gl.program_binary(pid, binary.format, &binary.binary);
 
                 if self.gl.get_program_iv(pid, gl::LINK_STATUS) == (0 as gl::GLint) {
                     let error_log = self.gl.get_program_info_log(pid);
                     println!(
                       "Failed to load a program object with a program binary: {:?} renderer {}\n{}",
                       base_filename,
@@ -1233,21 +1238,21 @@ impl<'a> Device<'a> {
                     base_filename,
                     error_log
                 );
                 self.gl.delete_program(pid);
                 return Err(ShaderError::Link(base_filename.to_string(), error_log));
             }
         }
 
-        if let Some(ref mut cached_programs) = self.cached_programs {
-            if !cached_programs.contains(&sources) {
+        if let Some(ref cached_programs) = self.cached_programs {
+            if !cached_programs.binaries.borrow().contains_key(&sources) {
                 let (buffer, format) = self.gl.get_program_binary(pid);
                 if buffer.len() > 0 {
-                  cached_programs.insert(sources, ProgramBinary::new(buffer, format));
+                  cached_programs.binaries.borrow_mut().insert(sources, ProgramBinary::new(buffer, format));
                 }
             }
         }
 
         let u_transform = self.gl.get_uniform_location(pid, "uTransform");
         let u_device_pixel_ratio = self.gl.get_uniform_location(pid, "uDevicePixelRatio");
         let u_mode = self.gl.get_uniform_location(pid, "uMode");
 
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -1,33 +1,33 @@
 
 /* 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/. */
 
 use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
-use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
+use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch, FilterOp};
 use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerVector2D};
 use api::{LayoutRect, LayoutSize};
 use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
 use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
 use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
 use clip::ClipRegion;
 use clip_scroll_node::StickyFrameInfo;
 use clip_scroll_tree::{ClipScrollTree, ScrollStates};
 use euclid::rect;
 use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
 use gpu_cache::GpuCache;
-use internal_types::{FastHashMap, FastHashSet, RendererFrame};
+use internal_types::{FastHashMap, FastHashSet, RenderedDocument};
 use prim_store::RectangleContent;
 use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
 use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
 use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
-use tiling::{CompositeOps, Frame};
+use tiling::CompositeOps;
 use util::ComplexClipRegionHelpers;
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
     r: 0.3,
     g: 0.3,
@@ -257,37 +257,34 @@ impl<'a> FlattenContext<'a> {
                 self.builder.current_reference_frame_id(),
             ));
         }
 
         // If we have a transformation, we establish a new reference frame. This means
         // that fixed position stacking contexts are positioned relative to us.
         let is_reference_frame =
             stacking_context.transform.is_some() || stacking_context.perspective.is_some();
-        if is_reference_frame {
-            let origin = reference_frame_relative_offset + bounds.origin.to_vector();
+        let origin = reference_frame_relative_offset + bounds.origin.to_vector();
+        reference_frame_relative_offset = if is_reference_frame {
             let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
             let mut clip_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
             clip_id = self.builder.push_reference_frame(
                 Some(clip_id),
                 pipeline_id,
                 &reference_frame_bounds,
                 stacking_context.transform,
                 stacking_context.perspective,
                 origin,
                 false,
                 self.clip_scroll_tree,
             );
             self.replacements.push((context_scroll_node_id, clip_id));
-            reference_frame_relative_offset = LayerVector2D::zero();
+            LayerVector2D::zero()
         } else {
-            reference_frame_relative_offset = LayerVector2D::new(
-                reference_frame_relative_offset.x + bounds.origin.x,
-                reference_frame_relative_offset.y + bounds.origin.y,
-            );
+            origin
         };
 
         let sc_scroll_node_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
 
         self.builder.push_stacking_context(
             pipeline_id,
             composition_operations,
             stacking_context.transform_style,
@@ -1019,25 +1016,27 @@ impl<'a> FlattenContext<'a> {
             );
         }
     }
 }
 
 /// Frame context contains the information required to update
 /// (e.g. scroll) a renderer frame builder (`FrameBuilder`).
 pub struct FrameContext {
+    window_size: DeviceUintSize,
     clip_scroll_tree: ClipScrollTree,
     pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
     id: FrameId,
     frame_builder_config: FrameBuilderConfig,
 }
 
 impl FrameContext {
     pub fn new(config: FrameBuilderConfig) -> Self {
         FrameContext {
+            window_size: DeviceUintSize::zero(),
             pipeline_epoch_map: FastHashMap::default(),
             clip_scroll_tree: ClipScrollTree::new(),
             id: FrameId(0),
             frame_builder_config: config,
         }
     }
 
     pub fn reset(&mut self) -> ScrollStates {
@@ -1078,53 +1077,53 @@ impl FrameContext {
 
     pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
         self.clip_scroll_tree
             .discard_frame_state_for_pipeline(pipeline_id);
     }
 
     pub fn create(
         &mut self,
-        old_builder: Option<FrameBuilder>,
+        old_builder: FrameBuilder,
         scene: &Scene,
         resource_cache: &mut ResourceCache,
         window_size: DeviceUintSize,
         inner_rect: DeviceUintRect,
         device_pixel_ratio: f32,
         output_pipelines: &FastHashSet<PipelineId>,
-    ) -> Option<FrameBuilder> {
+    ) -> FrameBuilder {
         let root_pipeline_id = match scene.root_pipeline_id {
             Some(root_pipeline_id) => root_pipeline_id,
             None => return old_builder,
         };
 
         let root_pipeline = match scene.pipelines.get(&root_pipeline_id) {
             Some(root_pipeline) => root_pipeline,
             None => return old_builder,
         };
 
         if window_size.width == 0 || window_size.height == 0 {
             error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
         }
+        self.window_size = window_size;
 
         let old_scrolling_states = self.reset();
 
         self.pipeline_epoch_map
             .insert(root_pipeline_id, root_pipeline.epoch);
 
         let background_color = root_pipeline
             .background_color
             .and_then(|color| if color.a > 0.0 { Some(color) } else { None });
 
         let frame_builder = {
             let mut roller = FlattenContext {
                 scene,
-                builder: FrameBuilder::new(
-                    old_builder,
-                    window_size,
+                builder: old_builder.recycle(
+                    inner_rect,
                     background_color,
                     self.frame_builder_config,
                 ),
                 clip_scroll_tree: &mut self.clip_scroll_tree,
                 font_instances: resource_cache.get_font_instances(),
                 tiled_image_map: resource_cache.get_tiled_image_map(),
                 pipeline_epochs: Vec::new(),
                 replacements: Vec::new(),
@@ -1160,52 +1159,47 @@ impl FrameContext {
             debug_assert!(roller.builder.picture_stack.is_empty());
 
             self.pipeline_epoch_map.extend(roller.pipeline_epochs.drain(..));
             roller.builder
         };
 
         self.clip_scroll_tree
             .finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
-        Some(frame_builder)
+        frame_builder
     }
 
     pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
         self.pipeline_epoch_map.insert(pipeline_id, epoch);
     }
 
-    fn get_renderer_frame_impl(&self, frame: Option<Frame>) -> RendererFrame {
-        let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
-        RendererFrame::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
-    }
-
-    pub fn build_renderer_frame(
+    pub fn build_rendered_document(
         &mut self,
         frame_builder: &mut FrameBuilder,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         device_pixel_ratio: f32,
+        layer: DocumentLayer,
         pan: LayerPoint,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
-        scene_properties: &SceneProperties,
-    ) -> RendererFrame {
+		scene_properties: &SceneProperties,
+    ) -> RenderedDocument {
         let frame = frame_builder.build(
             resource_cache,
             gpu_cache,
             self.id,
             &mut self.clip_scroll_tree,
             pipelines,
+            self.window_size,
             device_pixel_ratio,
+            layer,
             pan,
             texture_cache_profile,
             gpu_cache_profile,
             scene_properties,
         );
 
-        self.get_renderer_frame_impl(Some(frame))
-    }
-
-    pub fn get_renderer_frame(&self) -> RendererFrame {
-        self.get_renderer_frame_impl(None)
+        let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
+        RenderedDocument::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
     }
 }
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList};
 use api::{ClipAndScrollInfo, ClipId, ColorF, PropertyBinding};
-use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
-use api::{ExtendMode, FontRenderMode, LayoutTransform};
+use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
+use api::{DocumentLayer, ExtendMode, FontRenderMode, LayoutTransform};
 use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
 use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
 use api::{LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
 use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
 use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
 use api::{PremultipliedColorF, WorldPoint, YuvColorSpace, YuvData};
 use app_units::Au;
 use border::ImageBorderSegment;
@@ -24,22 +24,22 @@ use gpu_cache::GpuCache;
 use internal_types::{FastHashMap, FastHashSet};
 use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace};
 use prim_store::{TexelRect, YuvImagePrimitiveCpu};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
 use prim_store::{PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectangleContent, RectanglePrimitive, TextRunPrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_task::{ClearMode, RenderTask, RenderTaskTree};
+use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
 use resource_cache::ResourceCache;
 use scene::{ScenePipeline, SceneProperties};
-use std::{mem, usize, f32, i32};
+use std::{mem, usize, f32};
 use tiling::{CompositeOps, Frame};
-use tiling::{RenderPass, RenderTargetKind};
+use tiling::{RenderPass, RenderPassKind, RenderTargetKind};
 use tiling::{RenderTargetContext, ScrollbarPrimitive};
 use util::{self, pack_as_float, RectHelpers, recycle_vec};
 
 #[derive(Debug)]
 pub struct ScrollbarInfo(pub ClipId, pub LayerRect);
 
 /// Properties of a stacking context that are maintained
 /// during creation of the scene. These structures are
@@ -88,19 +88,19 @@ impl HitTestingItem {
             clip: info.local_clip,
             tag: tag,
         }
     }
 }
 
 pub struct HitTestingRun(Vec<HitTestingItem>, ClipAndScrollInfo);
 
-/// A builder structure for `RendererFrame`
+/// A builder structure for `tiling::Frame`
 pub struct FrameBuilder {
-    screen_size: DeviceUintSize,
+    screen_rect: DeviceUintRect,
     background_color: Option<ColorF>,
     prim_store: PrimitiveStore,
     pub clip_store: ClipStore,
     hit_testing_runs: Vec<HitTestingRun>,
     pub config: FrameBuilderConfig,
 
     // A stack of the current shadow primitives.
     // The sub-Vec stores a buffer of fast-path primitives to be appended on pop.
@@ -142,51 +142,56 @@ impl<'a> PrimitiveContext<'a> {
             display_list,
             clip_node,
             scroll_node,
         }
     }
 }
 
 impl FrameBuilder {
-    pub fn new(
-        previous: Option<Self>,
-        screen_size: DeviceUintSize,
+    pub fn empty() -> Self {
+        FrameBuilder {
+            hit_testing_runs: Vec::new(),
+            shadow_prim_stack: Vec::new(),
+            pending_shadow_contents: Vec::new(),
+            scrollbar_prims: Vec::new(),
+            reference_frame_stack: Vec::new(),
+            picture_stack: Vec::new(),
+            sc_stack: Vec::new(),
+            prim_store: PrimitiveStore::new(),
+            clip_store: ClipStore::new(),
+            screen_rect: DeviceUintRect::zero(),
+            background_color: None,
+            config: FrameBuilderConfig {
+                enable_scrollbars: false,
+                default_font_render_mode: FontRenderMode::Mono,
+                debug: false,
+            },
+        }
+    }
+
+    pub fn recycle(
+        self,
+        screen_rect: DeviceUintRect,
         background_color: Option<ColorF>,
         config: FrameBuilderConfig,
     ) -> Self {
-        match previous {
-            Some(prev) => FrameBuilder {
-                hit_testing_runs: recycle_vec(prev.hit_testing_runs),
-                shadow_prim_stack: recycle_vec(prev.shadow_prim_stack),
-                pending_shadow_contents: recycle_vec(prev.pending_shadow_contents),
-                scrollbar_prims: recycle_vec(prev.scrollbar_prims),
-                reference_frame_stack: recycle_vec(prev.reference_frame_stack),
-                picture_stack: recycle_vec(prev.picture_stack),
-                sc_stack: recycle_vec(prev.sc_stack),
-                prim_store: prev.prim_store.recycle(),
-                clip_store: prev.clip_store.recycle(),
-                screen_size,
-                background_color,
-                config,
-            },
-            None => FrameBuilder {
-                hit_testing_runs: Vec::new(),
-                shadow_prim_stack: Vec::new(),
-                pending_shadow_contents: Vec::new(),
-                scrollbar_prims: Vec::new(),
-                reference_frame_stack: Vec::new(),
-                picture_stack: Vec::new(),
-                sc_stack: Vec::new(),
-                prim_store: PrimitiveStore::new(),
-                clip_store: ClipStore::new(),
-                screen_size,
-                background_color,
-                config,
-            },
+        FrameBuilder {
+            hit_testing_runs: recycle_vec(self.hit_testing_runs),
+            shadow_prim_stack: recycle_vec(self.shadow_prim_stack),
+            pending_shadow_contents: recycle_vec(self.pending_shadow_contents),
+            scrollbar_prims: recycle_vec(self.scrollbar_prims),
+            reference_frame_stack: recycle_vec(self.reference_frame_stack),
+            picture_stack: recycle_vec(self.picture_stack),
+            sc_stack: recycle_vec(self.sc_stack),
+            prim_store: self.prim_store.recycle(),
+            clip_store: self.clip_store.recycle(),
+            screen_rect,
+            background_color,
+            config,
         }
     }
 
     /// Create a primitive and add it to the prim store. This method doesn't
     /// add the primitive to the draw list, so can be used for creating
     /// sub-primitives.
     pub fn create_primitive(
         &mut self,
@@ -1515,34 +1520,38 @@ impl FrameBuilder {
                 });
                 if !flags.contains(HitTestFlags::FIND_ALL) {
                     return result;
                 }
             }
         }
 
         result.items.dedup();
-        return result;
+        result
     }
 
     /// Compute the contribution (bounding rectangles, and resources) of layers and their
     /// primitives in screen space.
     fn build_layer_screen_rects_and_cull_layers(
         &mut self,
         clip_scroll_tree: &mut ClipScrollTree,
         pipelines: &FastHashMap<PipelineId, ScenePipeline>,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
         profile_counters: &mut FrameProfileCounters,
         device_pixel_ratio: f32,
         scene_properties: &SceneProperties,
-    ) {
+    ) -> Option<RenderTaskId> {
         profile_scope!("cull");
 
+        if self.prim_store.cpu_pictures.is_empty() {
+            return None
+        }
+
         // The root picture is always the first one added.
         let prim_run_cmds = mem::replace(&mut self.prim_store.cpu_pictures[0].runs, Vec::new());
         let root_clip_scroll_node = &clip_scroll_tree.nodes[&clip_scroll_tree.root_reference_frame_id()];
 
         let display_list = &pipelines
             .get(&root_clip_scroll_node.pipeline_id)
             .expect("No display list?")
             .display_list;
@@ -1586,16 +1595,17 @@ impl FrameBuilder {
             0.0,
             PremultipliedColorF::TRANSPARENT,
             ClearMode::Transparent,
             RasterizationSpace::Screen,
             child_tasks,
         );
 
         pic.render_task_id = Some(render_tasks.add(root_render_task));
+        pic.render_task_id
     }
 
     fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
         static SCROLLBAR_PADDING: f32 = 8.0;
 
         for scrollbar_prim in &self.scrollbar_prims {
             let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
             let scroll_frame = &clip_scroll_tree.nodes[&scrollbar_prim.clip_id];
@@ -1624,93 +1634,90 @@ impl FrameBuilder {
 
     pub fn build(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         frame_id: FrameId,
         clip_scroll_tree: &mut ClipScrollTree,
         pipelines: &FastHashMap<PipelineId, ScenePipeline>,
+        window_size: DeviceUintSize,
         device_pixel_ratio: f32,
+        layer: DocumentLayer,
         pan: LayerPoint,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
         scene_properties: &SceneProperties,
     ) -> Frame {
         profile_scope!("build");
+        debug_assert!(
+            DeviceUintRect::new(DeviceUintPoint::zero(), window_size)
+                .contains_rect(&self.screen_rect)
+        );
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters
             .total_primitives
             .set(self.prim_store.prim_count());
 
         resource_cache.begin_frame(frame_id);
         gpu_cache.begin_frame();
 
-        let screen_rect = DeviceIntRect::new(
-            DeviceIntPoint::zero(),
-            DeviceIntSize::new(
-                self.screen_size.width as i32,
-                self.screen_size.height as i32,
-            ),
-        );
-
         let mut node_data = Vec::new();
 
         clip_scroll_tree.update_tree(
-            &screen_rect,
+            &self.screen_rect.to_i32(),
             device_pixel_ratio,
             &mut self.clip_store,
             resource_cache,
             gpu_cache,
             pan,
             &mut node_data,
             scene_properties,
         );
 
         self.update_scroll_bars(clip_scroll_tree, gpu_cache);
 
         let mut render_tasks = RenderTaskTree::new();
 
-        self.build_layer_screen_rects_and_cull_layers(
+        let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
             clip_scroll_tree,
             pipelines,
             resource_cache,
             gpu_cache,
             &mut render_tasks,
             &mut profile_counters,
             device_pixel_ratio,
             scene_properties,
         );
 
-        let main_render_task_id = self.prim_store
-                                      .cpu_pictures[0]
-                                      .render_task_id
-                                      .expect("bug: no root render task!");
+        let mut passes = Vec::new();
+        resource_cache.block_until_all_resources_added(gpu_cache, texture_cache_profile);
+
+        if let Some(main_render_task_id) = main_render_task_id {
+            let mut required_pass_count = 0;
+            render_tasks.max_depth(main_render_task_id, 0, &mut required_pass_count);
+            assert_ne!(required_pass_count, 0);
 
-        let mut required_pass_count = 0;
-        render_tasks.max_depth(main_render_task_id, 0, &mut required_pass_count);
+            // Do the allocations now, assigning each tile's tasks to a render
+            // pass and target as required.
+            for _ in 0 .. required_pass_count - 1 {
+                passes.push(RenderPass::new_off_screen(self.screen_rect.size.to_i32()));
+            }
+            passes.push(RenderPass::new_main_framebuffer(self.screen_rect.size.to_i32()));
 
-        resource_cache.block_until_all_resources_added(gpu_cache, texture_cache_profile);
+            render_tasks.assign_to_passes(
+                main_render_task_id,
+                required_pass_count - 1,
+                &mut passes,
+            );
+        }
 
         let mut deferred_resolves = vec![];
 
-        let mut passes = Vec::new();
-
-        // Do the allocations now, assigning each tile's tasks to a render
-        // pass and target as required.
-        for index in 0 .. required_pass_count {
-            passes.push(RenderPass::new(
-                index == required_pass_count - 1,
-                screen_rect.size,
-            ));
-        }
-
-        render_tasks.assign_to_passes(main_render_task_id, passes.len() - 1, &mut passes);
-
         for pass in &mut passes {
             let ctx = RenderTargetContext {
                 device_pixel_ratio,
                 prim_store: &self.prim_store,
                 resource_cache,
                 node_data: &node_data,
                 clip_scroll_tree,
             };
@@ -1719,34 +1726,44 @@ impl FrameBuilder {
                 &ctx,
                 gpu_cache,
                 &mut render_tasks,
                 &mut deferred_resolves,
                 &self.clip_store,
             );
 
             profile_counters.passes.inc();
-            profile_counters
-                .color_targets
-                .add(pass.color_targets.target_count());
-            profile_counters
-                .alpha_targets
-                .add(pass.alpha_targets.target_count());
+
+            match pass.kind {
+                RenderPassKind::MainFramebuffer(_) => {
+                    profile_counters.color_targets.add(1);
+                }
+                RenderPassKind::OffScreen { ref color, ref alpha } => {
+                    profile_counters
+                        .color_targets
+                        .add(color.targets.len());
+                    profile_counters
+                        .alpha_targets
+                        .add(alpha.targets.len());
+                }
+            }
         }
 
         let gpu_cache_updates = gpu_cache.end_frame(gpu_cache_profile);
 
         render_tasks.build();
 
         resource_cache.end_frame();
 
         Frame {
+            window_size,
+            inner_rect: self.screen_rect,
             device_pixel_ratio,
             background_color: self.background_color,
-            window_size: self.screen_size,
+            layer,
             profile_counters,
             passes,
             node_data,
             render_tasks,
             deferred_resolves,
             gpu_cache_updates: Some(gpu_cache_updates),
         }
     }
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -2,48 +2,152 @@
  * 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/. */
 
 #[cfg(test)]
 use api::{IdNamespace, LayoutPoint};
 use api::{ColorF, ColorU, DevicePoint, DeviceUintSize};
 use api::{FontInstancePlatformOptions, FontRenderMode, FontVariation};
 use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey, SubpixelDirection};
-use api::{ImageData, ImageDescriptor, ImageFormat};
+use api::{ImageData, ImageDescriptor, ImageFormat, LayerToWorldTransform};
 use app_units::Au;
 use device::TextureFilter;
 use glyph_cache::{CachedGlyphInfo, GlyphCache};
 use gpu_cache::GpuCache;
 use internal_types::FastHashSet;
 use platform::font::FontContext;
 use profiler::TextureCacheProfileCounters;
 use rayon::ThreadPool;
 use rayon::prelude::*;
+use std::cmp;
 use std::collections::hash_map::Entry;
+use std::hash::{Hash, Hasher};
 use std::mem;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use texture_cache::{TextureCache, TextureCacheHandle};
 
+#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
+pub struct FontTransform {
+    pub scale_x: f32,
+    pub skew_x: f32,
+    pub skew_y: f32,
+    pub scale_y: f32,
+}
+
+// Floats don't impl Hash/Eq/Ord...
+impl Eq for FontTransform {}
+impl Ord for FontTransform {
+    fn cmp(&self, other: &Self) -> cmp::Ordering {
+        self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal)
+    }
+}
+impl Hash for FontTransform {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
+        self.scale_x.to_bits().hash(state);
+        self.skew_x.to_bits().hash(state);
+        self.skew_y.to_bits().hash(state);
+        self.scale_y.to_bits().hash(state);
+    }
+}
+
+impl FontTransform {
+    const QUANTIZE_SCALE: f32 = 1024.0;
+
+    pub fn new(scale_x: f32, skew_x: f32, skew_y: f32, scale_y: f32) -> Self {
+        FontTransform { scale_x, skew_x, skew_y, scale_y }
+    }
+
+    pub fn identity() -> Self {
+        FontTransform::new(1.0, 0.0, 0.0, 1.0)
+    }
+
+    pub fn is_identity(&self) -> bool {
+        *self == FontTransform::identity()
+    }
+
+    pub fn quantize(&self) -> Self {
+        FontTransform::new(
+            (self.scale_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
+            (self.skew_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
+            (self.skew_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
+            (self.scale_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
+        )
+    }
+
+    pub fn determinant(&self) -> f64 {
+        self.scale_x as f64 * self.scale_y as f64 - self.skew_y as f64 * self.skew_x as f64
+    }
+
+    pub fn compute_scale(&self) -> Option<(f64, f64)> {
+        let det = self.determinant();
+        if det != 0.0 {
+            let major = (self.scale_x as f64).hypot(self.skew_y as f64);
+            let minor = det.abs() / major;
+            Some((major, minor))
+        } else {
+            None
+        }
+    }
+
+    pub fn pre_scale(&self, scale_x: f32, scale_y: f32) -> Self {
+        FontTransform::new(
+            self.scale_x * scale_x,
+            self.skew_x * scale_y,
+            self.skew_y * scale_x,
+            self.scale_y * scale_y,
+        )
+    }
+
+    #[allow(dead_code)]
+    pub fn inverse(&self) -> Option<Self> {
+        let det = self.determinant();
+        if det != 0.0 {
+            let inv_det = det.recip() as f32;
+            Some(FontTransform::new(
+                self.scale_y * inv_det,
+                -self.skew_x * inv_det,
+                -self.skew_y * inv_det,
+                self.scale_x * inv_det
+            ))
+        } else {
+            None
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn apply(&self, x: f32, y: f32) -> (f32, f32) {
+        (self.scale_x * x + self.skew_x * y, self.skew_y * x + self.scale_y * y)
+    }
+}
+
+impl<'a> From<&'a LayerToWorldTransform> for FontTransform {
+    fn from(xform: &'a LayerToWorldTransform) -> Self {
+        FontTransform::new(xform.m11, xform.m21, xform.m12, xform.m22)
+    }
+}
+
 #[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
 pub struct FontInstance {
     pub font_key: FontKey,
     // The font size is in *device* pixels, not logical pixels.
     // It is stored as an Au since we need sub-pixel sizes, but
     // can't store as a f32 due to use of this type as a hash key.
     // TODO(gw): Perhaps consider having LogicalAu and DeviceAu
     //           or something similar to that.
     pub size: Au,
     pub color: ColorU,
     pub bg_color: ColorU,
     pub render_mode: FontRenderMode,
     pub subpx_dir: SubpixelDirection,
     pub platform_options: Option<FontInstancePlatformOptions>,
     pub variations: Vec<FontVariation>,
     pub synthetic_italics: bool,
+    pub transform: FontTransform,
 }
 
 impl FontInstance {
     pub fn new(
         font_key: FontKey,
         size: Au,
         color: ColorF,
         bg_color: ColorU,
@@ -58,47 +162,50 @@ impl FontInstance {
             size,
             color: color.into(),
             bg_color,
             render_mode,
             subpx_dir,
             platform_options,
             variations,
             synthetic_italics,
+            transform: FontTransform::identity(),
         }
     }
 
     pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
         match self.subpx_dir {
             SubpixelDirection::None => (0.0, 0.0),
             SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0),
             SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()),
         }
     }
+
+    pub fn get_subpixel_glyph_format(&self) -> GlyphFormat {
+        if self.transform.is_identity() { GlyphFormat::Subpixel } else { GlyphFormat::TransformedSubpixel }
+    }
+
+    #[allow(dead_code)]
+    pub fn get_glyph_format(&self) -> GlyphFormat {
+        match self.render_mode {
+            FontRenderMode::Mono | FontRenderMode::Alpha => GlyphFormat::Alpha,
+            FontRenderMode::Subpixel => self.get_subpixel_glyph_format(),
+            FontRenderMode::Bitmap => GlyphFormat::ColorBitmap,
+        }
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub enum GlyphFormat {
-    Mono,
     Alpha,
     Subpixel,
+    TransformedSubpixel,
     ColorBitmap,
 }
 
-impl From<FontRenderMode> for GlyphFormat {
-    fn from(render_mode: FontRenderMode) -> GlyphFormat {
-        match render_mode {
-            FontRenderMode::Mono => GlyphFormat::Mono,
-            FontRenderMode::Alpha => GlyphFormat::Alpha,
-            FontRenderMode::Subpixel => GlyphFormat::Subpixel,
-            FontRenderMode::Bitmap => GlyphFormat::ColorBitmap,
-        }
-    }
-}
-
 pub struct RasterizedGlyph {
     pub top: f32,
     pub left: f32,
     pub width: u32,
     pub height: u32,
     pub scale: f32,
     pub format: GlyphFormat,
     pub bytes: Vec<u8>,
@@ -391,25 +498,25 @@ impl GlyphRasterizer {
                             height: glyph.height,
                             stride: None,
                             format: ImageFormat::BGRA8,
                             is_opaque: false,
                             offset: 0,
                         },
                         TextureFilter::Linear,
                         ImageData::Raw(glyph_bytes.clone()),
-                        [glyph.left, glyph.top, glyph.scale],
+                        [glyph.left, -glyph.top, glyph.scale],
                         None,
                         gpu_cache,
                     );
                     Some(CachedGlyphInfo {
                         texture_cache_handle,
                         glyph_bytes,
                         size: DeviceUintSize::new(glyph.width, glyph.height),
-                        offset: DevicePoint::new(glyph.left, glyph.top),
+                        offset: DevicePoint::new(glyph.left, -glyph.top),
                         scale: glyph.scale,
                         format: glyph.format,
                     })
                 } else {
                     None
                 });
 
             let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(job.request.font);
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -77,19 +77,18 @@ impl BatchTextures {
     pub fn color(texture: SourceTexture) -> Self {
         BatchTextures {
             colors: [texture, SourceTexture::Invalid, SourceTexture::Invalid],
         }
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
-pub enum RenderTargetMode {
-    None,
-    RenderTarget,
+pub struct RenderTargetInfo {
+    pub has_depth: bool,
 }
 
 #[derive(Debug)]
 pub enum TextureUpdateSource {
     External {
         id: ExternalImageId,
         channel_index: u8,
     },
@@ -98,17 +97,17 @@ pub enum TextureUpdateSource {
 
 #[derive(Debug)]
 pub enum TextureUpdateOp {
     Create {
         width: u32,
         height: u32,
         format: ImageFormat,
         filter: TextureFilter,
-        mode: RenderTargetMode,
+        render_target: Option<RenderTargetInfo>,
         layer_count: i32,
     },
     Update {
         rect: DeviceUintRect,
         stride: Option<u32>,
         offset: u32,
         layer_index: i32,
         source: TextureUpdateSource,
@@ -135,53 +134,53 @@ impl TextureUpdateList {
 
     #[inline]
     pub fn push(&mut self, update: TextureUpdate) {
         self.updates.push(update);
     }
 }
 
 /// Mostly wraps a tiling::Frame, adding a bit of extra information.
-pub struct RendererFrame {
+pub struct RenderedDocument {
     /// The last rendered epoch for each pipeline present in the frame.
     /// This information is used to know if a certain transformation on the layout has
     /// been rendered, which is necessary for reftests.
     pub pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
     /// The layers that are currently affected by the over-scrolling animation.
     pub layers_bouncing_back: FastHashSet<ClipId>,
 
-    pub frame: Option<tiling::Frame>,
+    pub frame: tiling::Frame,
 }
 
-impl RendererFrame {
+impl RenderedDocument {
     pub fn new(
         pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
         layers_bouncing_back: FastHashSet<ClipId>,
-        frame: Option<tiling::Frame>,
+        frame: tiling::Frame,
     ) -> Self {
-        RendererFrame {
+        RenderedDocument {
             pipeline_epoch_map,
             layers_bouncing_back,
             frame,
         }
     }
 }
 
 pub enum DebugOutput {
     FetchDocuments(String),
     FetchClipScrollTree(String),
 }
 
 pub enum ResultMsg {
     DebugCommand(DebugCommand),
     DebugOutput(DebugOutput),
     RefreshShader(PathBuf),
-    NewFrame(
+    PublishDocument(
         DocumentId,
-        RendererFrame,
+        RenderedDocument,
         TextureUpdateList,
         BackendProfileCounters,
     ),
     UpdateResources {
         updates: TextureUpdateList,
         cancel_rendering: bool,
     },
 }
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -351,17 +351,42 @@ impl PicturePrimitive {
 
                         let readback_task_id = render_tasks.add(RenderTask::new_readback(*prim_screen_rect));
 
                         *readback_render_task_id = Some(readback_task_id);
                         parent_tasks.push(readback_task_id);
 
                         self.render_task_id = Some(render_tasks.add(picture_task));
                     }
-                    Some(PictureCompositeMode::Filter(..)) | Some(PictureCompositeMode::Blit) => {
+                    Some(PictureCompositeMode::Filter(filter)) => {
+                        // If this filter is not currently going to affect
+                        // the picture, just collapse this picture into the
+                        // current render task. This most commonly occurs
+                        // when opacity == 1.0, but can also occur on other
+                        // filters and be a significant performance win.
+                        if filter.is_noop() {
+                            parent_tasks.extend(child_tasks);
+                            self.render_task_id = None;
+                        } else {
+                            let picture_task = RenderTask::new_picture(
+                                Some(prim_screen_rect.size),
+                                prim_index,
+                                RenderTargetKind::Color,
+                                prim_screen_rect.origin.x as f32,
+                                prim_screen_rect.origin.y as f32,
+                                PremultipliedColorF::TRANSPARENT,
+                                ClearMode::Transparent,
+                                self.rasterization_kind,
+                                child_tasks,
+                            );
+
+                            self.render_task_id = Some(render_tasks.add(picture_task));
+                        }
+                    }
+                    Some(PictureCompositeMode::Blit) => {
                         let picture_task = RenderTask::new_picture(
                             Some(prim_screen_rect.size),
                             prim_index,
                             RenderTargetKind::Color,
                             prim_screen_rect.origin.x as f32,
                             prim_screen_rect.origin.y as f32,
                             PremultipliedColorF::TRANSPARENT,
                             ClearMode::Transparent,
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -12,25 +12,24 @@ use core_foundation::dictionary::{CFDict
 use core_foundation::number::{CFNumber, CFNumberRef};
 use core_foundation::string::{CFString, CFStringRef};
 use core_graphics::base::{kCGImageAlphaNoneSkipFirst, kCGImageAlphaPremultipliedFirst};
 use core_graphics::base::kCGBitmapByteOrder32Little;
 use core_graphics::color_space::CGColorSpace;
 use core_graphics::context::{CGContext, CGTextDrawingMode};
 use core_graphics::data_provider::CGDataProvider;
 use core_graphics::font::{CGFont, CGGlyph};
-use core_graphics::geometry::{CGPoint, CGRect, CGSize};
+use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize};
 use core_text;
 use core_text::font::{CTFont, CTFontRef};
 use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
 use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::collections::hash_map::Entry;
-use std::ptr;
 use std::sync::Arc;
 
 pub struct FontContext {
     cg_fonts: FastHashMap<FontKey, CGFont>,
     ct_fonts: FastHashMap<(FontKey, Au, Vec<FontVariation>), CTFont>,
     gamma_lut: GammaLut,
 }
 
@@ -76,21 +75,22 @@ fn supports_subpixel_aa() -> bool {
 fn should_use_white_on_black(color: ColorU) -> bool {
     let (r, g, b) = (color.r as u32, color.g as u32, color.b as u32);
     // These thresholds were determined on 10.12 by observing what CG does.
     r >= 85 && g >= 85 && b >= 85 && r + g + b >= 2 * 255
 }
 
 fn get_glyph_metrics(
     ct_font: &CTFont,
+    transform: Option<&CGAffineTransform>,
     glyph: CGGlyph,
     x_offset: f64,
     y_offset: f64,
 ) -> GlyphMetrics {
-    let bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]);
+    let mut bounds = ct_font.get_bounding_rects_for_glyphs(kCTFontDefaultOrientation, &[glyph]);
 
     if bounds.origin.x.is_nan() || bounds.origin.y.is_nan() || bounds.size.width.is_nan() ||
         bounds.size.height.is_nan()
     {
         // If an unexpected glyph index is requested, core text will return NaN values
         // which causes us to do bad thing as the value is cast into an integer and
         // overflow when expanding the bounds a few lines below.
         // Instead we are better off returning zero-sized metrics because this special
@@ -100,16 +100,24 @@ fn get_glyph_metrics(
             rasterized_width: 0,
             rasterized_height: 0,
             rasterized_ascent: 0,
             rasterized_descent: 0,
             advance: 0.0,
         };
     }
 
+    let mut advance = CGSize { width: 0.0, height: 0.0 };
+    ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, &mut advance, 1);
+
+    if let Some(transform) = transform {
+        bounds = bounds.apply_transform(transform);
+        advance = advance.apply_transform(transform);
+    }
+
     // First round out to pixel boundaries
     // CG Origin is bottom left
     let mut left = bounds.origin.x.floor() as i32;
     let mut bottom = bounds.origin.y.floor() as i32;
     let mut right = (bounds.origin.x + bounds.size.width + x_offset).ceil() as i32;
     let mut top = (bounds.origin.y + bounds.size.height + y_offset).ceil() as i32;
 
     // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
@@ -119,45 +127,42 @@ fn get_glyph_metrics(
     left -= 1;
     bottom -= 1;
     right += 1;
     top += 1;
 
     let width = right - left;
     let height = top - bottom;
 
-    let advance =
-        ct_font.get_advances_for_glyphs(kCTFontDefaultOrientation, &glyph, ptr::null_mut(), 1);
-
     let metrics = GlyphMetrics {
         rasterized_left: left,
         rasterized_width: width as u32,
         rasterized_height: height as u32,
         rasterized_ascent: top,
         rasterized_descent: -bottom,
-        advance: advance as f32,
+        advance: advance.width as f32,
     };
 
     metrics
 }
 
 #[link(name = "ApplicationServices", kind = "framework")]
 extern {
     static kCTFontVariationAxisIdentifierKey: CFStringRef;
     static kCTFontVariationAxisNameKey: CFStringRef;
     static kCTFontVariationAxisMinimumValueKey: CFStringRef;
     static kCTFontVariationAxisMaximumValueKey: CFStringRef;
     static kCTFontVariationAxisDefaultValueKey: CFStringRef;
 
     fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef;
 }
 
-fn new_ct_font_with_variations(cg_font: &CGFont, size: Au, variations: &[FontVariation]) -> CTFont {
+fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVariation]) -> CTFont {
     unsafe {
-        let ct_font = core_text::font::new_from_CGFont(cg_font, size.to_f64_px());
+        let ct_font = core_text::font::new_from_CGFont(cg_font, size);
         if variations.is_empty() {
             return ct_font;
         }
         let axes_ref = CTFontCopyVariationAxes(ct_font.as_concrete_TypeRef());
         if axes_ref.is_null() {
             return ct_font;
         }
         let axes: CFArray = TCFType::wrap_under_create_rule(axes_ref);
@@ -238,17 +243,17 @@ fn new_ct_font_with_variations(cg_font: 
                 vals.push((name, CFNumber::from_f64(val)));
             }
         }
         if vals.is_empty() {
             return ct_font;
         }
         let vals_dict = CFDictionary::from_CFType_pairs(&vals);
         let cg_var_font = cg_font.create_copy_from_variations(&vals_dict).unwrap();
-        core_text::font::new_from_CGFont(&cg_var_font, size.to_f64_px())
+        core_text::font::new_from_CGFont(&cg_var_font, size)
     }
 }
 
 impl FontContext {
     pub fn new() -> FontContext {
         debug!("Test for subpixel AA support: {}", supports_subpixel_aa());
 
         // Force CG to use sRGB color space to gamma correct.
@@ -312,28 +317,28 @@ impl FontContext {
     ) -> Option<CTFont> {
         match self.ct_fonts.entry((font_key, size, variations.to_vec())) {
             Entry::Occupied(entry) => Some((*entry.get()).clone()),
             Entry::Vacant(entry) => {
                 let cg_font = match self.cg_fonts.get(&font_key) {
                     None => return None,
                     Some(cg_font) => cg_font,
                 };
-                let ct_font = new_ct_font_with_variations(cg_font, size, variations);
+                let ct_font = new_ct_font_with_variations(cg_font, size.to_f64_px(), variations);
                 entry.insert(ct_font.clone());
                 Some(ct_font)
             }
         }
     }
 
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
         let character = ch as u16;
         let mut glyph = 0;
 
-        self.get_ct_font(font_key, Au(16 * 60), &[])
+        self.get_ct_font(font_key, Au::from_px(16), &[])
             .and_then(|ref ct_font| {
                 let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1);
 
                 if result {
                     Some(glyph as u32)
                 } else {
                     None
                 }
@@ -344,17 +349,17 @@ impl FontContext {
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<GlyphDimensions> {
         self.get_ct_font(font.font_key, font.size, &font.variations)
             .and_then(|ref ct_font| {
                 let glyph = key.index as CGGlyph;
                 let (x_offset, y_offset) = font.get_subpx_offset(key);
-                let metrics = get_glyph_metrics(ct_font, glyph, x_offset, y_offset);
+                let metrics = get_glyph_metrics(ct_font, None, glyph, x_offset, y_offset);
                 if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
                     None
                 } else {
                     Some(GlyphDimensions {
                         left: metrics.rasterized_left,
                         top: metrics.rasterized_ascent,
                         width: metrics.rasterized_width as u32,
                         height: metrics.rasterized_height as u32,
@@ -442,24 +447,39 @@ impl FontContext {
         }
     }
 
     pub fn rasterize_glyph(
         &mut self,
         font: &FontInstance,
         key: &GlyphKey,
     ) -> Option<RasterizedGlyph> {
-        let ct_font = match self.get_ct_font(font.font_key, font.size, &font.variations) {
+        let (.., minor) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+        let size = font.size.scale_by(minor as f32);
+        let ct_font = match self.get_ct_font(font.font_key, size, &font.variations) {
             Some(font) => font,
             None => return None,
         };
 
+        let shape = font.transform.pre_scale(minor.recip() as f32, minor.recip() as f32);
+        let transform = if shape.is_identity() {
+            None
+        } else {
+            Some(CGAffineTransform {
+                a: shape.scale_x as f64,
+                b: -shape.skew_y as f64,
+                c: -shape.skew_x as f64,
+                d: shape.scale_y as f64,
+                tx: 0.0,
+                ty: 0.0
+            })
+        };
         let glyph = key.index as CGGlyph;
         let (x_offset, y_offset) = font.get_subpx_offset(key);
-        let metrics = get_glyph_metrics(&ct_font, glyph, x_offset, y_offset);
+        let metrics = get_glyph_metrics(&ct_font, transform.as_ref(), glyph, x_offset, y_offset);
         if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
             return None;
         }
 
         // The result of this function, in all render modes, is going to be a
         // BGRA surface with white text on transparency using premultiplied
         // alpha. For subpixel text, the RGB values will be the mask value for
         // the individual components. For bitmap glyphs, the RGB values will be
@@ -547,38 +567,45 @@ impl FontContext {
         cg_context.set_allows_font_subpixel_quantization(false);
         cg_context.set_should_subpixel_quantize_fonts(false);
 
         cg_context.set_allows_font_smoothing(smooth);
         cg_context.set_should_smooth_fonts(smooth);
         cg_context.set_allows_antialiasing(antialias);
         cg_context.set_should_antialias(antialias);
 
-        // CG Origin is bottom left, WR is top left. Need -y offset
-        let rasterization_origin = CGPoint {
-            x: -metrics.rasterized_left as f64 + x_offset,
-            y: metrics.rasterized_descent as f64 - y_offset,
-        };
-
         // Fill the background. This could be opaque white, opaque black, or
         // transparency.
         cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha);
         let rect = CGRect {
             origin: CGPoint { x: 0.0, y: 0.0 },
             size: CGSize {
                 width: metrics.rasterized_width as f64,
                 height: metrics.rasterized_height as f64,
             },
         };
         cg_context.fill_rect(rect);
 
         // Set the text color and draw the glyphs.
         cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0);
         cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
-        ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone());
+
+        // CG Origin is bottom left, WR is top left. Need -y offset
+        let mut draw_origin = CGPoint {
+            x: -metrics.rasterized_left as f64 + x_offset,
+            y: metrics.rasterized_descent as f64 - y_offset,
+        };
+
+        if let Some(transform) = transform {
+            cg_context.set_text_matrix(&transform);
+
+            draw_origin = draw_origin.apply_transform(&transform.invert());
+        }
+
+        ct_font.draw_glyphs(&[glyph], &[draw_origin], cg_context.clone());
 
         let mut rasterized_pixels = cg_context.data().to_vec();
 
         if font.render_mode != FontRenderMode::Bitmap {
             // We rendered text into an opaque surface. The code below needs to
             // ignore the current value of each pixel's alpha channel. But it's
             // allowed to write to the alpha channel, because we're done calling
             // CG functions now.
@@ -625,13 +652,13 @@ impl FontContext {
         }
 
         Some(RasterizedGlyph {
             left: metrics.rasterized_left as f32,
             top: metrics.rasterized_ascent as f32,
             width: metrics.rasterized_width,
             height: metrics.rasterized_height,
             scale: 1.0,
-            format: GlyphFormat::from(font.render_mode),
+            format: font.get_glyph_format(),
             bytes: rasterized_pixels,
         })
     }
 }
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -9,16 +9,17 @@ use api::{FONT_FORCE_AUTOHINT, FONT_NO_A
 use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR};
 use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
 use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32};
 use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos};
 use freetype::freetype::{FT_F26Dot6, FT_Face, FT_Glyph_Format, FT_Long, FT_UInt};
 use freetype::freetype::{FT_GlyphSlot, FT_LcdFilter, FT_New_Face, FT_New_Memory_Face};
 use freetype::freetype::{FT_Init_FreeType, FT_Load_Glyph, FT_Render_Glyph};
 use freetype::freetype::{FT_Library, FT_Outline_Get_CBox, FT_Set_Char_Size, FT_Select_Size};
+use freetype::freetype::{FT_Fixed, FT_Matrix, FT_Set_Transform};
 use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT};
 use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
 use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
 use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES, FT_Err_Cannot_Render_Glyph};
 use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::{cmp, mem, ptr, slice};
 use std::cmp::max;
@@ -181,25 +182,43 @@ impl FontContext {
         }
         if (flags & FONT_VERTICAL_LAYOUT) != 0 {
             load_flags |= FT_LOAD_VERTICAL_LAYOUT;
         }
 
         load_flags |= FT_LOAD_COLOR;
         load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
+        let req_size = font.size.to_f64_px();
         let mut result = if font.render_mode == FontRenderMode::Bitmap {
             if (load_flags & FT_LOAD_NO_BITMAP) != 0 {
                 FT_Error(FT_Err_Cannot_Render_Glyph as i32)
             } else {
-                self.choose_bitmap_size(face.face, font.size.to_f64_px())
+                unsafe { FT_Set_Transform(face.face, ptr::null_mut(), ptr::null_mut()) };
+                self.choose_bitmap_size(face.face, req_size)
             }
         } else {
-            let char_size = font.size.to_f64_px() * 64.0 + 0.5;
-            unsafe { FT_Set_Char_Size(face.face, char_size as FT_F26Dot6, 0, 0, 0) }
+            let (major, minor) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+            let shape = font.transform.pre_scale(major.recip() as f32, minor.recip() as f32);
+            let mut ft_shape = FT_Matrix {
+                xx: (shape.scale_x * 65536.0) as FT_Fixed,
+                xy: (shape.skew_x * -65536.0) as FT_Fixed,
+                yx: (shape.skew_y * -65536.0) as FT_Fixed,
+                yy: (shape.scale_y * 65536.0) as FT_Fixed,
+            };
+            unsafe {
+                FT_Set_Transform(face.face, &mut ft_shape, ptr::null_mut());
+                FT_Set_Char_Size(
+                    face.face,
+                    (req_size * major * 64.0 + 0.5) as FT_F26Dot6,
+                    (req_size * minor * 64.0 + 0.5) as FT_F26Dot6,
+                    0,
+                    0,
+                )
+            }
         };
 
         if result.succeeded() {
             result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, load_flags as FT_Int32) };
         };
 
         if result.succeeded() {
             let slot = unsafe { (*face.face).glyph };
@@ -513,24 +532,24 @@ impl FontContext {
             key,
             font.render_mode,
             dimensions
         );
 
         let (format, actual_width, actual_height) = match pixel_mode {
             FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
                 assert!(bitmap.width % 3 == 0);
-                (GlyphFormat::Subpixel, (bitmap.width / 3) as i32, bitmap.rows as i32)
+                (font.get_subpixel_glyph_format(), (bitmap.width / 3) as i32, bitmap.rows as i32)
             }
             FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
                 assert!(bitmap.rows % 3 == 0);
-                (GlyphFormat::Subpixel, bitmap.width as i32, (bitmap.rows / 3) as i32)
+                (font.get_subpixel_glyph_format(), bitmap.width as i32, (bitmap.rows / 3) as i32)
             }
             FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
-                (GlyphFormat::Mono, bitmap.width as i32, bitmap.rows as i32)
+                (GlyphFormat::Alpha, bitmap.width as i32, bitmap.rows as i32)
             }
             FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
                 (GlyphFormat::Alpha, bitmap.width as i32, bitmap.rows as i32)
             }
             FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
                 (GlyphFormat::ColorBitmap, bitmap.width as i32, bitmap.rows as i32)
             }
             _ => panic!("Unsupported {:?}", pixel_mode),
@@ -615,18 +634,18 @@ impl FontContext {
                 }
                 _ => panic!("Unsupported {:?}", pixel_mode),
             }
             src_row = unsafe { src_row.offset(bitmap.pitch as isize) };
             dest = row_end;
         }
 
         Some(RasterizedGlyph {
-            left: ((dimensions.left + left) as f32 * scale).round(),
-            top: ((dimensions.top + top - actual_height) as f32 * scale).round(),
+            left: (dimensions.left + left) as f32,
+            top: (dimensions.top + top - actual_height) as f32,
             width: actual_width as u32,
             height: actual_height as u32,
             scale,
             format,
             bytes: final_buffer,
         })
     }
 }
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{FontInstancePlatformOptions, FontKey, FontRenderMode};
 use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
 use dwrote;
 use gamma_lut::{ColorLut, GammaLut};
-use glyph_rasterizer::{FontInstance, GlyphFormat, RasterizedGlyph};
+use glyph_rasterizer::{FontInstance, RasterizedGlyph};
 use internal_types::FastHashMap;
 use std::sync::Arc;
 
 lazy_static! {
     static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
         family_name: "Arial".to_owned(),
         weight: dwrote::FontWeight::Regular,
         stretch: dwrote::FontStretch::Normal,
@@ -156,50 +156,54 @@ impl FontContext {
         let face = self.fonts.get(&font.font_key).unwrap();
         let glyph = key.index as u16;
         let advance = 0.0f32;
         let offset = dwrote::GlyphOffset {
             advanceOffset: 0.0,
             ascenderOffset: 0.0,
         };
 
+        let (.., minor) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+        let size = (font.size.to_f64_px() * minor) as f32;
+
         let glyph_run = dwrote::DWRITE_GLYPH_RUN {
             fontFace: unsafe { face.as_ptr() },
-            fontEmSize: font.size.to_f32_px(), // size in DIPs (1/96", same as CSS pixels)
+            fontEmSize: size, // size in DIPs (1/96", same as CSS pixels)
             glyphCount: 1,
             glyphIndices: &glyph,
             glyphAdvances: &advance,
             glyphOffsets: &offset,
             isSideways: 0,
             bidiLevel: 0,
         };
 
         let dwrite_measure_mode = dwrite_measure_mode(font.render_mode, font.platform_options);
         let dwrite_render_mode = dwrite_render_mode(
             face,
             font.render_mode,
-            font.size.to_f32_px(),
+            size,
             dwrite_measure_mode,
             font.platform_options,
         );
 
         let (x_offset, y_offset) = font.get_subpx_offset(key);
-        let transform = Some(dwrote::DWRITE_MATRIX {
-            m11: 1.0,
-            m12: 0.0,
-            m21: 0.0,
-            m22: 1.0,
+        let shape = font.transform.pre_scale(minor.recip() as f32, minor.recip() as f32);
+        let transform = dwrote::DWRITE_MATRIX {
+            m11: shape.scale_x,
+            m12: shape.skew_y,
+            m21: shape.skew_x,
+            m22: shape.scale_y,
             dx: x_offset as f32,
             dy: y_offset as f32,
-        });
+        };
 
         dwrote::GlyphRunAnalysis::create(
             &glyph_run,
             1.0,
-            transform,
+            Some(transform),
             dwrite_render_mode,
             dwrite_measure_mode,
             0.0,
             0.0,
         )
     }
 
     pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
@@ -354,13 +358,13 @@ impl FontContext {
         }
 
         Some(RasterizedGlyph {
             left: bounds.left as f32,
             top: -bounds.top as f32,
             width,
             height,
             scale: 1.0,
-            format: GlyphFormat::from(font.render_mode),
+            format: font.get_glyph_format(),
             bytes: bgra_pixels,
         })
     }
 }
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect};
-use api::{DevicePoint, ExtendMode, GlyphInstance, GlyphKey};
+use api::{DevicePoint, ExtendMode, FontRenderMode, GlyphInstance, GlyphKey};
 use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
-use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
+use api::{ClipMode, LayerSize, LayerVector2D, LayerToWorldTransform, LineOrientation, LineStyle};
 use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset};
 use api::{ClipId, LayerTransform, PipelineId, YuvColorSpace, YuvFormat};
 use border::BorderCornerInstance;
 use clip_scroll_tree::ClipScrollTree;
 use clip::{ClipSourcesHandle, ClipStore};
 use frame_builder::PrimitiveContext;
-use glyph_rasterizer::FontInstance;
+use glyph_rasterizer::{FontInstance, FontTransform};
 use internal_types::FastHashMap;
 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
                 ToGpuBlocks};
 use picture::{PictureKind, PicturePrimitive};
 use profiler::FrameProfileCounters;
 use render_task::{ClipWorkItem, ClipChainNode};
 use render_task::{RenderTask, RenderTaskId, RenderTaskTree};
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
@@ -586,30 +586,42 @@ pub struct TextRunPrimitiveCpu {
     pub glyph_range: ItemRange<GlyphInstance>,
     pub glyph_count: usize,
     pub glyph_keys: Vec<GlyphKey>,
     pub glyph_gpu_blocks: Vec<GpuBlockData>,
 }
 
 
 impl TextRunPrimitiveCpu {
-    pub fn get_font(&self, device_pixel_ratio: f32) -> FontInstance {
+    pub fn get_font(
+        &self,
+        device_pixel_ratio: f32,
+        transform: &LayerToWorldTransform,
+    ) -> FontInstance {
         let mut font = self.font.clone();
         font.size = font.size.scale_by(device_pixel_ratio);
+        if font.render_mode == FontRenderMode::Subpixel {
+            if transform.has_perspective_component() || !transform.has_2d_inverse() {
+                font.render_mode = FontRenderMode::Alpha;
+            } else {
+                font.transform = FontTransform::from(transform).quantize();
+            }
+        }
         font
     }
 
     fn prepare_for_render(
         &mut self,
         resource_cache: &mut ResourceCache,
         device_pixel_ratio: f32,
+        transform: &LayerToWorldTransform,
         display_list: &BuiltDisplayList,
         gpu_cache: &mut GpuCache,
     ) {
-        let font = self.get_font(device_pixel_ratio);
+        let font = self.get_font(device_pixel_ratio, transform);
 
         // Cache the glyph positions, if not in the cache already.
         // TODO(gw): In the future, remove `glyph_instances`
         //           completely, and just reference the glyphs
         //           directly from the display list.
         if self.glyph_keys.is_empty() {
             let subpx_dir = font.subpx_dir.limit_by(font.render_mode);
             let src_glyphs = display_list.get(self.glyph_range);
@@ -1105,16 +1117,17 @@ impl PrimitiveStore {
                         parent_tasks,
                     );
             }
             PrimitiveKind::TextRun => {
                 let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
                 text.prepare_for_render(
                     resource_cache,
                     prim_context.device_pixel_ratio,
+                    &prim_context.scroll_node.world_content_transform,
                     prim_context.display_list,
                     gpu_cache,
                 );
             }
             PrimitiveKind::Image => {
                 let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
 
                 resource_cache.request_image(
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use api::{ColorF, ColorU};
 use debug_render::DebugRenderer;
 use euclid::{Point2D, Rect, Size2D, vec2};
 use query::{GpuSampler, GpuTimer, NamedTag};
 use std::collections::vec_deque::VecDeque;
+use internal_types::FastHashMap;
 use std::{f32, mem};
 use time::precise_time_ns;
 
 const GRAPH_WIDTH: f32 = 1024.0;
 const GRAPH_HEIGHT: f32 = 320.0;
 const GRAPH_PADDING: f32 = 8.0;
 const GRAPH_FRAME_HEIGHT: f32 = 16.0;
 const PROFILE_PADDING: f32 = 10.0;
@@ -298,16 +299,17 @@ impl ProfileCounter for AverageTimeProfi
             format!("{:.2} fps", 1000000000.0 / self.nanoseconds as f64)
         } else {
             format!("{:.2} ms", self.nanoseconds as f64 / 1000000.0)
         }
     }
 }
 
 
+#[derive(Clone)]
 pub struct FrameProfileCounters {
     pub total_primitives: IntProfileCounter,
     pub visible_primitives: IntProfileCounter,
     pub passes: IntProfileCounter,
     pub color_targets: IntProfileCounter,
     pub alpha_targets: IntProfileCounter,
 }
 
@@ -662,41 +664,76 @@ impl GpuFrameCollection {
         let mut y0 = graph_rect.origin.y;
 
         let max_time = self.frames
             .iter()
             .max_by_key(|f| f.total_time)
             .unwrap()
             .total_time as f32;
 
+        let mut tags_present = FastHashMap::default();
+
         for frame in &self.frames {
             let y1 = y0 + GRAPH_FRAME_HEIGHT;
 
             let mut current_ns = 0;
             for sample in &frame.samples {
                 let x0 = graph_rect.origin.x + w * current_ns as f32 / max_time;
                 current_ns += sample.time_ns;
                 let x1 = graph_rect.origin.x + w * current_ns as f32 / max_time;
-
                 let mut bottom_color = sample.tag.color;
                 bottom_color.a *= 0.5;
 
                 debug_renderer.add_quad(
                     x0,
                     y0,
                     x1,
                     y1,
                     sample.tag.color.into(),
                     bottom_color.into(),
                 );
+
+                tags_present.insert(sample.tag.label, sample.tag.color);
             }
 
             y0 = y1;
         }
 
+        // Add a legend to see which color correspond to what primitive.
+        const LEGEND_SIZE: f32 = 20.0;
+        const PADDED_LEGEND_SIZE: f32 = 25.0;
+        if !tags_present.is_empty() {
+            debug_renderer.add_quad(
+                bounding_rect.max_x() + GRAPH_PADDING,
+                bounding_rect.origin.y,
+                bounding_rect.max_x() + GRAPH_PADDING + 200.0,
+                bounding_rect.origin.y + tags_present.len() as f32 * PADDED_LEGEND_SIZE + GRAPH_PADDING,
+                ColorU::new(25, 25, 25, 200),
+                ColorU::new(51, 51, 51, 200),
+            );
+        }
+
+        for (i, (label, &color)) in tags_present.iter().enumerate() {
+            let x0 = bounding_rect.origin.x + bounding_rect.size.width + GRAPH_PADDING * 2.0;
+            let y0 = bounding_rect.origin.y + GRAPH_PADDING + i as f32 * PADDED_LEGEND_SIZE;
+
+            debug_renderer.add_quad(
+                x0, y0, x0 + LEGEND_SIZE, y0 + LEGEND_SIZE,
+                color.into(),
+                color.into(),
+            );
+
+            debug_renderer.add_text(
+                x0 + PADDED_LEGEND_SIZE,
+                y0 + LEGEND_SIZE * 0.75,
+                label,
+                ColorU::new(255, 255, 0, 255),
+            );
+        }
+
         bounding_rect
     }
 }
 
 pub struct Profiler {
     x_left: f32,
     y_left: f32,
     x_right: f32,
@@ -788,17 +825,17 @@ impl Profiler {
             self.y_left = new_y;
         } else {
             self.y_right = new_y;
         }
     }
 
     pub fn draw_profile(
         &mut self,
-        frame_profile: &FrameProfileCounters,
+        frame_profiles: &[FrameProfileCounters],
         backend_profile: &BackendProfileCounters,
         renderer_profile: &RendererProfileCounters,
         renderer_timers: &mut RendererProfileTimers,
         gpu_samplers: &[GpuSampler<GpuProfileTag>],
         screen_fraction: f32,
         debug_renderer: &mut DebugRenderer,
     ) {
         self.x_left = 20.0;
@@ -813,21 +850,16 @@ impl Profiler {
         }
         renderer_timers.gpu_time.set(gpu_time);
 
         self.draw_counters(&[&renderer_profile.frame_time], debug_renderer, true);
 
         self.draw_counters(
             &[
                 &renderer_profile.frame_counter,
-                &frame_profile.total_primitives,
-                &frame_profile.visible_primitives,
-                &frame_profile.passes,
-                &frame_profile.color_targets,
-                &frame_profile.alpha_targets,
                 &backend_profile.resources.gpu_cache.allocated_rows,
                 &backend_profile.resources.gpu_cache.allocated_blocks,
             ],
             debug_renderer,
             true,
         );
 
         self.draw_counters(
@@ -858,16 +890,30 @@ impl Profiler {
                 &backend_profile.ipc.send_time,
                 &backend_profile.ipc.consume_time,
                 &backend_profile.ipc.total_time,
             ],
             debug_renderer,
             true,
         );
 
+        for frame_profile in frame_profiles {
+            self.draw_counters(
+                &[
+                    &frame_profile.total_primitives,
+                    &frame_profile.visible_primitives,
+                    &frame_profile.passes,
+                    &frame_profile.color_targets,
+                    &frame_profile.alpha_targets,
+                ],
+                debug_renderer,
+                true,
+            );
+        }
+
         self.draw_counters(
             &[&renderer_profile.draw_calls, &renderer_profile.vertices],
             debug_renderer,
             true,
         );
 
         self.draw_counters(
             &[
--- a/gfx/webrender/src/query.rs
+++ b/gfx/webrender/src/query.rs
@@ -209,48 +209,32 @@ impl<T> GpuProfiler<T> {
     }
 
     pub fn disable_timers(&mut self) {
         for frame in &mut self.frames {
             frame.disable_timers();
         }
     }
 
-    pub fn toggle_timers_enabled(&mut self) {
-        if self.frames[0].timers.set.is_empty() {
-            self.enable_timers();
-        } else {
-            self.disable_timers();
-        }
-    }
-
     pub fn enable_samplers(&mut self) {
         const MAX_SAMPLERS_PER_FRAME: i32 = 16;
         if cfg!(target_os = "macos") {
             warn!("Expect OSX driver bugs related to sample queries")
         }
 
         for frame in &mut self.frames {
             frame.enable_samplers(MAX_SAMPLERS_PER_FRAME);
         }
     }
 
     pub fn disable_samplers(&mut self) {
         for frame in &mut self.frames {
             frame.disable_samplers();
         }
     }
-
-    pub fn toggle_samplers_enabled(&mut self) {
-        if self.frames[0].samplers.set.is_empty() {
-            self.enable_samplers();
-        } else {
-            self.disable_samplers();
-        }
-    }
 }
 
 impl<T: NamedTag> GpuProfiler<T> {
     pub fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
         self.frames[self.next_frame].build_samples()
     }
 
     pub fn begin_frame(&mut self, frame_id: FrameId) {
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -1,25 +1,26 @@
 /* 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/. */
 
 use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DebugCommand, DeviceIntPoint};
 #[cfg(feature = "debugger")]
 use api::{BuiltDisplayListIter, SpecificDisplayItem};
-use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg};
-use api::{HitTestResult, IdNamespace, LayerPoint, PipelineId, RenderNotifier};
+use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
+use api::{DocumentId, DocumentLayer, DocumentMsg};
+use api::{IdNamespace, LayerPoint, PipelineId, RenderNotifier};
 use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
 use api::channel::{PayloadSender, PayloadSenderHelperMethods};
 #[cfg(feature = "debugger")]
 use debug_server;
 use frame::FrameContext;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use gpu_cache::GpuCache;
-use internal_types::{DebugOutput, FastHashMap, FastHashSet, RendererFrame, ResultMsg};
+use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
 use profiler::{BackendProfileCounters, ResourceProfileCounters};
 use rayon::ThreadPool;
 use record::ApiRecordingReceiver;
 use resource_cache::ResourceCache;
 use scene::Scene;
 #[cfg(feature = "debugger")]
 use serde_json;
 use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
@@ -28,19 +29,21 @@ use std::sync::mpsc::Sender;
 use std::u32;
 use texture_cache::TextureCache;
 use thread_profiler::register_thread_with_profiler;
 use time::precise_time_ns;
 
 struct Document {
     scene: Scene,
     frame_ctx: FrameContext,
+    // the `Option` here is only to deal with borrow checker
     frame_builder: Option<FrameBuilder>,
     window_size: DeviceUintSize,
     inner_rect: DeviceUintRect,
+    layer: DocumentLayer,
     pan: DeviceIntPoint,
     device_pixel_ratio: f32,
     page_zoom_factor: f32,
     pinch_zoom_factor: f32,
     // A set of pipelines that the caller has requested be
     // made available as output textures.
     output_pipelines: FastHashSet<PipelineId>,
     // A helper switch to prevent any frames rendering triggered by scrolling
@@ -49,31 +52,33 @@ struct Document {
     // the first frame would produce inconsistent rendering results, because
     // scroll events are not necessarily received in deterministic order.
     render_on_scroll: Option<bool>,
 }
 
 impl Document {
     pub fn new(
         config: FrameBuilderConfig,
-        initial_size: DeviceUintSize,
+        window_size: DeviceUintSize,
+        layer: DocumentLayer,
         enable_render_on_scroll: bool,
         default_device_pixel_ratio: f32,
     ) -> Self {
         let render_on_scroll = if enable_render_on_scroll {
             Some(false)
         } else {
             None
         };
         Document {
             scene: Scene::new(),
             frame_ctx: FrameContext::new(config),
-            frame_builder: None,
-            window_size: initial_size,
-            inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_size),
+            frame_builder: Some(FrameBuilder::empty()),
+            window_size,
+            inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size),
+            layer,
             pan: DeviceIntPoint::zero(),
             page_zoom_factor: 1.0,
             pinch_zoom_factor: 1.0,
             device_pixel_ratio: default_device_pixel_ratio,
             render_on_scroll,
             output_pipelines: FastHashSet::default(),
         }
     }
@@ -81,65 +86,61 @@ impl Document {
     fn accumulated_scale_factor(&self) -> f32 {
         self.device_pixel_ratio *
         self.page_zoom_factor *
         self.pinch_zoom_factor
     }
 
     fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
         let accumulated_scale_factor = self.accumulated_scale_factor();
-        self.frame_builder = self.frame_ctx.create(
-            self.frame_builder.take(),
+        // this code is why we have `Option`, which is never `None`
+        let frame_builder = self.frame_ctx.create(
+            self.frame_builder.take().unwrap(),
             &self.scene,
             resource_cache,
             self.window_size,
             self.inner_rect,
             accumulated_scale_factor,
             &self.output_pipelines,
         );
+        self.frame_builder = Some(frame_builder);
     }
 
     fn render(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         resource_profile: &mut ResourceProfileCounters,
-    ) -> RendererFrame {
+    ) -> RenderedDocument {
         let accumulated_scale_factor = self.accumulated_scale_factor();
         let pan = LayerPoint::new(
             self.pan.x as f32 / accumulated_scale_factor,
             self.pan.y as f32 / accumulated_scale_factor,
         );
-        match self.frame_builder {
-            Some(ref mut builder) => {
-                self.frame_ctx.build_renderer_frame(
-                    builder,
-                    resource_cache,
-                    gpu_cache,
-                    &self.scene.pipelines,
-                    accumulated_scale_factor,
-                    pan,
-                    &mut resource_profile.texture_cache,
-                    &mut resource_profile.gpu_cache,
-                    &self.scene.properties,
-                )
-            }
-            None => {
-                self.frame_ctx.get_renderer_frame()
-            }
-        }
+        self.frame_ctx.build_rendered_document(
+            self.frame_builder.as_mut().unwrap(),
+            resource_cache,
+            gpu_cache,
+            &self.scene.pipelines,
+            accumulated_scale_factor,
+            self.layer,
+            pan,
+            &mut resource_profile.texture_cache,
+            &mut resource_profile.gpu_cache,
+            &self.scene.properties,
+        )
     }
 }
 
 enum DocumentOp {
     Nop,
     Built,
     ScrolledNop,
-    Scrolled(RendererFrame),
-    Rendered(RendererFrame),
+    Scrolled(RenderedDocument),
+    Rendered(RenderedDocument),
 }
 
 /// The unique id for WR resource identification.
 static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
 
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
 ///
@@ -356,23 +357,21 @@ impl RenderBackend {
                     );
                     DocumentOp::Scrolled(frame)
                 } else {
                     DocumentOp::ScrolledNop
                 }
             }
             DocumentMsg::HitTest(pipeline_id, point, flags, tx) => {
                 profile_scope!("HitTest");
-                let result = match doc.frame_builder {
-                    Some(ref builder) => {
-                        let cst = doc.frame_ctx.get_clip_scroll_tree();
-                        builder.hit_test(cst, pipeline_id, point, flags)
-                    },
-                    None => HitTestResult::default(),
-                };
+                let cst = doc.frame_ctx.get_clip_scroll_tree();
+                let result = doc.frame_builder
+                    .as_ref()
+                    .unwrap()
+                    .hit_test(cst, pipeline_id, point, flags);
                 tx.send(result).unwrap();
                 DocumentOp::Nop
             }
             DocumentMsg::ScrollNodeWithId(origin, id, clamp) => {
                 profile_scope!("ScrollNodeWithScrollId");
                 let _timer = profile_counters.total_time.timer();
 
                 if doc.frame_ctx.scroll_node(origin, id, clamp) && doc.render_on_scroll == Some(true) {
@@ -477,45 +476,46 @@ impl RenderBackend {
                         let index = self.resource_cache.get_glyph_index(font_key, ch);
                         glyph_indices.push(index);
                     }
                     tx.send(glyph_indices).unwrap();
                 }
                 ApiMsg::CloneApi(sender) => {
                     sender.send(self.next_namespace_id()).unwrap();
                 }
-                ApiMsg::AddDocument(document_id, initial_size) => {
+                ApiMsg::AddDocument(document_id, initial_size, layer) => {
                     let document = Document::new(
                         self.frame_config.clone(),
                         initial_size,
+                        layer,
                         self.enable_render_on_scroll,
                         self.default_device_pixel_ratio,
                     );
                     self.documents.insert(document_id, document);
                 }
                 ApiMsg::UpdateDocument(document_id, doc_msg) => match self.process_document(
                     document_id,
                     doc_msg,
                     frame_counter,
                     &mut profile_counters,
                 ) {
                     DocumentOp::Nop => {}
                     DocumentOp::Built => {}
                     DocumentOp::ScrolledNop => {
-                        self.notify_compositor_of_new_scroll_frame(false);
+                        self.notify_compositor_of_new_scroll_document(document_id, false);
                     }
-                    DocumentOp::Scrolled(frame) => {
-                        self.publish_frame(document_id, frame, &mut profile_counters);
-                        self.notify_compositor_of_new_scroll_frame(true);
+                    DocumentOp::Scrolled(doc) => {
+                        self.publish_document(document_id, doc, &mut profile_counters);
+                        self.notify_compositor_of_new_scroll_document(document_id, true);
                     }
-                    DocumentOp::Rendered(frame) => {
+                    DocumentOp::Rendered(doc) => {
                         frame_counter += 1;
-                        self.publish_frame_and_notify_compositor(
+                        self.publish_document_and_notify_compositor(
                             document_id,
-                            frame,
+                            doc,
                             &mut profile_counters,
                         );
                     }
                 },
                 ApiMsg::DeleteDocument(document_id) => {
                     self.documents.remove(&document_id);
                 }
                 ApiMsg::ExternalEvent(evt) => {
@@ -536,69 +536,70 @@ impl RenderBackend {
                     self.resource_cache.on_memory_pressure();
 
                     let pending_update = self.resource_cache.pending_updates();
                     let msg = ResultMsg::UpdateResources {
                         updates: pending_update,
                         cancel_rendering: true,
                     };
                     self.result_tx.send(msg).unwrap();
-                    // We use new_frame_ready to wake up the renderer and get the
-                    // resource updates processed, but the UpdateResources message
-                    // will cancel rendering the frame.
-                    self.notifier.new_frame_ready();
+                    self.notifier.wake_up();
                 }
                 ApiMsg::DebugCommand(option) => {
                     let msg = match option {
                         DebugCommand::FetchDocuments => {
                             let json = self.get_docs_for_debugger();
                             ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json))
                         }
                         DebugCommand::FetchClipScrollTree => {
                             let json = self.get_clip_scroll_tree_for_debugger();
                             ResultMsg::DebugOutput(DebugOutput::FetchClipScrollTree(json))
                         }
                         _ => ResultMsg::DebugCommand(option),
                     };
                     self.result_tx.send(msg).unwrap();
-                    self.notifier.new_frame_ready();
+                    self.notifier.wake_up();
                 }
                 ApiMsg::ShutDown => {
                     self.notifier.shut_down();
                     break;
                 }
             }
         }
     }
 
-    fn publish_frame(
+    fn publish_document(
         &mut self,
         document_id: DocumentId,
-        frame: RendererFrame,
+        document: RenderedDocument,
         profile_counters: &mut BackendProfileCounters,
     ) {
         let pending_update = self.resource_cache.pending_updates();
-        let msg = ResultMsg::NewFrame(document_id, frame, pending_update, profile_counters.clone());
+        let msg = ResultMsg::PublishDocument(document_id, document, pending_update, profile_counters.clone());
         self.result_tx.send(msg).unwrap();
         profile_counters.reset();
     }
 
-    fn publish_frame_and_notify_compositor(
+    fn publish_document_and_notify_compositor(
         &mut self,
         document_id: DocumentId,
-        frame: RendererFrame,
+        document: RenderedDocument,
         profile_counters: &mut BackendProfileCounters,
     ) {
-        self.publish_frame(document_id, frame, profile_counters);
+        self.publish_document(document_id, document, profile_counters);
 
-        self.notifier.new_frame_ready();
+        self.notifier.new_document_ready(document_id, false, true);
     }
 
-    fn notify_compositor_of_new_scroll_frame(&self, composite_needed: bool) {
-        self.notifier.new_scroll_frame_ready(composite_needed);
+    fn notify_compositor_of_new_scroll_document(
+        &self,
+        document_id: DocumentId,
+        composite_needed: bool,
+    ) {
+        self.notifier.new_document_ready(document_id, true, composite_needed);
     }
 
 
     #[cfg(not(feature = "debugger"))]
     fn get_docs_for_debugger(&self) -> String {
         String::new()
     }
 
@@ -671,21 +672,20 @@ impl RenderBackend {
         let mut debug_root = debug_server::ClipScrollTreeList::new();
 
         for (_, doc) in &self.documents {
             let debug_node = debug_server::TreeNode::new("document clip_scroll tree");
             let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
 
             // TODO(gw): Restructure the storage of clip-scroll tree, clip store
             //           etc so this isn't so untidy.
-            if let Some(ref frame_builder) = doc.frame_builder {
-                doc.frame_ctx
-                    .get_clip_scroll_tree()
-                    .print_with(&frame_builder.clip_store, &mut builder);
-            }
+            let clip_store = &doc.frame_builder.as_ref().unwrap().clip_store;
+            doc.frame_ctx
+                .get_clip_scroll_tree()
+                .print_with(clip_store, &mut builder);
 
             debug_root.add(builder.build());
         }
 
         serde_json::to_string(&debug_root).unwrap()
     }
 }
 
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -4,17 +4,17 @@
 
 use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use api::{LayerPoint, LayerRect, PremultipliedColorF};
 use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
 use clip_scroll_tree::CoordinateSystemId;
 use gpu_types::{ClipScrollNodeIndex};
 use picture::RasterizationSpace;
 use prim_store::{PrimitiveIndex};
-use std::{cmp, usize, f32, i32};
+use std::{cmp, ops, usize, f32, i32};
 use std::rc::Rc;
 use tiling::{RenderPass, RenderTargetIndex};
 use tiling::{RenderTargetKind};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128;
 
@@ -52,17 +52,17 @@ impl Iterator for ClipChainNodeIter {
             Some(ref item) => item.prev.clone(),
             None => return None,
         };
         previous
     }
 }
 
 impl RenderTaskTree {
-    pub fn new() -> RenderTaskTree {
+    pub fn new() -> Self {
         RenderTaskTree {
             tasks: Vec::new(),
             task_data: Vec::new(),
         }
     }
 
     pub fn add(&mut self, task: RenderTask) -> RenderTaskId {
         let id = RenderTaskId(self.tasks.len() as u32);
@@ -110,39 +110,43 @@ impl RenderTaskTree {
         } else {
             pass_index
         };
 
         let pass = &mut passes[pass_index];
         pass.add_render_task(id, task.get_dynamic_size(), task.target_kind());
     }
 
-    pub fn get(&self, id: RenderTaskId) -> &RenderTask {
-        &self.tasks[id.0 as usize]
-    }
-
-    pub fn get_mut(&mut self, id: RenderTaskId) -> &mut RenderTask {
-        &mut self.tasks[id.0 as usize]
-    }
-
     pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress {
-        let task = &self.tasks[id.0 as usize];
-        match task.kind {
+        match self[id].kind {
             RenderTaskKind::Alias(alias_id) => RenderTaskAddress(alias_id.0),
             _ => RenderTaskAddress(id.0),
         }
     }
 
     pub fn build(&mut self) {
         for task in &mut self.tasks {
             self.task_data.push(task.write_task_data());
         }
     }
 }
 
+impl ops::Index<RenderTaskId> for RenderTaskTree {
+    type Output = RenderTask;
+    fn index(&self, id: RenderTaskId) -> &RenderTask {
+        &self.tasks[id.0 as usize]
+    }
+}
+
+impl ops::IndexMut<RenderTaskId> for RenderTaskTree {
+    fn index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask {
+        &mut self.tasks[id.0 as usize]
+    }
+}
+
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 pub enum RenderTaskKey {
     /// Draw the alpha mask for a shared clip.
     CacheMask(ClipId),
 }
 
 #[derive(Debug)]
 pub enum RenderTaskLocation {
@@ -186,17 +190,17 @@ impl ClipWorkItem {
             .get_opt(&self.clip_sources)
             .expect("bug: clip handle should be valid")
             .clips();
         let mut rounded_rect_count = 0;
 
         for &(ref clip, _) in clips {
             match *clip {
                 ClipSource::Rectangle(..) => {
-                    if self.has_compatible_coordinate_system(prim_coordinate_system_id) {
+                    if !self.has_compatible_coordinate_system(prim_coordinate_system_id) {
                         return MaskGeometryKind::Default;
                     }
                 },
                 ClipSource::RoundedRectangle(..) => {
                     rounded_rect_count += 1;
                 }
                 ClipSource::Image(..) | ClipSource::BorderCorner(..) => {
                     return MaskGeometryKind::Default;
@@ -427,17 +431,17 @@ impl RenderTask {
         render_tasks: &mut RenderTaskTree,
         target_kind: RenderTargetKind,
         regions: &[LayerRect],
         clear_mode: ClearMode,
         color: PremultipliedColorF,
     ) -> Self {
         // Adjust large std deviation value.
         let mut adjusted_blur_std_deviation = blur_std_deviation;
-        let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size();
+        let blur_target_size = render_tasks[src_task_id].get_dynamic_size();
         let mut adjusted_blur_target_size = blur_target_size;
         let mut downscaling_src_task_id = src_task_id;
         let mut scale_factor = 1.0;
         while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
             if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
                adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
                 break;
             }
@@ -660,19 +664,9 @@ impl RenderTask {
 
             RenderTaskKind::CacheMask(..) => true,
 
             RenderTaskKind::Alias(..) => {
                 panic!("BUG: is_shared() called on aliased task");
             }
         }
     }
-
-    pub fn set_alias(&mut self, id: RenderTaskId) {
-        debug_assert!(self.cache_key.is_some());
-        // TODO(gw): We can easily handle invalidation of tasks that
-        //           contain children in the future. Since we don't
-        //           have any cases of that yet, just assert to simplify
-        //           the current implementation.
-        debug_assert!(self.children.is_empty());
-        self.kind = RenderTaskKind::Alias(id);
-    }
 }
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -5,18 +5,19 @@
 //! The webrender API.
 //!
 //! The `webrender::renderer` module provides the interface to webrender, which
 //! is accessible through [`Renderer`][renderer]
 //!
 //! [renderer]: struct.Renderer.html
 
 use api::{channel, BlobImageRenderer, FontRenderMode};
-use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier};
-use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
+use api::{ColorF, DocumentId, Epoch, PipelineId, RenderApiSender, RenderNotifier};
+use api::{DevicePixel, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
 use api::{ExternalImageId, ExternalImageType, ImageFormat};
 use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 use api::{YuvColorSpace, YuvFormat};
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 use api::DebugCommand;
 #[cfg(not(feature = "debugger"))]
 use api::channel::MsgSender;
@@ -26,25 +27,25 @@ use debug_render::DebugRenderer;
 use debug_server::{self, DebugServer};
 use device::{DepthFunction, Device, FrameId, Program, Texture,
              VertexDescriptor, PBO};
 use device::{get_gl_format_bgra, ExternalTexture, FBOId, TextureSlot, VertexAttribute,
              VertexAttributeKind};
 use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget,
              VertexUsageHint, VAO};
 use device::ProgramCache;
-use euclid::{rect, Transform3D};
+use euclid::{rect, ScaleFactor, Transform3D};
 use frame_builder::FrameBuilderConfig;
 use gleam::gl;
 use glyph_rasterizer::GlyphFormat;
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
 use gpu_types::PrimitiveInstance;
 use internal_types::{BatchTextures, SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
-use internal_types::{CacheTextureId, FastHashMap, RendererFrame, ResultMsg, TextureUpdateOp};
-use internal_types::{DebugOutput, RenderTargetMode, TextureUpdateList, TextureUpdateSource};
+use internal_types::{CacheTextureId, FastHashMap, RenderedDocument, ResultMsg, TextureUpdateOp};
+use internal_types::{DebugOutput, RenderTargetInfo, TextureUpdateList, TextureUpdateSource};
 use profiler::{BackendProfileCounters, Profiler};
 use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
 use query::{GpuProfiler, GpuTimer};
 use rayon::Configuration as ThreadPoolConfig;
 use rayon::ThreadPool;
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
 use render_task::{RenderTaskKind, RenderTaskTree};
@@ -58,17 +59,17 @@ use std::f32;
 use std::mem;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::Arc;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use thread_profiler::{register_thread_with_profiler, write_profile};
-use tiling::{AlphaRenderTarget, ColorRenderTarget, RenderTargetKind};
+use tiling::{AlphaRenderTarget, ColorRenderTarget, RenderPassKind, RenderTargetKind, RenderTargetList};
 use tiling::{BatchKey, BatchKind, BrushBatchKind, Frame, RenderTarget, ScalingInfo, TransformBatchKind};
 use time::precise_time_ns;
 use util::TransformedRectKind;
 
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag {
     label: "B_Mask",
@@ -237,16 +238,27 @@ impl BatchKind {
 
 bitflags! {
     #[derive(Default)]
     pub struct DebugFlags: u32 {
         const PROFILER_DBG      = 1 << 0;
         const RENDER_TARGET_DBG = 1 << 1;
         const TEXTURE_CACHE_DBG = 1 << 2;
         const ALPHA_PRIM_DBG    = 1 << 3;
+        const GPU_TIME_QUERIES  = 1 << 4;
+        const GPU_SAMPLE_QUERIES= 1 << 5;
+        const DISABLE_BATCHING  = 1 << 6;
+    }
+}
+
+fn flag_changed(before: DebugFlags, after: DebugFlags, select: DebugFlags) -> Option<bool> {
+    if before & select != after & select {
+        Some(after.contains(select))
+    } else {
+        None
     }
 }
 
 // A generic mode that can be passed to shaders to change
 // behaviour per draw-call.
 type ShaderMode = i32;
 
 #[repr(C)]
@@ -265,19 +277,19 @@ impl Into<ShaderMode> for TextShaderMode
     fn into(self) -> i32 {
         self as i32
     }
 }
 
 impl From<GlyphFormat> for TextShaderMode {
     fn from(format: GlyphFormat) -> TextShaderMode {
         match format {
-            GlyphFormat::Mono | GlyphFormat::Alpha => TextShaderMode::Alpha,
-            GlyphFormat::Subpixel => {
-                panic!("Subpixel glyph format must be handled separately.");
+            GlyphFormat::Alpha => TextShaderMode::Alpha,
+            GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => {
+                panic!("Subpixel glyph formats must be handled separately.");
             }
             GlyphFormat::ColorBitmap => TextShaderMode::ColorBitmap,
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 enum TextureSampler {
@@ -557,17 +569,17 @@ impl SourceTextureResolver {
     fn new(device: &mut Device) -> SourceTextureResolver {
         let mut dummy_cache_texture = device.create_texture(TextureTarget::Array);
         device.init_texture(
             &mut dummy_cache_texture,
             1,
             1,
             ImageFormat::BGRA8,
             TextureFilter::Linear,
-            RenderTargetMode::RenderTarget,
+            None,
             1,
             None,
         );
 
         SourceTextureResolver {
             cache_texture_map: Vec::new(),
             external_images: FastHashMap::default(),
             dummy_cache_texture,
@@ -581,36 +593,29 @@ impl SourceTextureResolver {
 
         for texture in self.cache_texture_map {
             device.delete_texture(texture);
         }
     }
 
     fn end_pass(
         &mut self,
-        is_last: bool,
         a8_texture: Option<Texture>,
         rgba8_texture: Option<Texture>,
         a8_pool: &mut Vec<Texture>,
         rgba8_pool: &mut Vec<Texture>,
     ) {
         // If we have cache textures from previous pass, return them to the pool.
         rgba8_pool.extend(self.cache_rgba8_texture.take());
         a8_pool.extend(self.cache_a8_texture.take());
 
-        if is_last {
-            // On the last pass, return the textures from this pass to the pool.
-            rgba8_pool.extend(rgba8_texture);
-            a8_pool.extend(a8_texture);
-        } else {
-            // We have another pass to process, make these textures available
-            // as inputs to the next pass.
-            self.cache_rgba8_texture = rgba8_texture;
-            self.cache_a8_texture = a8_texture;
-        }
+        // We have another pass to process, make these textures available
+        // as inputs to the next pass.
+        self.cache_rgba8_texture = rgba8_texture;
+        self.cache_a8_texture = a8_texture;
     }
 
     // Bind a source texture to the device.
     fn bind(&self, texture_id: &SourceTexture, sampler: TextureSampler, device: &mut Device) {
         match *texture_id {
             SourceTexture::Invalid => {}
             SourceTexture::CacheA8 => {
                 let texture = self.cache_a8_texture
@@ -748,17 +753,17 @@ impl CacheTexture {
             // Create a f32 texture that can be used for the vertex shader
             // to fetch data from.
             device.init_texture(
                 &mut self.texture,
                 MAX_VERTEX_TEXTURE_WIDTH as u32,
                 updates.height as u32,
                 ImageFormat::RGBAF32,
                 TextureFilter::Nearest,
-                RenderTargetMode::None,
+                None,
                 1,
                 None,
             );
 
             // Copy the current texture into the newly resized texture.
             if current_dimensions.height > 0 {
                 // If we had to resize the texture, just mark all rows
                 // as dirty so they will be uploaded to the texture
@@ -857,17 +862,17 @@ impl VertexDataTexture {
             let new_height = (needed_height + 127) & !127;
 
             device.init_texture(
                 &mut self.texture,
                 width,
                 new_height,
                 ImageFormat::RGBAF32,
                 TextureFilter::Nearest,
-                RenderTargetMode::None,
+                None,
                 1,
                 None,
             );
         }
 
         // Bind a PBO to do the texture upload.
         // Updating the texture via PBO avoids CPU-side driver stalls.
         device.bind_pbo(Some(&self.pbo));
@@ -888,16 +893,17 @@ const TRANSFORM_FEATURE: &str = "TRANSFO
 const CLIP_FEATURE: &str = "CLIP";
 const ALPHA_FEATURE: &str = "ALPHA_PASS";
 
 enum ShaderKind {
     Primitive,
     Cache(VertexArrayKind),
     ClipCache,
     Brush,
+    Text,
 }
 
 struct LazilyCompiledShader {
     program: Option<Program>,
     name: &'static str,
     kind: ShaderKind,
     features: Vec<&'static str>,
 }
@@ -913,17 +919,28 @@ impl LazilyCompiledShader {
         let mut shader = LazilyCompiledShader {
             program: None,
             name,
             kind,
             features: features.to_vec(),
         };
 
         if precache {
-            try!{ shader.get(device) };
+            let t0 = precise_time_ns();
+            let program = try!{ shader.get(device) };
+            let t1 = precise_time_ns();
+            device.bind_program(program);
+            device.draw_triangles_u16(0, 3);
+            let t2 = precise_time_ns();
+            println!("[C: {:.1} ms D: {:.1} ms] Precache {} {:?}",
+                (t1 - t0) as f64 / 1000000.0,
+                (t2 - t1) as f64 / 1000000.0,
+                name,
+                features
+            );
         }
 
         Ok(shader)
     }
 
     fn bind<M>(
         &mut self,
         device: &mut Device,
@@ -941,17 +958,17 @@ impl LazilyCompiledShader {
         device.bind_program(program);
         device.set_uniforms(program, projection, mode.into());
     }
 
     fn get(&mut self, device: &mut Device) -> Result<&Program, ShaderError> {
         if self.program.is_none() {
             let program = try!{
                 match self.kind {
-                    ShaderKind::Primitive | ShaderKind::Brush => {
+                    ShaderKind::Primitive | ShaderKind::Brush | ShaderKind::Text => {
                         create_prim_shader(self.name,
                                            device,
                                            &self.features,
                                            VertexArrayKind::Primitive)
                     }
                     ShaderKind::Cache(format) => {
                         create_prim_shader(self.name,
                                            device,
@@ -971,21 +988,16 @@ impl LazilyCompiledShader {
 
     fn deinit(self, device: &mut Device) {
         if let Some(program) = self.program {
             device.delete_program(program);
         }
     }
 }
 
-struct PrimitiveShader {
-    simple: LazilyCompiledShader,
-    transform: LazilyCompiledShader,
-}
-
 // A brush shader supports two modes:
 // opaque:
 //   Used for completely opaque primitives,
 //   or inside segments of partially
 //   opaque primitives. Assumes no need
 //   for clip masks, AA etc.
 // alpha:
 //   Used for brush primitives in the alpha
@@ -1049,26 +1061,19 @@ impl BrushShader {
     }
 
     fn deinit(self, device: &mut Device) {
         self.opaque.deinit(device);
         self.alpha.deinit(device);
     }
 }
 
-struct FileWatcher {
-    notifier: Box<RenderNotifier>,
-    result_tx: Sender<ResultMsg>,
-}
-
-impl FileWatcherHandler for FileWatcher {
-    fn file_changed(&self, path: PathBuf) {
-        self.result_tx.send(ResultMsg::RefreshShader(path)).ok();
-        self.notifier.new_frame_ready();
-    }
+struct PrimitiveShader {
+    simple: LazilyCompiledShader,
+    transform: LazilyCompiledShader,
 }
 
 impl PrimitiveShader {
     fn new(
         name: &'static str,
         device: &mut Device,
         features: &[&'static str],
         precache: bool,
@@ -1114,16 +1119,97 @@ impl PrimitiveShader {
     }
 
     fn deinit(self, device: &mut Device) {
         self.simple.deinit(device);
         self.transform.deinit(device);
     }
 }
 
+struct TextShader {
+    simple: LazilyCompiledShader,
+    transform: LazilyCompiledShader,
+    glyph_transform: LazilyCompiledShader,
+}
+
+impl TextShader {
+    fn new(
+        name: &'static str,
+        device: &mut Device,
+        features: &[&'static str],
+        precache: bool,
+    ) -> Result<TextShader, ShaderError> {
+        let simple = try!{
+            LazilyCompiledShader::new(ShaderKind::Text,
+                                      name,
+                                      features,
+                                      device,
+                                      precache)
+        };
+
+        let mut transform_features = features.to_vec();
+        transform_features.push("TRANSFORM");
+
+        let transform = try!{
+            LazilyCompiledShader::new(ShaderKind::Text,
+                                      name,
+                                      &transform_features,
+                                      device,
+                                      precache)
+        };
+
+        let mut glyph_transform_features = features.to_vec();
+        glyph_transform_features.push("GLYPH_TRANSFORM");
+
+        let glyph_transform = try!{
+            LazilyCompiledShader::new(ShaderKind::Text,
+                                      name,
+                                      &glyph_transform_features,
+                                      device,
+                                      precache)
+        };
+
+        Ok(TextShader { simple, transform, glyph_transform })
+    }
+
+    fn bind<M>(
+        &mut self,
+        device: &mut Device,
+        glyph_format: GlyphFormat,
+        transform_kind: TransformedRectKind,
+        projection: &Transform3D<f32>,
+        mode: M,
+        renderer_errors: &mut Vec<RendererError>,
+    ) where M: Into<ShaderMode> {
+        match glyph_format {
+            GlyphFormat::Alpha |
+            GlyphFormat::Subpixel |
+            GlyphFormat::ColorBitmap => {
+                match transform_kind {
+                    TransformedRectKind::AxisAligned => {
+                        self.simple.bind(device, projection, mode, renderer_errors)
+                    }
+                    TransformedRectKind::Complex => {
+                        self.transform.bind(device, projection, mode, renderer_errors)
+                    }
+                }
+            }
+            GlyphFormat::TransformedSubpixel => {
+                self.glyph_transform.bind(device, projection, mode, renderer_errors)
+            }
+        }
+    }
+
+    fn deinit(self, device: &mut Device) {
+        self.simple.deinit(device);
+        self.transform.deinit(device);
+        self.glyph_transform.deinit(device);
+    }
+}
+
 fn create_prim_shader(
     name: &'static str,
     device: &mut Device,
     features: &[&'static str],
     vertex_format: VertexArrayKind,
 ) -> Result<Program, ShaderError> {
     let mut prefix = format!(
         "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}\n",
@@ -1187,37 +1273,49 @@ fn create_clip_shader(name: &'static str
                 ("sSharedCacheA8", TextureSampler::SharedCacheA8),
             ],
         );
     }
 
     program
 }
 
+struct FileWatcher {
+    notifier: Box<RenderNotifier>,
+    result_tx: Sender<ResultMsg>,
+}
+
+impl FileWatcherHandler for FileWatcher {
+    fn file_changed(&self, path: PathBuf) {
+        self.result_tx.send(ResultMsg::RefreshShader(path)).ok();
+        self.notifier.wake_up();
+    }
+}
+
 #[derive(Clone, Debug, PartialEq)]
 pub enum ReadPixelsFormat {
     Rgba8,
     Bgra8,
 }
 
 struct FrameOutput {
     last_access: FrameId,
     fbo_id: FBOId,
 }
 
 /// The renderer is responsible for submitting to the GPU the work prepared by the
 /// RenderBackend.
-pub struct Renderer<'a> {
+pub struct Renderer {
     result_rx: Receiver<ResultMsg>,
     debug_server: DebugServer,
-    device: Device<'a>,
+    device: Device,
     pending_texture_updates: Vec<TextureUpdateList>,
     pending_gpu_cache_updates: Vec<GpuCacheUpdateList>,
     pending_shader_updates: Vec<PathBuf>,
-    current_frame: Option<RendererFrame>,
+    active_documents: Vec<(DocumentId, RenderedDocument)>,
 
     // These are "cache shaders". These shaders are used to
     // draw intermediate results to cache targets. The results
     // of these shaders are then used by the primitive shaders.
     cs_text_run: LazilyCompiledShader,
     cs_line: LazilyCompiledShader,
     cs_blur_a8: LazilyCompiledShader,
     cs_blur_rgba8: LazilyCompiledShader,
@@ -1239,18 +1337,18 @@ pub struct Renderer<'a> {
     // final results on screen. They are aware of tile boundaries.
     // Most draw directly to the framebuffer, but some use inputs
     // from the cache shaders to draw. Specifically, the box
     // shadow primitive shader stretches the box shadow cache
     // output, and the cache_image shader blits the results of
     // a cache shader (e.g. blur) to the screen.
     ps_rectangle: PrimitiveShader,
     ps_rectangle_clip: PrimitiveShader,
-    ps_text_run: PrimitiveShader,
-    ps_text_run_subpx_bg_pass1: PrimitiveShader,
+    ps_text_run: TextShader,
+    ps_text_run_subpx_bg_pass1: TextShader,
     ps_image: Vec<Option<PrimitiveShader>>,
     ps_yuv_image: Vec<Option<PrimitiveShader>>,
     ps_border_corner: PrimitiveShader,
     ps_border_edge: PrimitiveShader,
     ps_gradient: PrimitiveShader,
     ps_angle_gradient: PrimitiveShader,
     ps_radial_gradient: PrimitiveShader,
     ps_line: PrimitiveShader,
@@ -1258,22 +1356,20 @@ pub struct Renderer<'a> {
     ps_blend: LazilyCompiledShader,
     ps_hw_composite: LazilyCompiledShader,
     ps_split_composite: LazilyCompiledShader,
     ps_composite: LazilyCompiledShader,
 
     max_texture_size: u32,
 
     max_recorded_profiles: usize,
-    clear_framebuffer: bool,
-    clear_color: ColorF,
+    clear_color: Option<ColorF>,
     enable_clear_scissor: bool,
     debug: DebugRenderer,
     debug_flags: DebugFlags,
-    enable_batcher: bool,
     backend_profile_counters: BackendProfileCounters,
     profile_counters: RendererProfileCounters,
     profiler: Profiler,
     last_time: u64,
 
     color_render_targets: Vec<Texture>,
     alpha_render_targets: Vec<Texture>,
 
@@ -1330,17 +1426,17 @@ impl From<ShaderError> for RendererError
 }
 
 impl From<std::io::Error> for RendererError {
     fn from(err: std::io::Error) -> Self {
         RendererError::Thread(err)
     }
 }
 
-impl<'a> Renderer<'a> {
+impl Renderer {
     /// Initializes webrender and creates a `Renderer` and `RenderApiSender`.
     ///
     /// # Examples
     /// Initializes a `Renderer` with some reasonable values. For more information see
     /// [`RendererOptions`][rendereroptions].
     ///
     /// ```rust,ignore
     /// # use webrender::renderer::Renderer;
@@ -1394,18 +1490,17 @@ impl<'a> Renderer<'a> {
                 device_max_size,
                 options.max_texture_size.unwrap_or(device_max_size),
             ),
             min_texture_size,
         );
 
         register_thread_with_profiler("Compositor".to_owned());
 
-        // device-pixel ratio doesn't matter here - we are just creating resources.
-        device.begin_frame(1.0);
+        device.begin_frame();
 
         let cs_text_run = try!{
             LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Primitive),
                                       "cs_text_run",
                                       &[],
                                       &mut device,
                                       options.precache_shaders)
         };
@@ -1505,27 +1600,27 @@ impl<'a> Renderer<'a> {
         let ps_line = try!{
             PrimitiveShader::new("ps_line",
                                  &mut device,
                                  &[],
                                  options.precache_shaders)
         };
 
         let ps_text_run = try!{
-            PrimitiveShader::new("ps_text_run",
-                                 &mut device,
-                                 &[],
-                                 options.precache_shaders)
+            TextShader::new("ps_text_run",
+                            &mut device,
+                            &[],
+                           options.precache_shaders)
         };
 
         let ps_text_run_subpx_bg_pass1 = try!{
-            PrimitiveShader::new("ps_text_run",
-                                 &mut device,
-                                 &["SUBPX_BG_PASS1"],
-                                 options.precache_shaders)
+            TextShader::new("ps_text_run",
+                            &mut device,
+                            &["SUBPX_BG_PASS1"],
+                            options.precache_shaders)
         };
 
         // All image configuration.
         let mut image_features = Vec::new();
         let mut ps_image: Vec<Option<PrimitiveShader>> = Vec::new();
         // PrimitiveShader is not clonable. Use push() to initialize the vec.
         for _ in 0 .. IMAGE_BUFFER_KINDS.len() {
             ps_image.push(None);
@@ -1747,17 +1842,17 @@ impl<'a> Renderer<'a> {
 
             let mut texture = device.create_texture(TextureTarget::Default);
             device.init_texture(
                 &mut texture,
                 8,