Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Jun 2014 17:34:22 -0400
changeset 207578 cfd971d26d87bd5e5f8209bc55514fb4d31fb9b5
parent 207577 ff544fe359bc5e5d22eff9dc9399e005253bc932 (current diff)
parent 207560 62d33e3ba5148906105567837faf78491802ed1d (diff)
child 207579 45e4c987d7600d3085968ea05406f373e489b9e9
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge
browser/base/content/fonts/ClearSans-Regular.woff
browser/base/content/fonts/FiraSans-Light.woff
browser/base/content/fonts/FiraSans-Regular.woff
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,10 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 978238 needs clobber because of changes to js.msg (see bug 1019955).
+Bug 904723 (Array.from) needs a clobber once again because of changes to js.msg
+and self-hosted code (see bug 1019955).
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -421,17 +421,17 @@ nsAccessibilityService::TreeViewChanged(
                                         nsIContent* aContent,
                                         nsITreeView* aView)
 {
   DocAccessible* document = GetDocAccessible(aPresShell);
   if (document) {
     Accessible* accessible = document->GetAccessible(aContent);
     if (accessible) {
       XULTreeAccessible* treeAcc = accessible->AsXULTree();
-      if (treeAcc) 
+      if (treeAcc)
         treeAcc->TreeViewChanged(aView);
     }
   }
 }
 
 void
 nsAccessibilityService::RangeValueChanged(nsIPresShell* aPresShell,
                                           nsIContent* aContent)
@@ -1170,17 +1170,17 @@ nsAccessibilityService::CreateAccessible
 
   if (role.IsEmpty() || role.EqualsLiteral("none"))
     return nullptr;
 
   if (role.EqualsLiteral("outerdoc")) {
     nsRefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc);
     return accessible.forget();
   }
- 
+
   nsRefPtr<Accessible> accessible;
 #ifdef MOZ_XUL
   // XUL controls
   if (role.EqualsLiteral("xul:alert")) {
     accessible = new XULAlertAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:button")) {
     accessible = new XULButtonAccessible(aContent, aDoc);
@@ -1553,28 +1553,32 @@ nsAccessibilityService::CreateAccessible
       // when reading by AT.
       if (aContext->IsHTMLTableRow() || aContext->IsHTMLTable())
         newAcc = new HTMLTableCellAccessibleWrap(aContent, document);
       else
         newAcc = new HyperTextAccessibleWrap(aContent, document);
       break;
 
     case eHTMLTableRowType: {
-      // Accessible HTML table row must be a child of tbody/tfoot/thead of
-      // accessible HTML table or must be a child of accessible of HTML table.
-      if (aContext->IsTable()) {
+      // Accessible HTML table row may be a child of tbody/tfoot/thead of
+      // accessible HTML table or a direct child of accessible of HTML table.
+      Accessible* table = aContext->IsTable() ?
+        aContext :
+        (aContext->Parent()->IsTable() ? aContext->Parent() : nullptr);
+
+      if (table) {
         nsIContent* parentContent = aContent->GetParent();
         nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
-        if (parentFrame->GetType() == nsGkAtoms::tableRowGroupFrame) {
+        if (parentFrame->GetType() != nsGkAtoms::tableOuterFrame) {
           parentContent = parentContent->GetParent();
           parentFrame = parentContent->GetPrimaryFrame();
         }
 
         if (parentFrame->GetType() == nsGkAtoms::tableOuterFrame &&
-            aContext->GetContent() == parentContent) {
+            table->GetContent() == parentContent) {
           newAcc = new HTMLTableRowAccessible(aContent, document);
         }
       }
       break;
     }
     case eHTMLTextFieldType:
       newAcc = new HTMLTextFieldAccessible(aContent, document);
       break;
@@ -1644,17 +1648,17 @@ nsAccessibilityService::RemoveNativeRoot
 /**
  * Return accessibility service; creating one if necessary.
  */
 nsresult
 NS_GetAccessibilityService(nsIAccessibilityService** aResult)
 {
   NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
   *aResult = nullptr;
- 
+
   if (nsAccessibilityService::gAccessibilityService) {
     NS_ADDREF(*aResult = nsAccessibilityService::gAccessibilityService);
     return NS_OK;
   }
 
   nsRefPtr<nsAccessibilityService> service = new nsAccessibilityService();
   NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY);
 
--- a/accessible/tests/mochitest/table/test_indexes_table.html
+++ b/accessible/tests/mochitest/table/test_indexes_table.html
@@ -340,17 +340,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       <tr>
          <td colspan="2">4</td>
         <td rowspan="2">5</td>
         </tr>
       <tr>
         <td>6</td>
       </tr>
       <tr>
-        <td colspan="3">7</td>       
+        <td colspan="3">7</td>
       </tr>
 
     </tbody>
   </table>
 
   <table border="1" id="tableinsane5">
     <caption>just a crazy table</caption>
     <thead>
--- a/accessible/tests/mochitest/tree/test_table.html
+++ b/accessible/tests/mochitest/tree/test_table.html
@@ -133,26 +133,40 @@
           }
         ]
       };
 
       testAccessibleTree("table3", accTree);
 
       /////////////////////////////////////////////////////////////////////////
       // table4 (display: table-row)
-      accTree = 
+      accTree =
         { TABLE: [
           { ROW: [
             { CELL: [
               { TEXT_LEAF: [ ] }
             ] }
           ] } ]
         };
       testAccessibleTree("table4", accTree);
 
+      /////////////////////////////////////////////////////////////////////////
+      // table5 (intermediate accessible for tbody)
+      accTree =
+        { TABLE: [
+          { TEXT_CONTAINER: [
+            { ROW: [
+              { CELL: [
+                { TEXT_LEAF: [ ] }
+              ] }
+            ] }
+          ] } ]
+        };
+      testAccessibleTree("table5", accTree);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -237,10 +251,18 @@
     </tr>
   </table>
 
   <table id="table4">
     <div style="display: table-row">
       <td>cell1</td>
     </div>
   </table>
+
+  <table id="table5">
+    <tbody style="display:block;overflow:auto;">
+      <tr>
+        <td>bla</td>
+      </tr>
+    </tbody>
+  </table>
 </body>
 </html>
--- a/addon-sdk/source/examples/toolbar-api/package.json
+++ b/addon-sdk/source/examples/toolbar-api/package.json
@@ -1,9 +1,12 @@
 {
   "name": "toolbar-api",
-  "title": "toolbar-api",
-  "id": "toolbar-api",
+  "title": "Toolbar API",
+  "main": "./lib/main.js",
   "description": "a toolbar api example",
   "author": "",
   "license": "MPL 2.0",
-  "version": "0.1"
+  "version": "0.1",
+  "engines": {
+    "firefox": ">=27.0 <=30.0"
+  }
 }
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -7,21 +7,22 @@ module.metadata = {
 };
 
 const { Cc, Ci } = require('chrome');
 const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader');
 const { once } = require('../system/events');
 const { exit, env, staticArgs } = require('../system');
 const { when: unload } = require('../system/unload');
 const { loadReason } = require('../self');
-const { rootURI, metadata: { preferences } } = require("@loader/options");
+const { rootURI, metadata } = require("@loader/options");
 const globals = require('../system/globals');
 const xulApp = require('../system/xul-app');
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
+const { preferences } = metadata;
 
 const NAME2TOPIC = {
   'Firefox': 'sessionstore-windows-restored',
   'Fennec': 'sessionstore-windows-restored',
   'SeaMonkey': 'sessionstore-windows-restored',
   'Thunderbird': 'mail-startup-done'
 };
 
@@ -129,23 +130,23 @@ function run(options) {
     catch(error) {
       console.exception(error);
     }
 
     // native-options does stuff directly with preferences key from package.json
     if (preferences && preferences.length > 0) {
       try {
         require('../preferences/native-options').enable(preferences);
-      } 
+      }
       catch (error) {
-        console.exception(error); 
+        console.exception(error);
       }
-    } 
+    }
     else {
-      // keeping support for addons packaged with older SDK versions, 
+      // keeping support for addons packaged with older SDK versions,
       // when cfx didn't include the 'preferences' key in @loader/options
 
       // Initialize inline options localization, without preventing addon to be
       // run in case of error
       try {
         require('../l10n/prefs').enable();
       }
       catch(error) {
@@ -153,17 +154,17 @@ function run(options) {
       }
 
       // TODO: When bug 564675 is implemented this will no longer be needed
       // Always set the default prefs, because they disappear on restart
       if (options.prefsURI) {
         // Only set if `prefsURI` specified
         try {
           setDefaultPrefs(options.prefsURI);
-        } 
+        }
         catch (err) {
           // cfx bootstrap always passes prefsURI, even in addons without prefs
         }
       }
     }
 
     // this is where the addon's main.js finally run.
     let program = main(options.loader, options.main);
--- a/addon-sdk/source/lib/sdk/content/content.js
+++ b/addon-sdk/source/lib/sdk/content/content.js
@@ -2,18 +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/. */
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
-const { deprecateFunction } = require('../util/deprecate');
+const { deprecateUsage } = require('../util/deprecate');
+
+Object.defineProperty(exports, "Loader", { 
+  get: function() {
+    deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/loader` directly.');
+    return require('./loader').Loader;
+  }
+});
 
-exports.Loader = deprecateFunction(require('./loader').Loader,
-  '`sdk/content/content` is deprecated. Please use `sdk/content/loader` directly.');
-exports.Symbiont = deprecateFunction(require('../deprecated/symbiont').Symbiont,
-  'Both `sdk/content/content` and `sdk/deprecated/symbiont` are deprecated. ' +
-  '`sdk/core/heritage` supersedes Symbiont for inheritance.');
-exports.Worker = deprecateFunction(require('./worker').Worker,
-  '`sdk/content/content` is deprecated. Please use `sdk/content/worker` directly.');
+Object.defineProperty(exports, "Symbiont", { 
+  get: function() {
+    deprecateUsage('Both `sdk/content/content` and `sdk/deprecated/symbiont` are deprecated. ' +
+                   '`sdk/core/heritage` supersedes Symbiont for inheritance.');
+    return require('../deprecated/symbiont').Symbiont;
+  }
+});
 
+Object.defineProperty(exports, "Worker", { 
+  get: function() {
+    deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/worker` directly.');
+    return require('./worker').Worker;
+  }
+});
--- a/addon-sdk/source/lib/sdk/request.js
+++ b/addon-sdk/source/lib/sdk/request.js
@@ -39,36 +39,42 @@ const { validateOptions, validateSingleO
   },
   contentType: {
     map: function (v) v || "application/x-www-form-urlencoded",
     is:  ["string"],
   },
   overrideMimeType: {
     map: function(v) v || null,
     is: ["string", "null"],
+  },
+  anonymous: {
+    map: function(v) v || false,
+    is: ["boolean", "null"],
   }
 });
 
 const REUSE_ERROR = "This request object has been used already. You must " +
                     "create a new one to make a new request."
 
 // Utility function to prep the request since it's the same between
 // request types
 function runRequest(mode, target) {
   let source = request(target)
-  let { xhr, url, content, contentType, headers, overrideMimeType } = source;
+  let { xhr, url, content, contentType, headers, overrideMimeType, anonymous } = source;
 
   let isGetOrHead = (mode == "GET" || mode == "HEAD");
 
   // If this request has already been used, then we can't reuse it.
   // Throw an error.
   if (xhr)
     throw new Error(REUSE_ERROR);
 
-  xhr = source.xhr = new XMLHttpRequest();
+  xhr = source.xhr = new XMLHttpRequest({
+    mozAnon: anonymous
+  });
 
   // Build the data to be set. For GET or HEAD requests, we want to append that
   // to the URL before opening the request.
   let data = stringify(content);
   // If the URL already has ? in it, then we want to just use &
   if (isGetOrHead && data)
     url = url + (/\?/.test(url) ? "&" : "?") + data;
 
@@ -124,16 +130,17 @@ const Request = Class({
   get content() { return request(this).content; },
   set content(value) {
     request(this).content = validateSingleOption('content', value);
   },
   get contentType() { return request(this).contentType; },
   set contentType(value) {
     request(this).contentType = validateSingleOption('contentType', value);
   },
+  get anonymous() { return request(this).anonymous; },
   get response() { return request(this).response; },
   delete: function() {
     runRequest('DELETE', this);
     return this;
   },
   get: function() {
     runRequest('GET', this);
     return this;
@@ -197,17 +204,18 @@ const Response = Class({
         headers[key] = val;
         lastKey = key;
       }
       else {
         headers[lastKey] += "\n" + val;
       }
     });
     return headers;
-  }
+  },
+  get anonymous() response(this).request.mozAnon
 });
 
 // apiUtils.validateOptions doesn't give the ability to easily validate single
 // options, so this is a wrapper that provides that ability.
 function OptionsValidator(rules) {
   return {
     validateOptions: function (options) {
       return apiUtils.validateOptions(options, rules);
--- a/addon-sdk/source/lib/sdk/self.js
+++ b/addon-sdk/source/lib/sdk/self.js
@@ -3,37 +3,48 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "stable"
 };
 
 const { CC } = require('chrome');
-const { id, name, prefixURI, rootURI, metadata,
-        version, loadReason, preferencesBranch } = require('@loader/options');
+const options = require('@loader/options');
 
+const { get } = require("./preferences/service");
 const { readURISync } = require('./net/url');
 
-const addonDataURI = prefixURI + name + '/data/';
+const id = options.id;
+
+const readPref = key => get("extensions." + id + ".sdk." + key);
+
+const name = readPref("name") || options.name;
+const version = readPref("version") || options.version;
+const loadReason = readPref("load.reason") || options.loadReason;
+const rootURI = readPref("rootURI") || options.rootURI || "";
+const baseURI = readPref("baseURI") || options.prefixURI + name + "/";
+const addonDataURI = baseURI + "data/";
+const metadata = options.metadata || {};
+const permissions = metadata.permissions || {};
+const isPacked = rootURI && rootURI.indexOf("jar:") === 0;
 
 const uri = (path="") =>
   path.contains(":") ? path : addonDataURI + path;
 
 
 // Some XPCOM APIs require valid URIs as an argument for certain operations
 // (see `nsILoginManager` for example). This property represents add-on
 // associated unique URI string that can be used for that.
 exports.uri = 'addon:' + id;
 exports.id = id;
-exports.preferencesBranch = preferencesBranch || id;
+exports.preferencesBranch = options.preferencesBranch || id;
 exports.name = name;
 exports.loadReason = loadReason;
 exports.version = version;
-// If `rootURI` is jar:file://...!/ than add-on is packed.
-exports.packed = (rootURI || '').indexOf('jar:') === 0;
+exports.packed = isPacked;
 exports.data = Object.freeze({
   url: uri,
   load: function read(path) {
     return readURISync(uri(path));
   }
 });
-exports.isPrivateBrowsingSupported = ((metadata || {}).permissions || {})['private-browsing'] === true;
+exports.isPrivateBrowsingSupported = permissions['private-browsing'] === true;
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -56,17 +56,18 @@ function activateTab(tab, window) {
   else if (window && window.BrowserApp) {
     window.BrowserApp.selectTab(tab);
   }
   return null;
 }
 exports.activateTab = activateTab;
 
 function getTabBrowser(window) {
-  return window.gBrowser;
+  // bug 1009938 - may be null in SeaMonkey
+  return window.gBrowser || window.getBrowser();
 }
 exports.getTabBrowser = getTabBrowser;
 
 function getTabContainer(window) {
   return getTabBrowser(window).tabContainer;
 }
 exports.getTabContainer = getTabContainer;
 
@@ -235,73 +236,20 @@ exports.getTabContentWindow = getTabCont
  */
 function getAllTabContentWindows() {
   return getTabs().map(getTabContentWindow);
 }
 exports.getAllTabContentWindows = getAllTabContentWindows;
 
 // gets the tab containing the provided window
 function getTabForContentWindow(window) {
-  // Retrieve the topmost frame container. It can be either <xul:browser>,
-  // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser.
-  let browser;
-  try {
-    browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShell)
-                    .chromeEventHandler;
-  } catch(e) {
-    // Bug 699450: The tab may already have been detached so that `window` is
-    // in a almost destroyed state and can't be queryinterfaced anymore.
-  }
-
-  // Is null for toplevel documents
-  if (!browser) {
-    return null;
-  }
-
-  // Retrieve the owner window, should be browser.xul one
-  let chromeWindow = browser.ownerDocument.defaultView;
-
-  // Ensure that it is top-level browser window.
-  // We need extra checks because of Mac hidden window that has a broken
-  // `gBrowser` global attribute.
-  if ('gBrowser' in chromeWindow && chromeWindow.gBrowser &&
-      'browsers' in chromeWindow.gBrowser) {
-    // Looks like we are on Firefox Desktop
-    // Then search for the position in tabbrowser in order to get the tab object
-    let browsers = chromeWindow.gBrowser.browsers;
-    let i = browsers.indexOf(browser);
-    if (i !== -1)
-      return chromeWindow.gBrowser.tabs[i];
-    return null;
-  }
-  // Fennec
-  else if ('BrowserApp' in chromeWindow) {
-    return getTabForWindow(window);
-  }
-
-  return null;
+  return getTabs().find(tab => getTabContentWindow(tab) === window.top) || null;
 }
 exports.getTabForContentWindow = getTabForContentWindow;
 
-// used on fennec
-function getTabForWindow(window) {
-  for each (let { BrowserApp } in getWindows()) {
-    if (!BrowserApp)
-      continue;
-
-    for each (let tab in BrowserApp.tabs) {
-      if (tab.browser.contentWindow == window.top)
-        return tab;
-    }
-  }
-  return null;
-}
-
 function getTabURL(tab) {
   if (tab.browser) // fennec
     return String(tab.browser.currentURI.spec);
   return String(getBrowserForTab(tab).currentURI.spec);
 }
 exports.getTabURL = getTabURL;
 
 function setTabURL(tab, url) {
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -379,33 +379,32 @@ const resolve = iced(function resolve(id
 });
 exports.resolve = resolve;
 
 // Node-style module lookup
 // Takes an id and path and attempts to load a file using node's resolving
 // algorithm.
 // `id` should already be resolved relatively at this point.
 // http://nodejs.org/api/modules.html#modules_all_together
-const nodeResolve = iced(function nodeResolve(id, requirer, { manifest, rootURI }) {
+const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
   // Resolve again
   id = exports.resolve(id, requirer);
 
   // we assume that extensions are correct, i.e., a directory doesnt't have '.js'
   // and a js file isn't named 'file.json.js'
-
   let fullId = join(rootURI, id);
 
   let resolvedPath;
   if (resolvedPath = loadAsFile(fullId))
     return stripBase(rootURI, resolvedPath);
   else if (resolvedPath = loadAsDirectory(fullId))
     return stripBase(rootURI, resolvedPath);
   // If manifest has dependencies, attempt to look up node modules
   // in the `dependencies` list
-  else if (manifest.dependencies) {
+  else {
     let dirs = getNodeModulePaths(dirname(join(rootURI, requirer))).map(dir => join(dir, id));
     for (let i = 0; i < dirs.length; i++) {
       if (resolvedPath = loadAsFile(dirs[i]))
         return stripBase(rootURI, resolvedPath);
       if (resolvedPath = loadAsDirectory(dirs[i]))
         return stripBase(rootURI, resolvedPath);
     }
   }
@@ -528,17 +527,16 @@ const Require = iced(function Require(lo
       throw Error('you must provide a module name when calling require() from '
                   + requirer.id, requirer.uri);
 
     let requirement;
     let uri;
 
     // TODO should get native Firefox modules before doing node-style lookups
     // to save on loading time
-
     if (isNative) {
       // If a requireMap is available from `generateMap`, use that to
       // immediately resolve the node-style mapping.
       if (requireMap && requireMap[requirer.id])
         requirement = requireMap[requirer.id][id];
 
       // For native modules, we want to check if it's a module specified
       // in 'modules', like `chrome`, or `@loader` -- if it exists,
@@ -688,17 +686,18 @@ exports.unload = unload;
 //   If `resolve` does not returns `uri` string exception will be thrown by
 //   an associated `require` call.
 const Loader = iced(function Loader(options) {
   let console = new ConsoleAPI({
     consoleID: options.id ? "addon/" + options.id : ""
   });
 
   let {
-    modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative
+    modules, globals, resolve, paths, rootURI,
+    manifest, requireMap, isNative, metadata
   } = override({
     paths: {},
     modules: {},
     globals: {
       console: console
     },
     resolve: options.isNative ?
       exports.nodeResolve :
@@ -743,16 +742,17 @@ const Loader = iced(function Loader(opti
   // state. We freeze it and mark make it's properties non-enumerable
   // as they are pure implementation detail that no one should rely upon.
   let returnObj = {
     destructor: { enumerable: false, value: destructor },
     globals: { enumerable: false, value: globals },
     mapping: { enumerable: false, value: mapping },
     // Map of module objects indexed by module URIs.
     modules: { enumerable: false, value: modules },
+    metadata: { enumerable: false, value: metadata },
     // Map of module sandboxes indexed by module URIs.
     sandboxes: { enumerable: false, value: {} },
     resolve: { enumerable: false, value: resolve },
     // ID of the addon, if provided.
     id: { enumerable: false, value: options.id },
     // Whether the modules loaded should be ignored by the debugger
     invisibleToDebugger: { enumerable: false,
                            value: options.invisibleToDebugger || false },
--- a/addon-sdk/source/python-lib/cuddlefish/__init__.py
+++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py
@@ -1,16 +1,15 @@
 # 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/.
 
 import sys
 import os
 import optparse
-import webbrowser
 import time
 
 from copy import copy
 import simplejson as json
 from cuddlefish import packaging
 from cuddlefish._version import get_versions
 
 MOZRUNNER_BIN_NOT_FOUND = 'Mozrunner could not locate your binary'
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
@@ -143,22 +143,22 @@ class SmallXPI(unittest.TestCase):
                      ]]
 
         add_addon_sdk= lambda path: os.path.join(addon_sdk_dir, path)
         expected.extend([add_addon_sdk(module) for module in [
             os.path.join("sdk", "self.js"),
             os.path.join("sdk", "core", "promise.js"),
             os.path.join("sdk", "net", "url.js"),
             os.path.join("sdk", "util", "object.js"),
-            os.path.join("sdk", "util", "array.js")
+            os.path.join("sdk", "util", "array.js"),
+            os.path.join("sdk", "preferences", "service.js")
             ]])
 
         missing = set(expected) - set(used_files)
         extra = set(used_files) - set(expected)
-
         self.failUnlessEqual(list(missing), [])
         self.failUnlessEqual(list(extra), [])
         used_deps = m.get_used_packages()
 
         build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
                                                     used_deps,
                                                     include_tests=False)
         options = {'main': target_cfg.main}
@@ -185,16 +185,18 @@ class SmallXPI(unittest.TestCase):
                     "resources/addon-sdk/lib/sdk/self.js",
                     "resources/addon-sdk/lib/sdk/core/",
                     "resources/addon-sdk/lib/sdk/util/",
                     "resources/addon-sdk/lib/sdk/net/",
                     "resources/addon-sdk/lib/sdk/core/promise.js",
                     "resources/addon-sdk/lib/sdk/util/object.js",
                     "resources/addon-sdk/lib/sdk/util/array.js",
                     "resources/addon-sdk/lib/sdk/net/url.js",
+                    "resources/addon-sdk/lib/sdk/preferences/",
+                    "resources/addon-sdk/lib/sdk/preferences/service.js",
                     "resources/three/",
                     "resources/three/lib/",
                     "resources/three/lib/main.js",
                     "resources/three/data/",
                     "resources/three/data/msg.txt",
                     "resources/three/data/subdir/",
                     "resources/three/data/subdir/submsg.txt",
                     "resources/three-a/",
--- a/addon-sdk/source/test/addons/content-permissions/main.js
+++ b/addon-sdk/source/test/addons/content-permissions/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 { PageMod } = require("sdk/page-mod");
 const tabs = require("sdk/tabs");
 const { startServerAsync } = require("sdk/test/httpd");
 
--- a/addon-sdk/source/test/addons/l10n-properties/main.js
+++ b/addon-sdk/source/test/addons/l10n-properties/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 prefs = require("sdk/preferences/service");
 const { Loader } = require('sdk/test/loader');
 const { resolveURI } = require('toolkit/loader');
 const { rootURI } = require("@loader/options");
--- a/addon-sdk/source/test/addons/l10n/main.js
+++ b/addon-sdk/source/test/addons/l10n/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 prefs = require("sdk/preferences/service");
 const { Loader } = require('sdk/test/loader');
 const { resolveURI } = require('toolkit/loader');
 const { rootURI } = require("@loader/options");
--- a/addon-sdk/source/test/addons/layout-change/main.js
+++ b/addon-sdk/source/test/addons/layout-change/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 { LoaderWithHookedConsole } = require('sdk/test/loader');
 const { loader } = LoaderWithHookedConsole(module);
 const app = require("sdk/system/xul-app");
--- a/addon-sdk/source/test/addons/packed/main.js
+++ b/addon-sdk/source/test/addons/packed/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 { packed } = require("sdk/self");
 const url = require("sdk/url");
 
 exports["test self.packed"] = function (assert) {
--- a/addon-sdk/source/test/addons/require/main.js
+++ b/addon-sdk/source/test/addons/require/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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";
 
 exports["test local vs sdk module"] = function (assert) {
   assert.notEqual(require("memory"),
                   require("sdk/deprecated/memory"),
                   "Local module takes the priority over sdk modules");
--- a/addon-sdk/source/test/addons/simple-prefs/lib/main.js
+++ b/addon-sdk/source/test/addons/simple-prefs/lib/main.js
@@ -129,17 +129,17 @@ if (app.is('Firefox')) {
 
               tab.close(done);
             }
           });
       	}
       });
   }
 
-  // run it again, to test against inline options document caching 
+  // run it again, to test against inline options document caching
   // and duplication of <setting> nodes upon re-entry to about:addons
   exports.testAgainstDocCaching = exports.testAOM;
 
 }
 
 exports.testDefaultPreferencesBranch = function(assert) {
   assert.equal(preferencesBranch, self.id, 'preferencesBranch default the same as self.id');
 }
--- a/addon-sdk/source/test/addons/unpacked/main.js
+++ b/addon-sdk/source/test/addons/unpacked/main.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 { packed } = require("sdk/self");
 const url = require("sdk/url");
 
 exports["test self.packed"] = function (assert) {
--- a/addon-sdk/source/test/test-child_process.js
+++ b/addon-sdk/source/test/test-child_process.js
@@ -535,8 +535,11 @@ exports.testFork = function (assert) {
   assert.throws(function () {
     fork();
   }, /not currently supported/, 'fork() correctly throws an unsupported error');
 };
 
 after(exports, cleanUp);
 
 require("test").run(exports);
+
+// Test disabled because of bug 979675
+module.exports = {};
--- a/addon-sdk/source/test/test-content-symbiont.js
+++ b/addon-sdk/source/test/test-content-symbiont.js
@@ -4,16 +4,20 @@
 "use strict";
 
 const { Cc, Ci } = require('chrome');
 const { Symbiont } = require('sdk/deprecated/symbiont');
 const self = require('sdk/self');
 const fixtures = require("./fixtures");
 const { close } = require('sdk/window/helpers');
 const app = require("sdk/system/xul-app");
+const { LoaderWithHookedConsole } = require('sdk/test/loader');
+const { set: setPref, get: getPref } = require("sdk/preferences/service");
+
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 function makeWindow() {
   let content =
     '<?xml version="1.0"?>' +
     '<window ' +
     'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' +
     '<iframe id="content" type="content"/>' +
     '</window>';
@@ -152,9 +156,30 @@ exports["test:document element present o
         assert.ok(message, "document element present on 'start'");
       else
         assert.pass("document element not necessarily present on 'start'");
       done();
     }
   });
 };
 
+exports["test:content/content deprecation"] = function(assert) {
+  let pref = getPref(DEPRECATE_PREF, false);
+  setPref(DEPRECATE_PREF, true);
+
+  const { loader, messages } = LoaderWithHookedConsole(module);
+  const { Loader, Symbiont, Worker } = loader.require("sdk/content/content");
+
+  assert.equal(messages.length, 3, "Should see three warnings");
+
+  assert.strictEqual(Loader, loader.require('sdk/content/loader').Loader,
+    "Loader from content/content is the exact same object as the one from content/loader");
+
+  assert.strictEqual(Symbiont, loader.require('sdk/deprecated/symbiont').Symbiont,
+    "Symbiont from content/content is the exact same object as the one from deprecated/symbiont");
+
+  assert.strictEqual(Worker, loader.require('sdk/content/worker').Worker,
+    "Worker from content/content is the exact same object as the one from content/worker");
+
+  setPref(DEPRECATE_PREF, pref);
+}
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-request.js
+++ b/addon-sdk/source/test/test-request.js
@@ -38,16 +38,33 @@ exports.testOptionsValidator = function(
     url: "http://playground.zpao.com/jetpack/request/text.php",
     onComplete: function () {}
   });
   assert.throws(function () {
     req.url = 'www.mozilla.org';
   }, /The option "url" is invalid/);
   // The url shouldn't have changed, so check that
   assert.equal(req.url, "http://playground.zpao.com/jetpack/request/text.php");
+
+  // Test default anonymous parameter value
+  assert.equal(req.anonymous, false);
+  // Test set anonymous parameter value
+  req = Request({
+    url: "http://playground.zpao.com/jetpack/request/text.php",
+    anonymous: true,
+    onComplete: function () {}
+  });
+  assert.equal(req.anonymous, true);
+  // Test wrong value as anonymous parameter value
+  assert.throws(function() {
+    Request({
+      url: "http://playground.zpao.com/jetpack/request/text.php",
+      anonymous: "invalidvalue"
+    });
+  }, /The option "anonymous" must be one of the following types/);
 };
 
 exports.testContentValidator = function(assert, done) {
   runMultipleURLs(null, assert, done, {
     url: "data:text/html;charset=utf-8,response",
     content: { 'key1' : null, 'key2' : 'some value' },
     onComplete: function(response) {
       assert.equal(response.text, "response?key1=null&key2=some+value");
@@ -178,16 +195,70 @@ exports.test3rdPartyCookies = function (
           assert.equal(response.headers['x-jetpack-3rd-party'], 'true');
           srv.stop(done);
         }
       }).get();
     }
   }).get();
 };
 
+// Test anonymous request behavior
+exports.testAnonymousRequest = function(assert, done) {
+  let srv = startServerAsync(port, basePath);
+  let basename = "test-anonymous-request.sjs";
+  let testUrl = "http://localhost:" + port + "/" + basename;
+  // Function to handle the requests in the server
+  let content = function handleRequest(request, response) {
+    // Request to store cookie
+    response.setHeader("Set-Cookie", "anonymousKey=anonymousValue;", "true");
+    // Set response content type
+    response.setHeader("Content-Type", "application/json");
+    // Check if cookie was send during request
+    var cookiePresent = request.hasHeader("Cookie");
+    // Create server respone content
+    response.write(JSON.stringify({ "hasCookie": cookiePresent }));
+  }.toString();
+  prepareFile(basename, content);
+  // Create request callbacks
+  var checkCookieCreated = function (response) {
+    // Check that the server created the cookie
+    assert.equal(response.headers['Set-Cookie'], 'anonymousKey=anonymousValue;');
+    // Make an other request and check that the server this time got the cookie
+    Request({
+      url: testUrl,
+      onComplete: checkCookieSend
+    }).get();
+  },
+  checkCookieSend = function (response) {
+    // Check the response sent headers and cookies
+    assert.equal(response.anonymous, false);
+    // Check the server got the created cookie
+    assert.equal(response.json.hasCookie, true);
+    // Make a anonymous request and check the server did not get the cookie
+    Request({
+      url: testUrl,
+      anonymous: true,
+      onComplete: checkCookieNotSend
+    }).get();
+  },
+  checkCookieNotSend = function (response) {
+    // Check the response is anonymous
+    assert.equal(response.anonymous, true);
+    // Check the server did not get the cookie
+    assert.equal(response.json.hasCookie, false);
+    // Stop the server
+    srv.stop(done);
+  };
+  // Make the first request to create cookie
+  Request({
+    url: testUrl,
+    onComplete: checkCookieCreated
+  }).get();
+};
+
 exports.testSimpleJSON = function (assert, done) {
   let srv = startServerAsync(port, basePath);
   let json = { foo: "bar" };
   let basename = "test-request.json";
   prepareFile(basename, JSON.stringify(json));
 
   runMultipleURLs(srv, assert, done, {
     url: "http://localhost:" + port + "/" + basename,
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1232,22 +1232,18 @@ pref("devtools.toolbar.enabled", true);
 pref("devtools.toolbar.visible", false);
 pref("devtools.commands.dir", "");
 
 // Enable the app manager
 pref("devtools.appmanager.enabled", true);
 pref("devtools.appmanager.lastTab", "help");
 pref("devtools.appmanager.manifestEditor.enabled", true);
 
-// Enable devtools webide
-#ifdef MOZ_DEVTOOLS_WEBIDE
-pref("devtools.webide.enabled", true);
-#else
+// Disable devtools webide until bug 1007059
 pref("devtools.webide.enabled", false);
-#endif
 
 // Toolbox preferences
 pref("devtools.toolbox.footer.height", 250);
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
 pref("devtools.toolbox.selectedTool", "webconsole");
 pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage"]');
 pref("devtools.toolbox.sideEnabled", true);
--- a/browser/base/content/abouthome/aboutHome.xhtml
+++ b/browser/base/content/abouthome/aboutHome.xhtml
@@ -57,16 +57,20 @@
 
     <div id="launcher">
       <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button>
       <button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button>
       <button class="launchButton" id="history">&abouthome.historyButton.label;</button>
       <button class="launchButton" id="apps" hidden="true">&abouthome.appsButton.label;</button>
       <button class="launchButton" id="addons">&abouthome.addonsButton.label;</button>
       <button class="launchButton" id="sync">&abouthome.syncButton.label;</button>
-      <button class="launchButton" id="settings">&abouthome.settingsButton.label;</button>
+#ifdef XP_WIN
+      <button class="launchButton" id="settings">&abouthome.preferencesButtonWin.label;</button>
+#else
+      <button class="launchButton" id="settings">&abouthome.preferencesButtonUnix.label;</button>
+#endif
       <div id="restorePreviousSessionSeparator"/>
       <button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button>
     </div>
 
     <a id="aboutMozilla" href="https://www.mozilla.org/about/?utm_source=about-home&amp;utm_medium=Referral"/>
   </body>
 </html>
deleted file mode 100644
index 00c279d2e9f4b95f230b58ef897dfe689e6c36cb..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 979d0a5018299955cebb5378c941f72c907769cb..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 15605473f77ac5371ca3757e2a13a0e7c1427e5c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/base/content/sync/customize.xul
+++ b/browser/base/content/sync/customize.xul
@@ -33,33 +33,36 @@
     <description id="sync-customize-subtitle"
 #ifdef XP_UNIX
                  value="&syncCustomizeUnix.description;"
 #else
                  value="&syncCustomize.description;"
 #endif
                  />
 
-    <checkbox label="&engine.tabs.label;"
-              accesskey="&engine.tabs.accesskey;"
-              preference="engine.tabs"/>
-    <checkbox label="&engine.bookmarks.label;"
-              accesskey="&engine.bookmarks.accesskey;"
-              preference="engine.bookmarks"/>
-    <checkbox id="fxa-pweng-chk"
-              label="&engine.passwords.label;"
-              accesskey="&engine.passwords.accesskey;"
-              preference="engine.passwords"/>
-    <checkbox label="&engine.history.label;"
-              accesskey="&engine.history.accesskey;"
-              preference="engine.history"/>
-    <checkbox label="&engine.addons.label;"
-              accesskey="&engine.addons.accesskey;"
-              preference="engine.addons"/>
-    <checkbox label="&engine.prefs.label;"
-              accesskey="&engine.prefs.accesskey;"
-              preference="engine.prefs"/>
+  <vbox align="start">
+      <checkbox label="&engine.tabs.label;"
+                accesskey="&engine.tabs.accesskey;"
+                preference="engine.tabs"/>
+      <checkbox label="&engine.bookmarks.label;"
+                accesskey="&engine.bookmarks.accesskey;"
+                preference="engine.bookmarks"/>
+      <checkbox id="fxa-pweng-chk"
+                label="&engine.passwords.label;"
+                accesskey="&engine.passwords.accesskey;"
+                preference="engine.passwords"/>
+      <checkbox label="&engine.history.label;"
+                accesskey="&engine.history.accesskey;"
+                preference="engine.history"/>
+      <checkbox label="&engine.addons.label;"
+                accesskey="&engine.addons.accesskey;"
+                preference="engine.addons"/>
+      <checkbox label="&engine.prefs.label;"
+                accesskey="&engine.prefs.accesskey;"
+                preference="engine.prefs"/>
+  </vbox>
+
   </prefpane>
 
   <script type="application/javascript"
           src="chrome://browser/content/sync/customize.js" />
 
 </dialog>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -13,17 +13,17 @@ browser.jar:
 #endif
 %  overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul
 %  overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul
 
 *       content/browser/aboutDialog.xul               (content/aboutDialog.xul)
 *       content/browser/aboutDialog.js                (content/aboutDialog.js)
         content/browser/aboutDialog.css               (content/aboutDialog.css)
         content/browser/aboutRobots.xhtml             (content/aboutRobots.xhtml)
-        content/browser/abouthome/aboutHome.xhtml     (content/abouthome/aboutHome.xhtml)
+*       content/browser/abouthome/aboutHome.xhtml     (content/abouthome/aboutHome.xhtml)
         content/browser/abouthome/aboutHome.js        (content/abouthome/aboutHome.js)
 *       content/browser/abouthome/aboutHome.css       (content/abouthome/aboutHome.css)
         content/browser/abouthome/snippet1.png        (content/abouthome/snippet1.png)
         content/browser/abouthome/snippet2.png        (content/abouthome/snippet2.png)
         content/browser/abouthome/downloads.png       (content/abouthome/downloads.png)
         content/browser/abouthome/bookmarks.png       (content/abouthome/bookmarks.png)
         content/browser/abouthome/history.png         (content/abouthome/history.png)
         content/browser/abouthome/apps.png            (content/abouthome/apps.png)
@@ -73,19 +73,16 @@ browser.jar:
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 *       content/browser/chatWindow.xul                (content/chatWindow.xul)
         content/browser/content.js                    (content/content.js)
-        content/browser/fonts/ClearSans-Regular.woff  (content/fonts/ClearSans-Regular.woff)
-        content/browser/fonts/FiraSans-Regular.woff   (content/fonts/FiraSans-Regular.woff)
-        content/browser/fonts/FiraSans-Light.woff     (content/fonts/FiraSans-Light.woff)
         content/browser/newtab/newTab.xul             (content/newtab/newTab.xul)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
--- a/browser/branding/aurora/pref/firefox-branding.js
+++ b/browser/branding/aurora/pref/firefox-branding.js
@@ -7,18 +7,18 @@
 // We don't have pages ready for this, so leave these prefs empty for now (bug 648330)
 pref("startup.homepage_override_url","");
 pref("startup.homepage_welcome_url","");
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 28800); // 8 hours
 // The time interval between the downloading of mar file chunks in the
 // background (in seconds)
 pref("app.update.download.backgroundInterval", 60);
-// Give the user x seconds to react before showing the big UI. default=24 hours
-pref("app.update.promptWaitTime", 86400);
+// Give the user x seconds to react before showing the big UI. default=168 hours
+pref("app.update.promptWaitTime", 604800);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
 pref("app.update.url.manual", "https://www.mozilla.org/firefox/aurora/");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard. 
 pref("app.update.url.details", "https://www.mozilla.org/firefox/aurora/");
 
 // The number of days a binary is permitted to be old
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -180,44 +180,45 @@
 
         <!-- These panels are for the Firefox Accounts identity provider -->
         <vbox id="fxaDeterminingStatus" align="center">
           <spacer flex="1"/>
           <p>&determiningAcctStatus.label;</p>
           <spacer flex="1"/>
         </vbox>
 
-        <vbox id="noFxaAccount">
+        <vbox id="noFxaAccount" align="start">
           <label>&welcome.description;</label>
-          <label class="text-link"
-                 onclick="gSyncPane.signUp(); return false;"
-                 value="&welcome.createAccount.label;"/>
-          <label class="text-link"
-                 onclick="gSyncPane.signIn(); return false;"
-                 value="&welcome.signIn.label;"/>
-          <separator/>
-          <label class="text-link"
-                 onclick="gSyncPane.openOldSyncSupportPage(); return false;"
-                 value="&welcome.useOldSync.label;"/>
+            <label class="text-link"
+                   onclick="gSyncPane.signUp(); return false;"
+                   value="&welcome.createAccount.label;"/>
+            <label class="text-link"
+                   onclick="gSyncPane.signIn(); return false;"
+                   value="&welcome.signIn.label;"/>
+            <separator/>
+            <label class="text-link"
+                   onclick="gSyncPane.openOldSyncSupportPage(); return false;"
+                   value="&welcome.useOldSync.label;"/>
         </vbox>
 
         <vbox id="hasFxaAccount">
           <groupbox id="fxaGroup">
             <caption label="&syncBrand.fxAccount.label;"/>
 
             <deck id="fxaLoginStatus">
 
               <!-- logged in and verified and all is good -->
               <hbox>
                 <label id="fxaEmailAddress1"/>
-                <vbox flex="1">
+                <vbox>
                   <label class="text-link"
                          onclick="gSyncPane.manageFirefoxAccount();"
                          value="&manage.label;"/>
                 </vbox>
+                <spacer flex="1"/>
                 <vbox>
                   <button id="fxaUnlinkButton"
                           oncommand="gSyncPane.unlinkFirefoxAccount(true);"
                           label="&disconnect.label;"/>
                 </vbox>
               </hbox>
 
               <!-- logged in to an unverified account -->
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -439,17 +439,16 @@ ThreadState.prototype = {
    * Connect to the current thread client.
    */
   connect: function() {
     dumpn("ThreadState is connecting...");
     this.activeThread.addListener("paused", this._update);
     this.activeThread.addListener("resumed", this._update);
     this.activeThread.pauseOnExceptions(Prefs.pauseOnExceptions,
                                         Prefs.ignoreCaughtExceptions);
-    this.handleTabNavigation();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function() {
     if (!this.activeThread) {
       return;
--- a/browser/devtools/moz.build
+++ b/browser/devtools/moz.build
@@ -23,19 +23,17 @@ DIRS += [
     'shadereditor',
     'shared',
     'sourceeditor',
     'styleeditor',
     'styleinspector',
     'tilt',
     'webaudioeditor',
     'webconsole',
+    'webide',
 ]
 
-if CONFIG['MOZ_DEVTOOLS_WEBIDE']:
-    DIRS += ['webide']
-
 EXTRA_COMPONENTS += [
     'devtools-clhandler.js',
     'devtools-clhandler.manifest',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
--- a/browser/devtools/projecteditor/chrome/content/projecteditor-loader.js
+++ b/browser/devtools/projecteditor/chrome/content/projecteditor-loader.js
@@ -2,19 +2,19 @@ const Cu = Components.utils;
 const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const require = devtools.require;
 const promise = require("projecteditor/helpers/promise");
 const ProjectEditor = require("projecteditor/projecteditor");
 
 const SAMPLE_PATH = buildTempDirectoryStructure();
-const SAMPLE_NAME = "DevTools Content";
+const SAMPLE_NAME = "DevTools Content Application Name";
 const SAMPLE_PROJECT_URL = "http://mozilla.org";
-const SAMPLE_ICON = "chrome://browser/skin/devtools/tool-options.svg";
+const SAMPLE_ICON = "chrome://browser/skin/devtools/tool-debugger.svg";
 
 /**
  * Create a workspace for working on projecteditor, available at
  * chrome://browser/content/devtools/projecteditor-loader.xul.
  * This emulates the integration points that the app manager uses.
  */
 document.addEventListener("DOMContentLoaded", function onDOMReady(e) {
   document.removeEventListener("DOMContentLoaded", onDOMReady, false);
@@ -51,21 +51,23 @@ document.addEventListener("DOMContentLoa
   projecteditor.on("onCommand", (cmd) => {
     console.log("Command: " + cmd);
   });
 
   projecteditor.loaded.then(() => {
     projecteditor.setProjectToAppPath(SAMPLE_PATH, {
       name: SAMPLE_NAME,
       iconUrl: SAMPLE_ICON,
-      projectOverviewURL: SAMPLE_PROJECT_URL
+      projectOverviewURL: SAMPLE_PROJECT_URL,
+      validationStatus: "valid"
     }).then(() => {
       let allResources = projecteditor.project.allResources();
       console.log("All resources have been loaded", allResources, allResources.map(r=>r.basename).join("|"));
     });
+
   });
 
 }, false);
 
 /**
  * Build a temporary directory as a workspace for this loader
  * https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O
  */
--- a/browser/devtools/projecteditor/chrome/content/projecteditor.xul
+++ b/browser/devtools/projecteditor/chrome/content/projecteditor.xul
@@ -1,18 +1,16 @@
 <?xml version="1.0"?>
 <!-- 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/. -->
 <?xml-stylesheet href="chrome://browser/skin/devtools/light-theme.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/projecteditor/projecteditor.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/devtools/debugger.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/devtools/markup-view.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/devtools/markup-view.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 
 <!DOCTYPE window [
 <!ENTITY % scratchpadDTD SYSTEM "chrome://browser/locale/devtools/scratchpad.dtd" >
  %scratchpadDTD;
--- a/browser/devtools/projecteditor/lib/plugins/app-manager/plugin.js
+++ b/browser/devtools/projecteditor/lib/plugins/app-manager/plugin.js
@@ -1,15 +1,16 @@
 const { Cu } = require("chrome");
 const { Class } = require("sdk/core/heritage");
 const { EventTarget } = require("sdk/event/target");
 const { emit } = require("sdk/event/core");
 const promise = require("projecteditor/helpers/promise");
 var { registerPlugin, Plugin } = require("projecteditor/plugins/core");
 const { AppProjectEditor } = require("./app-project-editor");
+const OPTION_URL = "chrome://browser/skin/devtools/tool-options.svg";
 
 var AppManagerRenderer = Class({
   extends: Plugin,
 
   isAppManagerProject: function() {
     return !!this.host.project.appManagerOpts;
   },
   editorForResource: function(resource) {
@@ -20,28 +21,40 @@ var AppManagerRenderer = Class({
   onAnnotate: function(resource, editor, elt) {
     if (resource.parent || !this.isAppManagerProject()) {
       return;
     }
 
     let {appManagerOpts} = this.host.project;
     let doc = elt.ownerDocument;
     let image = doc.createElement("image");
-    let label = doc.createElement("label");
+    let optionImage = doc.createElement("image");
+    let flexElement = doc.createElement("div");
+    let nameLabel = doc.createElement("span");
+    let statusElement = doc.createElement("div");
 
-    label.className = "project-name-label";
     image.className = "project-image";
+    optionImage.className = "project-options";
+    nameLabel.className = "project-name-label";
+    statusElement.className = "project-status";
+    flexElement.className = "project-flex";
 
     let name = appManagerOpts.name || resource.basename;
     let url = appManagerOpts.iconUrl || "icon-sample.png";
+    let status = appManagerOpts.validationStatus || "unknown";
 
-    label.textContent = name;
+    nameLabel.textContent = name;
     image.setAttribute("src", url);
+    optionImage.setAttribute("src", OPTION_URL);
+    statusElement.setAttribute("status", status)
 
     elt.innerHTML = "";
     elt.appendChild(image);
-    elt.appendChild(label);
+    elt.appendChild(nameLabel);
+    elt.appendChild(flexElement);
+    elt.appendChild(statusElement);
+    elt.appendChild(optionImage);
     return true;
   }
 });
 
 exports.AppManagerRenderer = AppManagerRenderer;
 registerPlugin(AppManagerRenderer);
--- a/browser/devtools/projecteditor/lib/project.js
+++ b/browser/devtools/projecteditor/lib/project.js
@@ -131,23 +131,21 @@ var Project = Class({
     for (let [path, store] of this.localStores) {
       yield store;
     }
   },
 
   /**
    * Get every file path used inside of the project.
    *
-   * @returns generator-iterator<string>
+   * @returns Array<string>
    *          A list of all file paths
    */
-  allPaths: function*() {
-    for (let [path, store] of this.localStores) {
-      yield path;
-    }
+  allPaths: function() {
+    return [path for (path of this.localStores.keys())];
   },
 
   /**
    * Get the store that contains a path.
    *
    * @returns Store
    *          The store, if any.  Will return null if no store
    *          contains the given path.
--- a/browser/devtools/projecteditor/lib/projecteditor.js
+++ b/browser/devtools/projecteditor/lib/projecteditor.js
@@ -259,24 +259,39 @@ var ProjectEditor = Class({
 
   /**
    * Set the current project viewed by the projecteditor to a single path,
    * used by the app manager.
    *
    * @param string path
    *               The file path to set
    * @param Object opts
-   *               Custom options used by the project. See plugins/app-manager.
+   *               Custom options used by the project.
+   *                - name: display name for project
+   *                - iconUrl: path to icon for project
+   *                - validationStatus: one of 'unknown|error|warning|valid'
+   *                - projectOverviewURL: path to load for iframe when project
+   *                    is selected in the tree.
    * @param Promise
    *        Promise that is resolved once the project is ready to be used.
    */
   setProjectToAppPath: function(path, opts = {}) {
     this.project.appManagerOpts = opts;
-    this.project.removeAllStores();
-    this.project.addPath(path);
+
+    let existingPaths = this.project.allPaths();
+    if (existingPaths.length !== 1 || existingPaths[0] !== path) {
+      // Only fully reset if this is a new path.
+      this.project.removeAllStores();
+      this.project.addPath(path);
+    } else {
+      // Otherwise, just ask for the root to be redrawn
+      let rootResource = this.project.localStores.get(path).root;
+      emit(rootResource, "label-change", rootResource);
+    }
+
     return this.project.refresh();
   },
 
   /**
    * Open a resource in a particular shell.
    *
    * @param Resource resource
    *                 The file to be opened.
--- a/browser/devtools/projecteditor/lib/tree.js
+++ b/browser/devtools/projecteditor/lib/tree.js
@@ -34,17 +34,17 @@ var ResourceContainer = Class({
 
     let doc = tree.doc;
 
     this.elt = doc.createElementNS(HTML_NS, "li");
     this.elt.classList.add("child");
 
     this.line = doc.createElementNS(HTML_NS, "div");
     this.line.classList.add("child");
-    this.line.classList.add("side-menu-widget-item");
+    this.line.classList.add("entry");
     this.line.setAttribute("theme", "dark");
     this.line.setAttribute("tabindex", "0");
 
     this.elt.appendChild(this.line);
 
     this.highlighter = doc.createElementNS(HTML_NS, "span");
     this.highlighter.classList.add("highlighter");
     this.line.appendChild(this.highlighter);
@@ -218,25 +218,24 @@ var TreeView = Class({
     this.options = merge({
       resourceFormatter: function(resource, elt) {
         elt.textContent = resource.toString();
       }
     }, options);
     this.models = new Set();
     this.roots = new Set();
     this._containers = new Map();
-    this.elt = document.createElement("vbox");
+    this.elt = document.createElementNS(HTML_NS, "div");
     this.elt.tree = this;
-    this.elt.className = "side-menu-widget-container sources-tree";
+    this.elt.className = "sources-tree";
     this.elt.setAttribute("with-arrows", "true");
     this.elt.setAttribute("theme", "dark");
     this.elt.setAttribute("flex", "1");
 
     this.children = document.createElementNS(HTML_NS, "ul");
-    this.children.setAttribute("flex", "1");
     this.elt.appendChild(this.children);
 
     this.resourceChildrenChanged = this.resourceChildrenChanged.bind(this);
     this.updateResource = this.updateResource.bind(this);
   },
 
   destroy: function() {
     this._destroyed = true;
@@ -310,17 +309,17 @@ var TreeView = Class({
     this.roots.add(model.root);
     model.root.refresh().then(root => {
       if (this._destroyed || !this.models.has(model)) {
         // model may have been removed during the initial refresh.
         // In this case, do not import the resource or add to DOM, just leave it be.
         return;
       }
       let container = this.importResource(root);
-      container.line.classList.add("side-menu-widget-group-title");
+      container.line.classList.add("entry-group-title");
       container.line.setAttribute("theme", "dark");
       this.selectContainer(container);
 
       this.children.insertBefore(container.elt, placeholder);
       this.children.removeChild(placeholder);
     });
   },
 
--- a/browser/devtools/projecteditor/test/browser.ini
+++ b/browser/devtools/projecteditor/test/browser.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 skip-if = os == "win" && !debug # Bug 1014046
 subsuite = devtools
 support-files =
   head.js
   helper_homepage.html
 
+[browser_projecteditor_app_options.js]
 [browser_projecteditor_delete_file.js]
 [browser_projecteditor_editing_01.js]
 [browser_projecteditor_immediate_destroy.js]
 [browser_projecteditor_init.js]
 [browser_projecteditor_new_file.js]
 [browser_projecteditor_stores.js]
 [browser_projecteditor_tree_selection.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_app_options.js
@@ -0,0 +1,102 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that options can be changed without resetting the whole
+// editor.
+let test = asyncTest(function*() {
+
+  let TEMP_PATH = buildTempDirectoryStructure();
+  let projecteditor = yield addProjectEditorTab();
+
+  let resourceBeenAdded = promise.defer();
+  projecteditor.project.once("resource-added", () => {
+    info ("A resource has been added");
+    resourceBeenAdded.resolve();
+  });
+
+  info ("About to set project to: " + TEMP_PATH);
+  yield projecteditor.setProjectToAppPath(TEMP_PATH, {
+    name: "Test",
+    iconUrl: "chrome://browser/skin/devtools/tool-options.svg",
+    projectOverviewURL: SAMPLE_WEBAPP_URL
+  });
+
+  info ("Making sure a resource has been added before continuing");
+  yield resourceBeenAdded.promise;
+
+  info ("From now on, if a resource is added it should fail");
+  projecteditor.project.on("resource-added", failIfResourceAdded);
+
+  info ("Getting ahold and validating the project header DOM");
+  let header = projecteditor.document.querySelector(".entry-group-title");
+  let image = header.querySelector(".project-image");
+  let nameLabel = header.querySelector(".project-name-label");
+  let statusElement = header.querySelector(".project-status");
+  is (statusElement.getAttribute("status"), "unknown", "The status starts out as unknown.");
+  is (nameLabel.textContent, "Test", "The name label has been set correctly");
+  is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-options.svg", "The icon has been set correctly");
+
+  info ("About to set project with new options.");
+  yield projecteditor.setProjectToAppPath(TEMP_PATH, {
+    name: "Test2",
+    iconUrl: "chrome://browser/skin/devtools/tool-inspector.svg",
+    projectOverviewURL: SAMPLE_WEBAPP_URL,
+    validationStatus: "error"
+  });
+
+  ok (!nameLabel.parentNode, "The old elements have been removed");
+
+  info ("Getting ahold of and validating the project header DOM");
+  let image = header.querySelector(".project-image");
+  let nameLabel = header.querySelector(".project-name-label");
+  let statusElement = header.querySelector(".project-status");
+  is (statusElement.getAttribute("status"), "error", "The status has been set correctly.");
+  is (nameLabel.textContent, "Test2", "The name label has been set correctly");
+  is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-inspector.svg", "The icon has been set correctly");
+
+  info ("About to set project with new options.");
+  yield projecteditor.setProjectToAppPath(TEMP_PATH, {
+    name: "Test3",
+    iconUrl: "chrome://browser/skin/devtools/tool-webconsole.svg",
+    projectOverviewURL: SAMPLE_WEBAPP_URL,
+    validationStatus: "warning"
+  });
+
+  ok (!nameLabel.parentNode, "The old elements have been removed");
+
+  info ("Getting ahold of and validating the project header DOM");
+  let image = header.querySelector(".project-image");
+  let nameLabel = header.querySelector(".project-name-label");
+  let statusElement = header.querySelector(".project-status");
+  is (statusElement.getAttribute("status"), "warning", "The status has been set correctly.");
+  is (nameLabel.textContent, "Test3", "The name label has been set correctly");
+  is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-webconsole.svg", "The icon has been set correctly");
+
+  info ("About to set project with new options.");
+  yield projecteditor.setProjectToAppPath(TEMP_PATH, {
+    name: "Test4",
+    iconUrl: "chrome://browser/skin/devtools/tool-debugger.svg",
+    projectOverviewURL: SAMPLE_WEBAPP_URL,
+    validationStatus: "valid"
+  });
+
+  ok (!nameLabel.parentNode, "The old elements have been removed");
+
+  info ("Getting ahold of and validating the project header DOM");
+  let image = header.querySelector(".project-image");
+  let nameLabel = header.querySelector(".project-name-label");
+  let statusElement = header.querySelector(".project-status");
+  is (statusElement.getAttribute("status"), "valid", "The status has been set correctly.");
+  is (nameLabel.textContent, "Test4", "The name label has been set correctly");
+  is (image.getAttribute("src"), "chrome://browser/skin/devtools/tool-debugger.svg", "The icon has been set correctly");
+
+  info ("Test finished, cleaning up");
+  projecteditor.project.off("resource-added", failIfResourceAdded);
+});
+
+function failIfResourceAdded() {
+  ok (false, "A resource has been added, but it shouldn't have been");
+}
--- a/browser/devtools/projecteditor/test/browser_projecteditor_editing_01.js
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_editing_01.js
@@ -2,17 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test ProjectEditor basic functionality
 let test = asyncTest(function*() {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
-  let TEMP_PATH = [...projecteditor.project.allPaths()][0];
+  let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
 
   ok (projecteditor.currentEditor, "There is an editor for projecteditor");
   let resources = projecteditor.project.allResources();
 
   resources.forEach((r, i) => {
     console.log("Resource detected", r.path, i);
--- a/browser/devtools/projecteditor/test/browser_projecteditor_stores.js
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_stores.js
@@ -2,15 +2,15 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test ProjectEditor basic functionality
 let test = asyncTest(function*() {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
-  let TEMP_PATH = [...projecteditor.project.allPaths()][0];
+  let TEMP_PATH = projecteditor.project.allPaths()[0];
   is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
 
-  is ([...projecteditor.project.allPaths()].length, 1, "1 path is set");
+  is (projecteditor.project.allPaths().length, 1, "1 path is set");
   projecteditor.project.removeAllStores();
-  is ([...projecteditor.project.allPaths()].length, 0, "No paths are remaining");
+  is (projecteditor.project.allPaths().length, 0, "No paths are remaining");
 });
--- a/browser/devtools/projecteditor/test/browser_projecteditor_tree_selection.js
+++ b/browser/devtools/projecteditor/test/browser_projecteditor_tree_selection.js
@@ -3,17 +3,17 @@
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test tree selection functionality
 
 let test = asyncTest(function*() {
   let projecteditor = yield addProjectEditorTabForTempDirectory();
-  let TEMP_PATH = [...projecteditor.project.allPaths()][0];
+  let TEMP_PATH = projecteditor.project.allPaths()[0];
 
   is (getTempFile("").path, TEMP_PATH, "Temp path is set correctly.");
 
   ok (projecteditor.currentEditor, "There is an editor for projecteditor");
   let resources = projecteditor.project.allResources();
 
   is (
     resources.map(r=>r.basename).join("|"),
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -449,36 +449,33 @@ StyleEditorUI.prototype = {
 
         summary.addEventListener("focus", function onSummaryFocus(event) {
           if (event.target == summary) {
             // autofocus the stylesheet name
             summary.querySelector(".stylesheet-name").focus();
           }
         }, false);
 
-        Task.spawn(function* () {
-          // autofocus if it's a new user-created stylesheet
-          if (editor.isNew) {
-            yield this._selectEditor(editor);
-          }
+        // autofocus if it's a new user-created stylesheet
+        if (editor.isNew) {
+          this._selectEditor(editor);
+        }
 
-          if (this._styleSheetToSelect
-              && this._styleSheetToSelect.stylesheet == editor.styleSheet.href) {
-            yield this.switchToSelectedSheet();
-          }
+        if (this._styleSheetToSelect
+            && this._styleSheetToSelect.stylesheet == editor.styleSheet.href) {
+          this.switchToSelectedSheet();
+        }
 
-          // If this is the first stylesheet and there is no pending request to
-          // select a particular style sheet, select this sheet.
-          if (!this.selectedEditor && !this._styleSheetBoundToSelect
-              && editor.styleSheet.styleSheetIndex == 0) {
-            yield this._selectEditor(editor);
-          }
-
-          this.emit("editor-added", editor);
-        }.bind(this)).then(null, Cu.reportError);
+        // If this is the first stylesheet and there is no pending request to
+        // select a particular style sheet, select this sheet.
+        if (!this.selectedEditor && !this._styleSheetBoundToSelect
+            && editor.styleSheet.styleSheetIndex == 0) {
+          this._selectEditor(editor);
+        }
+        this.emit("editor-added", editor);
       }.bind(this),
 
       onShow: function(summary, details, data) {
         let editor = data.editor;
         this.selectedEditor = editor;
 
         Task.spawn(function* () {
           if (!editor.sourceEditor) {
@@ -707,60 +704,84 @@ StyleEditorUI.prototype = {
    * Update the @media rules sidebar for an editor. Hide if there are no rules
    * Display a list of the @media rules in the editor's associated style sheet.
    * Emits a 'media-list-changed' event after updating the UI.
    *
    * @param  {StyleSheetEditor} editor
    *         Editor to update @media sidebar of
    */
   _updateMediaList: function(editor) {
-    this.getEditorDetails(editor).then((details) => {
+    Task.spawn(function* () {
+      let details = yield this.getEditorDetails(editor);
       let list = details.querySelector(".stylesheet-media-list");
 
       while (list.firstChild) {
         list.removeChild(list.firstChild);
       }
 
       let rules = editor.mediaRules;
       let showSidebar = Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR);
       let sidebar = details.querySelector(".stylesheet-sidebar");
-      sidebar.hidden = !showSidebar || !rules.length;
+
+      let inSource = false;
 
       for (let rule of rules) {
+        let {line, column, parentStyleSheet} = rule;
+
+        let location = {
+          line: line,
+          column: column,
+          source: editor.styleSheet.href,
+          styleSheet: parentStyleSheet
+        };
+        if (editor.styleSheet.isOriginalSource) {
+          location = yield editor.cssSheet.getOriginalLocation(line, column);
+        }
+
+        // this @media rule is from a different original source
+        if (location.source != editor.styleSheet.href) {
+          continue;
+        }
+        inSource = true;
+
         let div = this._panelDoc.createElement("div");
         div.className = "media-rule-label";
-        div.addEventListener("click", this._jumpToMediaRule.bind(this, rule));
+        div.addEventListener("click", this._jumpToLocation.bind(this, location));
 
         let cond = this._panelDoc.createElement("div");
         cond.textContent = rule.conditionText;
         cond.className = "media-rule-condition"
         if (!rule.matches) {
           cond.classList.add("media-condition-unmatched");
         }
         div.appendChild(cond);
 
-        let line = this._panelDoc.createElement("div");
-        line.className = "media-rule-line theme-link";
-        line.textContent = ":" + rule.line;
-        div.appendChild(line);
+        let link = this._panelDoc.createElement("div");
+        link.className = "media-rule-line theme-link";
+        link.textContent = ":" + location.line;
+        div.appendChild(link);
 
         list.appendChild(div);
       }
+
+      sidebar.hidden = !showSidebar || !inSource;
+
       this.emit("media-list-changed", editor);
-    });
+    }.bind(this)).then(null, Cu.reportError);
   },
 
   /**
    * Jump cursor to the editor for a stylesheet and line number for a rule.
    *
-   * @param  {MediaRuleFront} rule
-   *         Rule to jump to.
+   * @param  {object} location
+   *         Location object with 'line', 'column', and 'source' properties.
    */
-  _jumpToMediaRule: function(rule) {
-    this.selectStyleSheet(rule.parentStyleSheet, rule.line - 1, rule.column - 1);
+  _jumpToLocation: function(location) {
+    let source = location.styleSheet || location.source;
+    this.selectStyleSheet(source, location.line - 1, location.column - 1);
   },
 
   destroy: function() {
     this._clearStyleSheetEditors();
 
     this._optionsMenu.removeEventListener("popupshowing",
                                           this._updateOptionsMenu);
 
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -91,31 +91,24 @@ function StyleSheetEditor(styleSheet, wi
 
   this._onPropertyChange = this._onPropertyChange.bind(this);
   this._onError = this._onError.bind(this);
   this._onMediaRuleMatchesChange = this._onMediaRuleMatchesChange.bind(this);
   this._onMediaRulesChanged = this._onMediaRulesChanged.bind(this)
   this.checkLinkedFileForChanges = this.checkLinkedFileForChanges.bind(this);
   this.markLinkedFileBroken = this.markLinkedFileBroken.bind(this);
 
+  this._focusOnSourceEditorReady = false;
+  this.cssSheet.on("property-change", this._onPropertyChange);
+  this.styleSheet.on("error", this._onError);
   this.mediaRules = [];
-  if (this.styleSheet.getMediaRules) {
-    this.styleSheet.getMediaRules().then(this._onMediaRulesChanged);
+  if (this.cssSheet.getMediaRules) {
+    this.cssSheet.getMediaRules().then(this._onMediaRulesChanged);
   }
-  this.styleSheet.on("media-rules-changed", this._onMediaRulesChanged);
-
-  this._focusOnSourceEditorReady = false;
-
-  let relatedSheet = this.styleSheet.relatedStyleSheet;
-  if (relatedSheet) {
-    relatedSheet.on("property-change", this._onPropertyChange);
-  }
-  this.styleSheet.on("property-change", this._onPropertyChange);
-  this.styleSheet.on("error", this._onError);
-
+  this.cssSheet.on("media-rules-changed", this._onMediaRulesChanged);
   this.savedFile = file;
   this.linkCSSFile();
 }
 
 StyleSheetEditor.prototype = {
   /**
    * Whether there are unsaved changes in the editor
    */
@@ -126,16 +119,27 @@ StyleSheetEditor.prototype = {
   /**
    * Whether the editor is for a stylesheet created by the user
    * through the style editor UI.
    */
   get isNew() {
     return this._isNew;
   },
 
+  /**
+   * The style sheet or the generated style sheet for this source if it's an
+   * original source.
+   */
+  get cssSheet() {
+    if (this.styleSheet.isOriginalSource) {
+      return this.styleSheet.relatedStyleSheet;
+    }
+    return this.styleSheet;
+  },
+
   get savedFile() {
     return this._savedFile;
   },
 
   set savedFile(name) {
     this._savedFile = name;
 
     this.linkCSSFile();
@@ -525,17 +529,19 @@ StyleSheetEditor.prototype = {
 
   /**
    * Called when this source has been successfully saved to disk.
    */
   onFileSaved: function(returnFile) {
     this._friendlyName = null;
     this.savedFile = returnFile;
 
-    this.sourceEditor.setClean();
+    if (this.sourceEditor) {
+      this.sourceEditor.setClean();
+    }
 
     this.emit("property-change");
 
     // TODO: replace with file watching
     this._modCheckCount = 0;
     this._window.clearTimeout(this._timeout);
 
     if (this.linkedCSSFile && !this.linkedCSSFileError) {
@@ -623,18 +629,18 @@ StyleSheetEditor.prototype = {
 
   /**
    * Clean up for this editor.
    */
   destroy: function() {
     if (this.sourceEditor) {
       this.sourceEditor.destroy();
     }
-    this.styleSheet.off("media-rules-changed", this._onMediaRulesChanged);
-    this.styleSheet.off("property-change", this._onPropertyChange);
+    this.cssSheet.off("property-change", this._onPropertyChange);
+    this.cssSheet.off("media-rules-changed", this._onMediaRulesChanged);
     this.styleSheet.off("error", this._onError);
   }
 }
 
 
 const TAB_CHARS = "\t";
 
 const CURRENT_OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -11,31 +11,35 @@ support-files =
   import2.css
   inline-1.html
   inline-2.html
   longload.html
   media-small.css
   media.html
   media-rules.html
   media-rules.css
+  media-rules-sourcemaps.html
   minified.html
   nostyle.html
   pretty.css
   resources_inpage.jsi
   resources_inpage1.css
   resources_inpage2.css
   simple.css
   simple.css.gz
   simple.css.gz^headers^
   simple.gz.html
   simple.html
   sourcemap-css/contained.css
   sourcemap-css/sourcemaps.css
   sourcemap-css/sourcemaps.css.map
+  sourcemap-css/media-rules.css
+  sourcemap-css/media-rules.css.map
   sourcemap-sass/sourcemaps.scss
+  sourcemap-sass/media-rules.scss
   sourcemaps.html
   test_private.css
   test_private.html
 
 [browser_styleeditor_autocomplete.js]
 [browser_styleeditor_bug_740541_iframes.js]
 skip-if = os == "linux" || "mac" # bug 949355
 [browser_styleeditor_bug_851132_middle_click.js]
@@ -44,16 +48,17 @@ skip-if = os == "linux" || "mac" # bug 9
 [browser_styleeditor_enabled.js]
 [browser_styleeditor_filesave.js]
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
 [browser_styleeditor_init.js]
 [browser_styleeditor_inline_friendly_names.js]
 [browser_styleeditor_loading.js]
 [browser_styleeditor_media_sidebar.js]
+[browser_styleeditor_media_sidebar_sourcemaps.js]
 [browser_styleeditor_new.js]
 [browser_styleeditor_nostyle.js]
 [browser_styleeditor_pretty.js]
 [browser_styleeditor_private_perwindowpb.js]
 [browser_styleeditor_reload.js]
 [browser_styleeditor_sv_keynav.js]
 [browser_styleeditor_sv_resize.js]
 [browser_styleeditor_selectstylesheet.js]
--- a/browser/devtools/styleeditor/test/browser_styleeditor_init.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js
@@ -15,17 +15,17 @@ function test()
   content.location = TESTCASE_URI;
 }
 
 let gEditorAddedCount = 0;
 function testEditorAdded(aEditor)
 {
   if (aEditor.styleSheet.styleSheetIndex == 0) {
     gEditorAddedCount++;
-    testFirstStyleSheetEditor(aEditor);
+    gUI.editors[0].getSourceEditor().then(testFirstStyleSheetEditor);
   }
   if (aEditor.styleSheet.styleSheetIndex == 1) {
     gEditorAddedCount++;
     testSecondStyleSheetEditor(aEditor);
   }
 
   if (gEditorAddedCount == 2) {
     gUI = null;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_media_sidebar_sourcemaps.js
@@ -0,0 +1,72 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// https rather than chrome to improve coverage
+const TESTCASE_URI = TEST_BASE_HTTPS + "media-rules-sourcemaps.html";
+const MEDIA_PREF = "devtools.styleeditor.showMediaSidebar";
+const MAP_PREF = "devtools.styleeditor.source-maps-enabled";
+
+const LABELS = ["screen and (max-width: 320px)",
+                "screen and (min-width: 1200px)"];
+const LINE_NOS = [4, 4];
+
+waitForExplicitFinish();
+
+let test = asyncTest(function*() {
+  Services.prefs.setBoolPref(MEDIA_PREF, true);
+  Services.prefs.setBoolPref(MAP_PREF, true);
+
+  let {UI} = yield addTabAndOpenStyleEditors(2, null, TESTCASE_URI);
+
+  yield listenForMediaChange(UI);
+
+  is(UI.editors.length, 1, "correct number of editors");
+
+  // Test editor with @media rules
+  let mediaEditor = UI.editors[0];
+  yield openEditor(mediaEditor);
+  testMediaEditor(mediaEditor);
+
+  Services.prefs.clearUserPref(MEDIA_PREF);
+  Services.prefs.clearUserPref(MAP_PREF);
+});
+
+function testMediaEditor(editor) {
+  let sidebar = editor.details.querySelector(".stylesheet-sidebar");
+  is(sidebar.hidden, false, "sidebar is showing on editor with @media");
+
+  let entries = [...sidebar.querySelectorAll(".media-rule-label")];
+  is(entries.length, 2, "two @media rules displayed in sidebar");
+
+  testRule(entries[0], LABELS[0], LINE_NOS[0]);
+  testRule(entries[1], LABELS[1], LINE_NOS[1]);
+}
+
+function testRule(rule, text, lineno) {
+  let cond = rule.querySelector(".media-rule-condition");
+  is(cond.textContent, text, "media label is correct for " + text);
+
+  let line = rule.querySelector(".media-rule-line");
+  is(line.textContent, ":" + lineno, "correct line number shown");
+}
+
+/* Helpers */
+
+function openEditor(editor) {
+  getLinkFor(editor).click();
+
+  return editor.getSourceEditor();
+}
+
+function listenForMediaChange(UI) {
+  let deferred = promise.defer();
+  UI.once("media-list-changed", () => {
+    deferred.resolve();
+  })
+  return deferred.promise;
+}
+
+function getLinkFor(editor) {
+  return editor.summary.querySelector(".stylesheet-name");
+}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_reload.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_reload.js
@@ -22,31 +22,35 @@ function test()
   });
 
   content.location = TESTCASE_URI;
 }
 
 function runTests()
 {
   let count = 0;
-  gUI.once("editor-selected", (event, editor) => {
+  gUI.on("editor-selected", function editorSelected(event, editor) {
+    if (editor.styleSheet != gUI.editors[1].styleSheet) {
+      return;
+    }
+    gUI.off("editor-selected", editorSelected);
     editor.getSourceEditor().then(() => {
       info("selected second editor, about to reload page");
       reloadPage();
 
       gUI.on("editor-added", function editorAdded(event, editor) {
         if (++count == 2) {
           info("all editors added after reload");
           gUI.off("editor-added", editorAdded);
           gUI.editors[1].getSourceEditor().then(testRemembered);
         }
       })
     });
   });
-  gUI.selectStyleSheet(gUI.editors[1].styleSheet.href, LINE_NO, COL_NO);
+  gUI.selectStyleSheet(gUI.editors[1].styleSheet, LINE_NO, COL_NO);
 }
 
 function testRemembered()
 {
   is(gUI.selectedEditor, gUI.editors[1], "second editor is selected");
 
   let {line, ch} = gUI.selectedEditor.sourceEditor.getCursor();
   is(line, LINE_NO, "correct line selected");
--- a/browser/devtools/styleeditor/test/browser_styleeditor_selectstylesheet.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_selectstylesheet.js
@@ -25,17 +25,22 @@ function test()
 }
 
 function runTests()
 {
   let count = 0;
 
   // Make sure Editor doesn't go into an infinite loop when
   // column isn't passed. See bug 941018.
-  gUI.once("editor-selected", (event, editor) => {
+  gUI.on("editor-selected", function editorSelected(event, editor) {
+    if (editor.styleSheet != gUI.editors[1].styleSheet) {
+      return;
+    }
+    gUI.off("editor-selected", editorSelected);
+
     editor.getSourceEditor().then(() => {
       is(gUI.selectedEditor, gUI.editors[1], "second editor is selected");
       let {line, ch} = gUI.selectedEditor.sourceEditor.getCursor();
 
       is(line, LINE_NO, "correct line selected");
       is(ch, COL_NO, "correct column selected");
 
       gUI = null;
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sourcemaps.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sourcemaps.js
@@ -99,19 +99,23 @@ function togglePref(UI) {
   let deferred = promise.defer();
   let count = 0;
 
   UI.on("editor-added", (event, editor) => {
     if (++count == 3) {
       deferred.resolve();
     }
   })
+  let editorsPromise = deferred.promise;
+
+  let selectedPromise = UI.once("editor-selected");
 
   Services.prefs.setBoolPref(PREF, false);
-  return deferred.promise;
+
+  return promise.all([editorsPromise, selectedPromise]);
 }
 
 function openEditor(editor) {
   getLinkFor(editor).click();
 
   return editor.getSourceEditor();
 }
 
--- a/browser/devtools/styleeditor/test/head.js
+++ b/browser/devtools/styleeditor/test/head.js
@@ -40,19 +40,20 @@ function cleanup()
     gBrowser.removeCurrentTab();
   }
 }
 
 function addTabAndOpenStyleEditors(count, callback, uri) {
   let deferred = promise.defer();
   let currentCount = 0;
   let panel;
-  addTabAndCheckOnStyleEditorAdded(p => panel = p, function () {
+  addTabAndCheckOnStyleEditorAdded(p => panel = p, function (editor) {
     currentCount++;
-    info(currentCount + " of " + count + " editors opened");
+    info(currentCount + " of " + count + " editors opened: "
+         + editor.styleSheet.href);
     if (currentCount == count) {
       if (callback) {
         callback(panel);
       }
       deferred.resolve(panel);
     }
   });
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/media-rules-sourcemaps.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <link rel="stylesheet" type="text/css" href="sourcemap-css/media-rules.css"
+</head>
+<body>
+  <div>
+    Testing style editor media sidebar with source maps
+  </div>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/sourcemap-css/media-rules.css
@@ -0,0 +1,8 @@
+@media screen and (max-width: 320px) {
+  div {
+    width: 100px; } }
+@media screen and (min-width: 1200px) {
+  div {
+    width: 400px; } }
+
+/*# sourceMappingURL=media-rules.css.map */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/sourcemap-css/media-rules.css.map
@@ -0,0 +1,6 @@
+{
+"version": 3,
+"mappings": "AAIE,oCAA4C;EAD9C,GAAI;IAEA,KAAK,EAAE,KAAK;AAEd,qCAA4C;EAJ9C,GAAI;IAKA,KAAK,EAAE,KAAK",
+"sources": ["../sourcemap-sass/media-rules.scss"],
+"file": "media-rules.css"
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/sourcemap-sass/media-rules.scss
@@ -0,0 +1,11 @@
+$break-small: 320px;
+$break-large: 1200px;
+
+div {
+  @media screen and (max-width: $break-small) {
+    width: 100px;
+  }
+  @media screen and (min-width: $break-large) {
+    width: 400px;
+  }
+}
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -8,16 +8,17 @@
 const {Cc, Ci, Cu} = require("chrome");
 
 loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
 loader.lazyImporter(this, "escapeHTML", "resource:///modules/devtools/VariablesView.jsm");
 loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
 loader.lazyImporter(this, "Task","resource://gre/modules/Task.jsm");
 
 const Heritage = require("sdk/core/heritage");
+const URI = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
 
 const WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils;
 const l10n = new WebConsoleUtils.l10n(STRINGS_URI);
 
 // Constants for compatibility with the Web Console output implementation before
@@ -1076,16 +1077,21 @@ Messages.Extended.prototype = Heritage.e
       if (grip.type == "longString") {
         let widget = new Widgets.LongString(this, grip, options).render();
         return widget.element;
       }
     }
 
     let result = this.document.createElementNS(XHTML_NS, "span");
     if (isPrimitive) {
+      if (Widgets.URLString.prototype.containsURL.call(Widgets.URLString.prototype, grip)) {
+        let widget = new Widgets.URLString(this, grip, options).render();
+        return widget.element;
+      }
+
       let className = this.getClassNameForValueGrip(grip);
       if (className) {
         result.className = className;
       }
 
       result.textContent = VariablesView.getString(grip, {
         noStringQuotes: noStringQuotes,
         concise: options.concise,
@@ -1753,16 +1759,135 @@ Widgets.MessageTimestamp.prototype = Her
     this.element.textContent = l10n.timestampString(this.timestamp) + " ";
 
     return this;
   },
 }); // Widgets.MessageTimestamp.prototype
 
 
 /**
+ * The URLString widget, for rendering strings where at least one token is a
+ * URL.
+ *
+ * @constructor
+ * @param object message
+ *        The owning message.
+ * @param string str
+ *        The string, which contains at least one valid URL.
+ */
+Widgets.URLString = function(message, str)
+{
+  Widgets.BaseWidget.call(this, message);
+  this.str = str;
+};
+
+Widgets.URLString.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
+{
+  /**
+   * The string to format, which contains at least one valid URL.
+   * @type string
+   */
+  str: "",
+
+  render: function()
+  {
+    if (this.element) {
+      return this;
+    }
+
+    // The rendered URLString will be a <span> containing a number of text
+    // <spans> for non-URL tokens and <a>'s for URL tokens.
+    this.element = this.el("span", {
+      class: "console-string"
+    });
+    this.element.appendChild(this._renderText("\""));
+
+    // As we walk through the tokens of the source string, we make sure to preserve
+    // the original whitespace that seperated the tokens.
+    let tokens = this.str.split(/\s+/);
+    let textStart = 0;
+    let tokenStart;
+    for (let token of tokens) {
+      tokenStart = this.str.indexOf(token, textStart);
+      if (this._isURL(token)) {
+        this.element.appendChild(this._renderText(this.str.slice(textStart, tokenStart)));
+        textStart = tokenStart + token.length;
+        this.element.appendChild(this._renderURL(token));
+      }
+    }
+
+    // Clean up any non-URL text at the end of the source string.
+    this.element.appendChild(this._renderText(this.str.slice(textStart, this.str.length)));
+    this.element.appendChild(this._renderText("\""));
+
+    return this;
+  },
+
+  /**
+   * Determines whether a grip is a string containing a URL.
+   *
+   * @param string grip
+   *        The grip, which may contain a URL.
+   * @return boolean
+   *         Whether the grip is a string containing a URL.
+   */
+  containsURL: function(grip)
+  {
+    if (typeof grip != "string") {
+      return false;
+    }
+
+    let tokens = grip.split(/\s+/);
+    return tokens.some(this._isURL);
+  },
+
+  /**
+   * Determines whether a string token is a valid URL.
+   *
+   * @param string token
+   *        The token.
+   * @return boolean
+   *         Whenther the token is a URL.
+   */
+  _isURL: function(token) {
+    try {
+      let uri = URI.newURI(token, null, null);
+      let url = uri.QueryInterface(Ci.nsIURL);
+      return true;
+    } catch (e) {
+      return false;
+    }
+  },
+
+  /**
+   * Renders a string as a URL.
+   *
+   * @param string url
+   *        The string to be rendered as a url.
+   * @return DOMElement
+   *         An element containing the rendered string.
+   */
+  _renderURL: function(url)
+  {
+    let result = this.el("a", {
+      class: "url",
+      title: url,
+      href: url,
+      draggable: false
+    }, url);
+    this.message._addLinkCallback(result);
+    return result;
+  },
+
+  _renderText: function(text) {
+    return this.el("span", text);
+  },
+}); // Widgets.URLString.prototype
+
+/**
  * Widget used for displaying ObjectActors that have no specialised renderers.
  *
  * @constructor
  * @param object message
  *        The owning message.
  * @param object objectActor
  *        The ObjectActor to display.
  * @param object [options]
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -240,16 +240,17 @@ run-if = os == "mac"
 [browser_webconsole_bug_837351_securityerrors.js]
 [browser_webconsole_bug_846918_hsts_invalid-headers.js]
 [browser_webconsole_bug_915141_toggle_response_logging_with_keyboard.js]
 [browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
 [browser_webconsole_bug_1010953_cspro.js]
 [browser_webconsole_cached_autocomplete.js]
 [browser_webconsole_change_font_size.js]
 [browser_webconsole_chrome.js]
+[browser_webconsole_clickable_urls.js]
 [browser_webconsole_closure_inspection.js]
 [browser_webconsole_completion.js]
 [browser_webconsole_console_extras.js]
 [browser_webconsole_console_logging_api.js]
 [browser_webconsole_count.js]
 [browser_webconsole_dont_navigate_on_doubleclick.js]
 [browser_webconsole_execution_scope.js]
 [browser_webconsole_for_of.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_clickable_urls.js
@@ -0,0 +1,83 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// When strings containing URLs are entered into the webconsole,
+// check its output and ensure that the output can be clicked to open those URLs.
+
+const TEST_URI = "data:text/html;charset=utf8,Bug 1005909 - Clickable URLS";
+
+let inputTests = [
+
+  // 0: URL opens page when clicked.
+  {
+    input: "'http://example.com'",
+    output: "http://example.com",
+    expectedTab: "http://example.com/",
+  },
+
+  // 1: URL opens page using https when clicked.
+  {
+    input: "'https://example.com'",
+    output: "https://example.com",
+    expectedTab: "https://example.com/",
+  },
+
+  // 2: URL with port opens page when clicked.
+  {
+    input: "'https://example.com:443'",
+    output: "https://example.com:443",
+    expectedTab: "https://example.com/",
+  },
+
+  // 3: URL containing non-empty path opens page when clicked.
+  {
+    input: "'http://example.com/foo'",
+    output: "http://example.com/foo",
+    expectedTab: "http://example.com/foo",
+  },
+
+  // 4: URL opens page when clicked, even when surrounded by non-URL tokens.
+  {
+  	input: "'foo http://example.com bar'",
+  	output: "foo http://example.com bar",
+  	expectedTab: "http://example.com/",
+  },
+
+  // 5: URL opens page when clicked, and whitespace is be preserved.
+  {
+  	input: "'foo\\nhttp://example.com\\nbar'",
+  	output: "foo\nhttp://example.com\nbar",
+  	expectedTab: "http://example.com/",
+  },
+
+  // 6: URL opens page when clicked when multiple links are present.
+  {
+  	input: "'http://example.com http://example.com'",
+  	output: "http://example.com http://example.com",
+  	expectedTab: "http://example.com/",
+  },
+
+  // 7: URL without scheme does not open page when clicked.
+  {
+    input: "'example.com'",
+    output: "example.com",
+  },
+
+  // 8: URL with invalid scheme does not open page when clicked.
+  {
+    input: "'foo://example.com'",
+    output: "foo://example.com",
+  },
+
+];
+
+function test() {
+  Task.spawn(function*() {
+    let {tab} = yield loadTab(TEST_URI);
+    let hud = yield openConsole(tab);
+    yield checkOutputForInputs(hud, inputTests);
+    inputTests = null;
+  }).then(finishTest);
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_output_03.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_03.js
@@ -3,16 +3,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Test the webconsole output for various types of objects.
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-03.html";
 
 let inputTests = [
+
   // 0
   {
     input: "document",
     output: "HTMLDocument \u2192 " + TEST_URI,
     printOutput: "[object HTMLDocument]",
     inspectable: true,
     noClick: true,
   },
@@ -52,16 +53,17 @@ let inputTests = [
     inspectable: true,
     variablesViewLabel: "DOMTokenList[0]",
   },
 
   // 5
   {
     input: "window.location.href",
     output: '"' + TEST_URI + '"',
+    noClick: true,
   },
 
   // 6
   {
     input: "window.location",
     output: "Location \u2192 " + TEST_URI,
     printOutput: TEST_URI,
     inspectable: true,
--- a/browser/devtools/webconsole/test/browser_webconsole_output_04.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_04.js
@@ -105,16 +105,15 @@ let inputTests = [
     output: 'CSSMediaRule "print"',
     printOutput: "[object CSSMediaRule",
     inspectable: true,
     variablesViewLabel: "CSSMediaRule",
   },
 ];
 
 function test() {
-  addTab(TEST_URI);
-  browser.addEventListener("load", function onLoad() {
-    browser.removeEventListener("load", onLoad, true);
-    openConsole().then((hud) => {
-      return checkOutputForInputs(hud, inputTests);
-    }).then(finishTest);
-  }, true);
+  Task.spawn(function*() {
+    const {tab} = yield loadTab(TEST_URI);
+    const hud = yield openConsole(tab);
+    yield checkOutputForInputs(hud, inputTests);
+    inputTests = null;
+  }).then(finishTest);
 }
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -79,16 +79,42 @@ function loadTab(url) {
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
     deferred.resolve({tab: tab, browser: browser});
   }, true);
 
   return deferred.promise;
 }
 
+function loadBrowser(browser) {
+  let deferred = promise.defer();
+
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    deferred.resolve(null)
+  }, true);
+
+  return deferred.promise;
+}
+
+function closeTab(tab) {
+  let deferred = promise.defer();
+
+  let container = gBrowser.tabContainer;
+
+  container.addEventListener("TabClose", function onTabClose() {
+    container.removeEventListener("TabClose", onTabClose, true);
+    deferred.resolve(null);
+  }, true);
+
+  gBrowser.removeTab(tab);
+
+  return deferred.promise;
+}
+
 function afterAllTabsLoaded(callback, win) {
   win = win || window;
 
   let stillToLoad = 0;
 
   function onLoad() {
     this.removeEventListener("load", onLoad, true);
     stillToLoad--;
@@ -1377,31 +1403,32 @@ function whenDelayedStartupFinished(aWin
  *
  *        - variablesViewLabel: string|RegExp, optional, the expected variables
  *        view label when the object is inspected. If this is not provided, then
  *        |output| is used.
  *
  *        - inspectorIcon: boolean, when true, the test runner expects the
  *        result widget to contain an inspectorIcon element (className
  *        open-inspector).
+ *
+ *        - expectedTab: string, optional, the full URL of the new tab which must
+ *        open. If this is not provided, any new tabs that open will cause a test
+ *        failure.
  */
 function checkOutputForInputs(hud, inputTests)
 {
-  let eventHandlers = new Set();
+  let container = gBrowser.tabContainer;
 
   function* runner()
   {
     for (let [i, entry] of inputTests.entries()) {
       info("checkInput(" + i + "): " + entry.input);
       yield checkInput(entry);
     }
-
-    for (let fn of eventHandlers) {
-      hud.jsterm.off("variablesview-open", fn);
-    }
+    container = null;
   }
 
   function* checkInput(entry)
   {
     yield checkConsoleLog(entry);
     yield checkPrintOutput(entry);
     yield checkJSEval(entry);
   }
@@ -1462,37 +1489,49 @@ function checkOutputForInputs(hud, input
     if (!entry.noClick) {
       yield checkObjectClick(entry, msg);
     }
     if (typeof entry.inspectorIcon == "boolean") {
       yield checkLinkToInspector(entry, msg);
     }
   }
 
-  function checkObjectClick(entry, msg)
+  function* checkObjectClick(entry, msg)
   {
     let body = msg.querySelector(".message-body a") ||
                msg.querySelector(".message-body");
     ok(body, "the message body");
 
-    let deferred = promise.defer();
+    let deferredVariablesView = promise.defer();
+    entry._onVariablesViewOpen = onVariablesViewOpen.bind(null, entry, deferredVariablesView);
+    hud.jsterm.on("variablesview-open", entry._onVariablesViewOpen);
 
-    entry._onVariablesViewOpen = onVariablesViewOpen.bind(null, entry, deferred);
-    hud.jsterm.on("variablesview-open", entry._onVariablesViewOpen);
-    eventHandlers.add(entry._onVariablesViewOpen);
+    let deferredTab = promise.defer();
+    entry._onTabOpen = onTabOpen.bind(null, entry, deferredTab);
+    container.addEventListener("TabOpen", entry._onTabOpen, true);
 
     body.scrollIntoView();
     EventUtils.synthesizeMouse(body, 2, 2, {}, hud.iframeWindow);
 
     if (entry.inspectable) {
       info("message body tagName '" + body.tagName +  "' className '" + body.className + "'");
-      return deferred.promise; // wait for the panel to open if we need to.
+      yield deferredVariablesView.promise;
+    } else {
+      hud.jsterm.off("variablesview-open", entry._onVariablesView);
+      entry._onVariablesView = null;
     }
 
-    return promise.resolve(null);
+    if (entry.expectedTab) {
+      yield deferredTab.promise;
+    } else {
+      container.removeEventListener("TabOpen", entry._onTabOpen, true);
+      entry._onTabOpen = null;
+    }
+
+    yield promise.resolve(null);
   }
 
   function checkLinkToInspector(entry, msg)
   {
     let elementNodeWidget = [...msg._messageObject.widgets][0];
     if (!elementNodeWidget) {
       ok(!entry.inspectorIcon, "The message has no ElementNode widget");
       return;
@@ -1508,29 +1547,42 @@ function checkOutputForInputs(hud, input
           "The ElementNode widget isn't linked to the inspector");
       }
     }, () => {
       // linkToInspector promise rejected, node not linked to inspector
       ok(!entry.inspectorIcon, "The ElementNode widget isn't linked to the inspector");
     });
   }
 
-  function onVariablesViewOpen(entry, deferred, event, view, options)
+  function onVariablesViewOpen(entry, {resolve, reject}, event, view, options)
   {
     let label = entry.variablesViewLabel || entry.output;
     if (typeof label == "string" && options.label != label) {
       return;
     }
     if (label instanceof RegExp && !label.test(options.label)) {
       return;
     }
 
     hud.jsterm.off("variablesview-open", entry._onVariablesViewOpen);
-    eventHandlers.delete(entry._onVariablesViewOpen);
     entry._onVariablesViewOpen = null;
-
     ok(entry.inspectable, "variables view was shown");
 
-    deferred.resolve(null);
+    resolve(null);
+  }
+
+  function onTabOpen(entry, {resolve, reject}, event)
+  {
+    container.removeEventListener("TabOpen", entry._onTabOpen, true);
+    entry._onTabOpen = null;
+
+    let tab = event.target;
+    let browser = gBrowser.getBrowserForTab(tab);
+    loadBrowser(browser).then(() => {
+      let uri = content.location.href;
+      ok(entry.expectedTab && entry.expectedTab == uri,
+        "opened tab '" + uri +  "', expected tab '" + entry.expectedTab + "'");
+      return closeTab(tab);
+    }).then(resolve, reject);
   }
 
   return Task.spawn(runner);
 }
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -152,22 +152,24 @@ let UI = {
     this.hidePanels();
     document.querySelector("window").classList.add("busy")
     this.updateCommands();
   },
 
   unbusy: function() {
     document.querySelector("window").classList.remove("busy")
     this.updateCommands();
+    this._busyPromise = null;
   },
 
   busyUntil: function(promise, operationDescription) {
     // Freeze the UI until the promise is resolved. A 30s timeout
     // will unfreeze the UI, just in case the promise never gets
     // resolved.
+    this._busyPromise = promise;
     let timeout = setTimeout(() => {
       this.unbusy();
       UI.reportError("error_operationTimeout", operationDescription);
     }, 30000);
     this.busy();
     promise.then(() => {
       clearTimeout(timeout);
       this.unbusy();
@@ -611,21 +613,20 @@ let Cmds = {
       let projects = AppProjects.store.object.projects;
       for (let i = 0; i < projects.length; i++) {
         let project = projects[i];
         let panelItemNode = document.createElement("toolbarbutton");
         panelItemNode.className = "panel-item";
         projectsNode.appendChild(panelItemNode);
         panelItemNode.setAttribute("label", project.name || AppManager.DEFAULT_PROJECT_NAME);
         panelItemNode.setAttribute("image", project.icon || AppManager.DEFAULT_PROJECT_ICON);
-        if (!project.validationStatus) {
-          // The result of the validation process (storing names, icons, …) has never been
-          // stored in the IndexedDB database. This happens when the project has been created
-          // from the old app manager. We need to run the validation again and update the name
-          // and icon of the app
+        if (!project.name || !project.icon) {
+          // The result of the validation process (storing names, icons, …) is not stored in
+          // the IndexedDB database when App Manager v1 is used.
+          // We need to run the validation again and update the name and icon of the app.
           AppManager.validateProject(project).then(() => {
             panelItemNode.setAttribute("label", project.name);
             panelItemNode.setAttribute("image", project.icon);
           });
         }
         panelItemNode.addEventListener("click", () => {
           UI.hidePanels();
           AppManager.selectedProject = project;
--- a/browser/devtools/webide/test/head.js
+++ b/browser/devtools/webide/test/head.js
@@ -57,27 +57,25 @@ function closeWebIDE(win) {
   });
 
   win.close();
 
   return deferred.promise;
 }
 
 function removeAllProjects() {
-  let deferred = promise.defer();
-  AppProjects.load().then(() => {
+  return Task.spawn(function* () {
+    yield AppProjects.load();
     let projects = AppProjects.store.object.projects;
     for (let i = 0; i < projects.length; i++) {
-      AppProjects.remove(projects[i].location);
+      yield AppProjects.remove(projects[i].location);
     }
-    deferred.resolve();
   });
+}
 
-  return deferred.promise;
-}
 function nextTick() {
   let deferred = promise.defer();
   SimpleTest.executeSoon(() => {
     deferred.resolve();
   });
 
   return deferred.promise;
 }
--- a/browser/devtools/webide/test/test_cli.html
+++ b/browser/devtools/webide/test/test_cli.html
@@ -14,25 +14,22 @@
 
   <body>
 
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
-          let clClass = Components.classes["@mozilla.org/toolkit/command-line;1"].createInstance();
-
           Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
           DebuggerServer.init(function () { return true; });
           DebuggerServer.addBrowserActors();
 
           let win = yield openWebIDE();
 
-
           let packagedAppLocation = getTestFilePath("app");
 
           let cli = "actions=addPackagedApp&location=" + packagedAppLocation;
           yield win.handleCommandline(cli);
 
           let project = win.AppManager.selectedProject;
           is(project.location, packagedAppLocation, "Project imported");
 
--- a/browser/devtools/webide/test/test_import.html
+++ b/browser/devtools/webide/test/test_import.html
@@ -17,16 +17,19 @@
     <script type="application/javascript;version=1.8">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Task.spawn(function* () {
             let win = yield openWebIDE();
             let packagedAppLocation = getTestFilePath("app");
 
+            yield win.AppProjects.load();
+            is(win.AppProjects.store.object.projects.length, 0, "IDB is empty");
+
             yield win.Cmds.importPackagedApp(packagedAppLocation);
 
             let project = win.AppManager.selectedProject;
             is(project.location, packagedAppLocation, "Location is valid");
             is(project.name, "A name (in app directory)", "name field has been updated");
             is(project.manifest.launch_path, "/index.html", "manifest found. launch_path valid.");
             is(project.manifest.description, "desc", "manifest found. description valid");
 
--- a/browser/devtools/webide/test/test_runtime.html
+++ b/browser/devtools/webide/test/test_runtime.html
@@ -44,71 +44,61 @@
 
             getName: function() {
               return "fakeRuntime";
             }
           });
 
           win.AppManager.update("runtimelist");
 
-          let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
-          yield win.Cmds.importHostedApp(hostedAppManifest);
+          let packagedAppLocation = getTestFilePath("app");
 
-          yield win.Cmds.showRuntimePanel();
+          yield win.Cmds.importPackagedApp(packagedAppLocation);
 
           let panelNode = win.document.querySelector("#runtime-panel");
           let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
           is(items.length, 1, "Found one runtime button");
 
           let deferred = promise.defer();
           win.AppManager.connection.once(
               win.Connection.Events.CONNECTED,
               () => deferred.resolve());
 
           items[0].click();
 
-          yield deferred.promise;
+          ok(win.document.querySelector("window").className, "busy", "UI is busy");
+          yield win.UI._busyPromise;
 
           is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
 
+          ok(isPlayActive(), "play button is enabled 1");
+          ok(!isStopActive(), "stop button is disabled 1");
+          let oldProject = win.AppManager.selectedProject;
+          win.AppManager.selectedProject = null;
+
           yield nextTick();
 
           ok(!isPlayActive(), "play button is disabled 2");
           ok(!isStopActive(), "stop button is disabled 2");
-
-          win.AppManager.selectedProject.errorsCount = 0;
+          win.AppManager._selectedProject = oldProject;
           win.UI.updateCommands();
 
           yield nextTick();
 
           ok(isPlayActive(), "play button is enabled 3");
           ok(!isStopActive(), "stop button is disabled 3");
-          let oldProject = win.AppManager.selectedProject;
-          win.AppManager.selectedProject = null;
-
-          yield nextTick();
-
-          ok(!isPlayActive(), "play button is disabled 4");
-          ok(!isStopActive(), "stop button is disabled 4");
-          win.AppManager._selectedProject = oldProject;
-          win.UI.updateCommands();
-
-          yield nextTick();
-
-          ok(isPlayActive(), "play button is enabled 5");
-          ok(!isStopActive(), "stop button is disabled 5");
 
 
           yield win.Cmds.disconnectRuntime();
 
           is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
 
           ok(win.AppManager.selectedProject, "A project is still selected");
-          ok(!isPlayActive(), "play button is disabled 6");
-          ok(!isStopActive(), "stop button is disabled 6");
+          ok(!isPlayActive(), "play button is disabled 4");
+          ok(!isStopActive(), "stop button is disabled 4");
 
           deferred = promise.defer();
           win.AppManager.connection.once(
               win.Connection.Events.CONNECTED,
               () => deferred.resolve());
 
           win.document.querySelectorAll(".runtime-panel-item-custom")[1].click();
 
@@ -117,16 +107,18 @@
           is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
 
           yield win.Cmds.disconnectRuntime();
 
           yield closeWebIDE(win);
 
           DebuggerServer.destroy();
 
+          yield removeAllProjects();
+
           SimpleTest.finish();
 
         });
       }
 
 
     </script>
   </body>
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -1,9 +1,9 @@
 # -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 pref("devtools.webide.showProjectEditor", true);
-pref("devtools.webide.templatesURL", "http://fixme/");
+pref("devtools.webide.templatesURL", "http://people.mozilla.org/~prouget/webidetemplates/template.json"); // See bug 1021504
 pref("devtools.webide.lastprojectlocation", "");
 pref("devtools.webide.enableLocalRuntime", false);
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -631,21 +631,19 @@
 @BINPATH@/chrome/recording/*
 #ifdef MOZ_GTK
 @BINPATH@/browser/chrome/icons/default/default16.png
 @BINPATH@/browser/chrome/icons/default/default32.png
 @BINPATH@/browser/chrome/icons/default/default48.png
 #endif
 
 ; [Webide Files]
-#ifdef MOZ_DEVTOOLS_WEBIDE
 @BINPATH@/browser/chrome/webide@JAREXT@
 @BINPATH@/browser/chrome/webide.manifest
 @BINPATH@/browser/@PREF_DIR@/webide-prefs.js
-#endif
 
 ; shell icons
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 ; shell icons
 @BINPATH@/browser/icons/*.png
 #ifdef MOZ_UPDATER
 ; updater icon
--- a/browser/locales/en-US/chrome/browser/aboutHome.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutHome.dtd
@@ -21,13 +21,18 @@
      text in <a/> will be linked to the featured add-ons on addons.mozilla.org
 -->
 <!ENTITY abouthome.defaultSnippet2.v1 "It's easy to customize your Firefox exactly the way you want it. <a>Choose from thousands of add-ons</a>.">
 <!-- LOCALIZATION NOTE (abouthome.rightsSnippet): text in <a/> will be linked to about:rights -->
 <!ENTITY abouthome.rightsSnippet "&brandFullName; is free and open source software from the non-profit Mozilla Foundation. <a>Know your rights…</a>">
 
 <!ENTITY abouthome.bookmarksButton.label "Bookmarks">
 <!ENTITY abouthome.historyButton.label   "History">
-<!ENTITY abouthome.settingsButton.label  "Settings">
+<!-- LOCALIZATION NOTE (abouthome.preferencesButtonWin.label): The label for the
+     preferences/options item on about:home on Windows -->
+<!ENTITY abouthome.preferencesButtonWin.label  "Options">
+<!-- LOCALIZATION NOTE (abouthome.preferencesButtonUnix.label): The label for the
+     preferences/options item on about:home on Linux and OS X -->
+<!ENTITY abouthome.preferencesButtonUnix.label  "Preferences">
 <!ENTITY abouthome.addonsButton.label    "Add-ons">
 <!ENTITY abouthome.appsButton.label      "Marketplace">
 <!ENTITY abouthome.downloadsButton.label "Downloads">
 <!ENTITY abouthome.syncButton.label      "&syncBrand.shortName.label;">
--- a/browser/locales/en-US/chrome/browser/translation.dtd
+++ b/browser/locales/en-US/chrome/browser/translation.dtd
@@ -39,16 +39,18 @@
 <!ENTITY translation.translatedToSuffix.label   "">
 
 <!ENTITY translation.showOriginal.button        "Show Original">
 <!ENTITY translation.showTranslation.button     "Show Translation">
 
 <!ENTITY translation.errorTranslating.label     "There has been an error translating this page.">
 <!ENTITY translation.tryAgain.button            "Try Again">
 
+<!ENTITY translation.serviceUnavailable.label   "Translation is not available at the moment. Please try again later.">
+
 <!ENTITY translation.options.menu               "Options">
 <!-- LOCALIZATION NOTE (translation.options.neverForSite.accesskey,
   -                     translation.options.preferences.accesskey):
   -  The accesskey values used here should not clash with the value used for
   -  translation.options.neverForLanguage.accesskey in translation.properties
   -->
 <!ENTITY translation.options.neverForSite.label "Never translate this site">
 <!ENTITY translation.options.neverForSite.accesskey "e">
--- a/browser/locales/filter.py
+++ b/browser/locales/filter.py
@@ -1,24 +1,24 @@
 # 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/.
 
 def test(mod, path, entity = None):
   import re
   # ignore anything but Firefox
   if mod not in ("netwerk", "dom", "toolkit", "security/manager",
-                 "browser", "browser/metro", "webapprt",
+                 "browser", "webapprt",
                  "extensions/reporter", "extensions/spellcheck",
                  "other-licenses/branding/firefox",
                  "browser/branding/official",
                  "services/sync"):
     return "ignore"
-  if mod not in ("browser", "browser/metro", "extensions/spellcheck"):
-    # we only have exceptions for browser, metro and extensions/spellcheck
+  if mod not in ("browser", "extensions/spellcheck"):
+    # we only have exceptions for browser and extensions/spellcheck
     return "error"
   if not entity:
     # the only files to ignore are spell checkers and search
     if mod == "extensions/spellcheck":
       return "ignore"
     # browser
     return "ignore" if re.match(r"searchplugins\/.+\.xml", path) else "error"
   if mod == "extensions/spellcheck":
@@ -30,13 +30,9 @@ def test(mod, path, entity = None):
   if mod == "browser" and path == "chrome/browser-region/region.properties":
     # only region.properties exceptions remain, compare all others
     return ("ignore"
             if (re.match(r"browser\.search\.order\.[1-9]", entity) or
                 re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or
                 re.match(r"gecko\.handlerService\.schemes\.", entity) or
                 re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity))
             else "error")
-  if mod == "browser/metro" and path == "chrome/region.properties":
-      return ("ignore"
-              if re.match(r"browser\.search\.order\.[1-9]", entity)
-              else "error")
   return "error"
--- a/browser/locales/l10n.ini
+++ b/browser/locales/l10n.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [general]
 depth = ../..
 all = browser/locales/all-locales
 
 [compare]
 dirs = browser
-     browser/metro
      extensions/reporter
      other-licenses/branding/firefox
      browser/branding/official
 
 [includes]
 # non-central apps might want to use %(topsrcdir)s here, or other vars
 # RFE: that needs to be supported by compare-locales, too, though
 toolkit = toolkit/locales/l10n.ini
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -104,22 +104,16 @@ toolbarseparator {
   min-height: 22px;
 }
 
 #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
   -moz-appearance: none;
   background: url(chrome://browser/skin/Toolbar-background-noise.png) hsl(0,0%,83%);
 }
 
-#TabsToolbar:not([collapsed="true"]) + #nav-bar {
-  /* Position the toolbar above the bottom of background tabs */
-  position: relative;
-  z-index: 1;
-}
-
 #nav-bar {
   -moz-appearance: none;
   background: url(chrome://browser/skin/Toolbar-background-noise.png),
               linear-gradient(hsl(0,0%,93%), hsl(0,0%,83%));
   background-clip: border-box;
   background-origin: border-box !important;
 
   /* Move the noise texture out of the top 1px strip because that overlaps
@@ -133,16 +127,29 @@ toolbarseparator {
 }
 
 @media (min-resolution: 2dppx) {
   #nav-bar {
     background-size: 100px 100px, auto;
   }
 }
 
+/* Draw the bottom border of the tabs toolbar when it's not using
+   -moz-appearance: toolbar. */
+#main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
+#main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
+#TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
+  border-top: 1px solid hsla(0,0%,0%,.3);
+  background-clip: padding-box;
+  margin-top: -@tabToolbarNavbarOverlap@;
+  /* Position the toolbar above the bottom of background tabs */
+  position: relative;
+  z-index: 1;
+}
+
 #nav-bar-customization-target {
   padding: 4px;
 }
 
 #PersonalToolbar {
   padding: 0 4px 4px;
   /* 4px padding ^  plus 19px personal-bookmarks (see below) */
   min-height: 23px;
@@ -2765,76 +2772,35 @@ toolbarbutton.chevron > .toolbarbutton-m
   border: solid transparent;
   border-width: 0 11px;
 }
 
 .tabbrowser-tab:focus > .tab-stack > .tab-content > .tab-label {
   box-shadow: @focusRingShadow@;
 }
 
-/* We want the titlebar to be unified, but we still want to be able
- * to give #TabsToolbar a background. So we can't set -moz-appearance:
- * toolbar on #TabsToolbar itself. Instead, we set it on a box of the
- * right size which is put underneath #TabsToolbar.
- *
- * Because of Bug 941309, we make sure this pseudoelement always exists,
- * but we only make it visible when we need it.
- */
-#navigator-toolbox::before {
-  content: '';
-  display: block;
+#TabsToolbar {
+  -moz-appearance: none;
+  margin-bottom: -1px; /* Overlap the inner highlight at the top of the nav-bar */
+}
+
+#main-window:not([customizing]) #navigator-toolbox[inFullscreen] > #TabsToolbar:not(:-moz-lwtheme),
+#main-window:not(:-moz-any([customizing],[tabsintitlebar])) #navigator-toolbox > #TabsToolbar:not(:-moz-lwtheme) {
   -moz-appearance: toolbar;
-  height: @tabMinHeight@;
-  margin-bottom: -@tabMinHeight@;
-  visibility: hidden;
-}
-
-#main-window:not([customizing]) #navigator-toolbox[inFullscreen]:not(:-moz-lwtheme)::before,
-#main-window:not(:-moz-any([customizing],[tabsintitlebar])) #navigator-toolbox:not(:-moz-lwtheme)::before {
-  visibility: visible;
-}
-
-#TabsToolbar {
-  position: relative;
-  -moz-appearance: none;
-  background-repeat: repeat-x;
-  margin-bottom: -@tabToolbarNavbarOverlap@;
 }
 
 #TabsToolbar:not(:-moz-lwtheme) {
   color: #333;
   text-shadow: @loweredShadow@;
 }
 
-/*
- * Draw the bottom border of the tabstrip when core doesn't do it for us.
- * Because of Bug 941309, we make sure this pseudoelement always exists,
- * but we only make it visible when we need it.
- */
-#TabsToolbar::after {
-  content: '';
-  /* Because we use placeholders for window controls etc. in the tabstrip,
-   * and position those with ordinal attributes, and because our layout code
-   * expects :before/:after nodes to come first/last in the frame list,
-   * we have to reorder this element to come last, hence the
-   * ordinal group value (see bug 853415). */
-  -moz-box-ordinal-group: 1001;
-  position: absolute;
-  bottom: @tabToolbarNavbarOverlap@;
-  left: 0;
-  right: 0;
-  z-index: 0;
-  border-bottom: 1px solid hsla(0,0%,0%,.3);
-  visibility: hidden;
-}
-
-#main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar::after,
-#main-window:not([tabsintitlebar]) #TabsToolbar::after,
-#TabsToolbar:-moz-lwtheme::after {
-  visibility: visible;
+@media (-moz-mac-lion-theme) {
+  #navigator-toolbox[inFullscreen] > #TabsToolbar {
+    padding-top: @spaceAboveTabbar@;
+  }
 }
 
 #tabbrowser-tabs {
   -moz-box-align: stretch;
 }
 
 .tabs-newtab-button > .toolbarbutton-icon {
   padding: 6px 0 4px;
@@ -4053,27 +4019,16 @@ menulist.translate-infobar-element > .me
 
 .statuspanel-label:-moz-locale-dir(rtl):not([mirror]),
 .statuspanel-label:-moz-locale-dir(ltr)[mirror] {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
-/* Lion Fullscreen window styling */
-@media (-moz-mac-lion-theme) {
-  #navigator-toolbox[inFullscreen]::before {
-    /* Adjust by the full element height of #titlebar, since that element is
-     * not displayed in native full-screen.
-     * Also add the height of the tabs, since we're calculating the
-     * total height of this pseudo-element, not just the top-padding. */
-    height: calc(@tabMinHeight@ + @spaceAboveTabbar@) !important;
-  }
-}
-
 #full-screen-warning-message {
   background-image: url("chrome://browser/skin/fullscreen-darknoise.png");
   color: white;
   border-radius: 4px;
   margin-top: 30px;
   padding: 30px 50px;
   box-shadow: 0 0 2px white;
 }
@@ -4416,16 +4371,21 @@ window > chatbox {
   -moz-border-left-colors: hsla(0,0%,0%,.05) hsla(0,0%,0%,.1) hsla(0,0%,0%,.2);
 }
 
 #main-window[customize-entered] #customization-container,
 #main-window[customize-entered] #navigator-toolbox > toolbar:not(#TabsToolbar) {
   border-bottom-width: 0;
 }
 
+#main-window[customize-entered] #nav-bar {
+  border-top-left-radius: 2.5px;
+  border-top-right-radius: 2.5px;
+}
+
 /* Compensate for the border set above for this horizontal line. */
 #main-window[customize-entered] #navigator-toolbox::after {
   margin-left: 3px;
   margin-right: 3px;
 }
 
 #main-window[customize-entered] #TabsToolbar {
   background-clip: padding-box;
--- a/browser/themes/shared/devtools/projecteditor/projecteditor.css
+++ b/browser/themes/shared/devtools/projecteditor/projecteditor.css
@@ -6,130 +6,151 @@
  :root {
   color: #18191a;
 }
 
 .plugin-hidden {
   display: none;
 }
 
+.arrow {
+  -moz-appearance: treetwisty;
+  width: 20px;
+  height: 20px;
+}
+
+.arrow[open] {
+  -moz-appearance: treetwistyopen;
+}
+
+.arrow[invisible] {
+  visibility: hidden;
+}
+
 #projecteditor-menubar {
   /* XXX: Hide menu bar until we have option to add menu items
      to an existing one. */
   display: none;
 }
 
 #projecteditor-toolbar,
 #projecteditor-toolbar-bottom {
   display: none; /* For now don't show the status bars */
   min-height: 22px;
   height: 22px;
   background: rgb(237, 237, 237);
 }
 
 .sources-tree {
   overflow:auto;
+  overflow-x: hidden;
   -moz-user-focus: normal;
+
+  /* Allows this to expand inside of parent xul element, while
+     still supporting child flexbox elements, including ellipses. */
+  -moz-box-flex: 1;
+  display: block;
 }
 
 .sources-tree input {
   margin: 2px;
   border: 1px solid gray;
 }
 
 #main-deck .sources-tree {
   background: rgb(225, 225, 225);
-  min-width: 50px;
-}
-
-#main-deck .sources-tree .side-menu-widget-item {
-  color: #18191A;
+  min-width: 100px;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .file-label {
-  vertical-align: middle;
-  display: inline-block;
+.entry {
+  color: #18191A;
+  display: flex;
+  align-items: center;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .file-icon {
+.entry .file-label {
+  display: flex;
+  flex: 1;
+  align-items: center;
+}
+
+.entry .file-icon {
   display: inline-block;
   background: url(file-icons-sheet@2x.png);
   background-size: 140px 15px;
   background-repeat: no-repeat;
   width: 20px;
   height: 15px;
-  vertical-align: middle;
   background-position: -40px 0;
+  flex-shrink: 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .file-icon.icon-none {
+.entry .file-icon.icon-none {
   display: none;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-css {
+.entry .icon-css {
   background-position: 0 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-js {
+.entry .icon-js {
   background-position: -20px 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-html {
+.entry .icon-html {
   background-position: -40px 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-file {
+.entry .icon-file {
   background-position: -60px 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-folder {
+.entry .icon-folder {
   background-position: -80px 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-img {
+.entry .icon-img {
   background-position: -100px 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item .icon-manifest {
+.entry .icon-manifest {
   background-position: -120px 0;
 }
 
-#main-deck .sources-tree .side-menu-widget-item:hover {
-  background: rgba(0, 0, 0, .05);
+.entry {
+  border: none;
+  box-shadow: none;
+  white-space: nowrap;
   cursor: pointer;
 }
 
-#main-deck .sources-tree .side-menu-widget-item {
-  border: none;
-  box-shadow: none;
-  line-height: 20px;
-  vertical-align: middle;
-  white-space: nowrap;
+.entry:hover:not(.entry-group-title):not(.selected) {
+  background: rgba(0, 0, 0, .05);
 }
 
-#main-deck .sources-tree .side-menu-widget-item.selected {
-  background: #3875D7;
+.entry.selected {
+  background: rgba(56, 117, 215, 1);
   color: #F5F7FA;
   outline: none;
 }
 
-#main-deck .sources-tree .side-menu-widget-group-title,
-#main-deck .sources-tree .side-menu-widget-group-title:hover:not(.selected) {
-  background: #B4D7EB;
-  color: #222;
+.entry-group-title {
+  background: rgba(56, 117, 215, 0.8);
+  color: #F5F7FA;
   font-weight: bold;
   font-size: 1.05em;
-  cursor: default;
   line-height: 35px;
+  padding: 0 10px;
 }
 
-#main-deck .sources-tree li.child:only-child .side-menu-widget-group-title .expander {
+.sources-tree .entry-group-title .expander {
   display: none;
 }
-#main-deck .sources-tree .side-menu-widget-item .expander {
+
+.entry .expander {
   width: 16px;
   padding: 0;
 }
 
 .tree-collapsed .children {
   display: none;
 }
 
@@ -138,35 +159,67 @@
 #projecteditor-toolbar textbox {
   margin: 0;
 }
 
 .projecteditor-basic-display {
   padding: 0 3px;
 }
 
+/* App Manager */
 .project-name-label {
   font-weight: bold;
   padding-left: 10px;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
-.project-version-label {
-  color: #666;
-  padding-left: 5px;
-  font-size: .9em;
+.project-flex {
+  flex: 1;
 }
 
 .project-image {
-  max-height: 28px;
-  margin-left: -.5em;
-  vertical-align: middle;
+  max-height: 25px;
+  margin-left: -10px;
+}
+
+.project-image,
+.project-status,
+.project-options {
+  flex-shrink: 0;
+}
+
+.project-status {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  border: solid 1px rgba(255, 255, 255, .5);
+  margin-right: 10px;
+  visibility: hidden;
 }
 
+.project-status[status=valid] {
+  background: #70bf53;
+  visibility: visible;
+}
+
+.project-status[status=warning] {
+  background: #d99b28;
+  visibility: visible;
+}
+
+.project-status[status=error] {
+  background: #ed2655;
+  visibility: visible;
+}
+
+/* Status Bar */
+.projecteditor-file-label {
+  font-weight: bold;
+  padding-left: 29px;
+  padding-right: 10px;
+  flex: 1;
+}
+
+/* Image View */
 .editor-image {
   padding: 10px;
 }
-
-.projecteditor-file-label {
-  font-weight: bold;
-  padding-left: 29px;
-  vertical-align: middle;
-}
-
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -1,20 +1,15 @@
 %if 0
 /* - 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/. */
 %endif
 @namespace html "http://www.w3.org/1999/xhtml";
 
-@font-face {
-  font-family: "Clear Sans";
-  src: url("chrome://browser/content/fonts/ClearSans-Regular.woff") format('woff');
-}
-
 page {
   -moz-appearance: none;
   background-color: white;
 }
 
 * {
   -moz-user-select: text;
 }
@@ -34,17 +29,17 @@ caption > label {
 .main-content {
   padding: 40px 48px 48px;
   overflow: auto;
 }
 
 prefpane {
   max-width: 800px;
   padding: 0;
-  font-family: "Clear Sans", sans-serif;
+  font: message-box;
   font-size: 1.25rem;
   line-height: 22px;
   color: #424E5A;
 }
 
 prefpane > .content-box {
   overflow: visible;
 }
@@ -287,17 +282,16 @@ button[type="menu"] > menupopup {
   background-color: #FFFFFF;
 }
 
 menulist > menupopup menu,
 menulist > menupopup menuitem,
 button[type="menu"] > menupopup menu,
 button[type="menu"] > menupopup menuitem {
   -moz-appearance: none;
-  font-family: "Clear Sans", sans-serif;
   font-size: 1.25rem;
   line-height: 22px;
   height: 40px;
   color: #333333;
   -moz-padding-start: 10px;
   -moz-padding-end: 30px;
 }
 
@@ -483,17 +477,16 @@ radio[disabled="true"] > .radio-check {
 .category[selected] {
   background-color: #343f48;
   color: #f2f2f2;
   box-shadow: inset 4px 0 0 0 #FF9500;
 }
 
 .category-name {
   line-height: 22px;
-  font-family: "Clear Sans", sans-serif;
   font-size: 1.25rem;
   padding-bottom: 2px;
   -moz-padding-start: 9px;
   margin: 0;
 }
 
 .category-icon {
   width: 24px;
@@ -689,17 +682,16 @@ filefield {
   border-radius: 2px;
   background-color: #FBFBFB;
   overflow-y: auto;
 }
 
 #typeColumn,
 #actionColumn {
   -moz-appearance: none;
-  font-family: "Clear Sans", sans-serif;
   line-height: 20px;
   color: #333333;
   height: 36px;
   padding: 0 10px;
   background-color: #FBFBFB;
   border: 1px solid #C1C1C1;
   -moz-border-top-colors: none;
   -moz-border-right-colors: none;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -435,21 +435,23 @@ browser.jar:
         skin/classic/aero/browser/identity-icons-https-mixed-active.png
         skin/classic/aero/browser/identity-icons-https-mixed-display.png
         skin/classic/aero/browser/in-content/common.css              (../shared/in-content/common.css)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/livemark-folder.png                (livemark-folder-aero.png)
         skin/classic/aero/browser/menu-back.png                      (menu-back-aero.png)
         skin/classic/aero/browser/menu-forward.png                   (menu-forward-aero.png)
-        skin/classic/aero/browser/menuPanel.png                      (menuPanel-aero.png)
+        skin/classic/aero/browser/menuPanel.png
+        skin/classic/aero/browser/menuPanel-aero.png
         skin/classic/aero/browser/menuPanel-customize.png
         skin/classic/aero/browser/menuPanel-exit.png
         skin/classic/aero/browser/menuPanel-help.png
-        skin/classic/aero/browser/menuPanel-small.png                (menuPanel-small-aero.png)
+        skin/classic/aero/browser/menuPanel-small.png
+        skin/classic/aero/browser/menuPanel-small-aero.png
         skin/classic/aero/browser/Metro_Glyph.png                    (Metro_Glyph-aero.png)
         skin/classic/aero/browser/Metro_Glyph-inverted.png
         skin/classic/aero/browser/Metro_Glyph-menuPanel.png
         skin/classic/aero/browser/mixed-content-blocked-16.png
         skin/classic/aero/browser/mixed-content-blocked-64.png
         skin/classic/aero/browser/monitor.png
         skin/classic/aero/browser/monitor_16-10.png
         skin/classic/aero/browser/notification-16.png
@@ -801,10 +803,16 @@ browser.jar:
         skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-light.png   (../shared/devtools/tooltip/arrow-horizontal-light.png)
         skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-light@2x.png   (../shared/devtools/tooltip/arrow-horizontal-light@2x.png)
         skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light.png   (../shared/devtools/tooltip/arrow-vertical-light.png)
         skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light@2x.png   (../shared/devtools/tooltip/arrow-vertical-light@2x.png)
 
 % override chrome://browser/skin/Toolbar.png               chrome://browser/skin/Toolbar-aero.png                  os=WINNT osversion=6
 % override chrome://browser/skin/Toolbar.png               chrome://browser/skin/Toolbar-aero.png                  os=WINNT osversion=6.1
 
+% override chrome://browser/skin/menuPanel.png             chrome://browser/skin/menuPanel-aero.png                os=WINNT osversion=6
+% override chrome://browser/skin/menuPanel.png             chrome://browser/skin/menuPanel-aero.png                os=WINNT osversion=6.1
+
+% override chrome://browser/skin/menuPanel-small.png       chrome://browser/skin/menuPanel-small-aero.png          os=WINNT osversion=6
+% override chrome://browser/skin/menuPanel-small.png       chrome://browser/skin/menuPanel-small-aero.png          os=WINNT osversion=6.1
+
 % override chrome://browser/skin/sync-horizontalbar.png          chrome://browser/skin/sync-horizontalbar-XPVista7.png          os=WINNT osversion<6.2
 % override chrome://browser/skin/syncProgress-horizontalbar.png  chrome://browser/skin/syncProgress-horizontalbar-XPVista7.png  os=WINNT osversion<6.2
--- a/configure.in
+++ b/configure.in
@@ -7675,29 +7675,16 @@ if test "$MOZ_CHROME_FILE_FORMAT" = "sym
 fi
 
 if test "$MOZ_CHROME_FILE_FORMAT" != "jar" &&
     test "$MOZ_CHROME_FILE_FORMAT" != "flat" &&
     test "$MOZ_CHROME_FILE_FORMAT" != "omni"; then
     AC_MSG_ERROR([--enable-chrome-format must be set to either jar, flat, or omni])
 fi
 
-dnl ========================================================
-dnl = Enable Support for devtools webide
-dnl ========================================================
-MOZ_ARG_ENABLE_BOOL(devtools-webide,
-[  --enable-devtools-webide Set compile flags necessary for compiling devtools webide ],
-MOZ_DEVTOOLS_WEBIDE=1,
-MOZ_DEVTOOLS_WEBIDE= )
-
-if test -n "$MOZ_DEVTOOLS_WEBIDE"; then
-    AC_DEFINE(MOZ_DEVTOOLS_WEBIDE)
-fi
-AC_SUBST(MOZ_DEVTOOLS_WEBIDE)
-
 dnl =========================================================
 dnl Omnijar packaging (bug 552121)
 dnl =========================================================
 dnl Omnijar packaging is compatible with flat packaging.
 dnl In unpackaged builds, omnijar looks for files as if
 dnl things were flat packaged. After packaging, all files
 dnl are loaded from a single jar. MOZ_CHROME_FILE_FORMAT
 dnl is set to flat since putting files into jars is only
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -4041,17 +4041,17 @@ CanvasRenderingContext2D::PutImageData(I
 
 nsresult
 CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                                 dom::Uint8ClampedArray* aArray,
                                                 bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
                                                 int32_t dirtyWidth, int32_t dirtyHeight)
 {
   if (w == 0 || h == 0) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   IntRect dirtyRect;
   IntRect imageDataRect(0, 0, w, h);
 
   if (hasDirtyRect) {
     // fix up negative dimensions
     if (dirtyWidth < 0) {
@@ -4095,17 +4095,17 @@ CanvasRenderingContext2D::PutImageData_e
   }
 
   aArray->ComputeLengthAndData();
 
   uint32_t dataLen = aArray->Length();
 
   uint32_t len = w * h * 4;
   if (dataLen != len) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   nsRefPtr<gfxImageSurface> imgsurf = new gfxImageSurface(gfxIntSize(w, h),
                                                           gfxImageFormat::ARGB32,
                                                           false);
   if (!imgsurf || imgsurf->CairoStatus()) {
     return NS_ERROR_FAILURE;
   }
--- a/content/media/MediaData.cpp
+++ b/content/media/MediaData.cpp
@@ -225,17 +225,18 @@ VideoData* VideoData::Create(VideoInfo& 
   if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
       aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
     NS_ERROR("C planes with different sizes");
     return nullptr;
   }
 
   // The following situations could be triggered by invalid input
   if (aPicture.width <= 0 || aPicture.height <= 0) {
-    NS_WARNING("Empty picture rect");
+    // In debug mode, makes the error more noticeable
+    MOZ_ASSERT(false, "Empty picture rect");
     return nullptr;
   }
   if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
       !ValidatePlane(aBuffer.mPlanes[2])) {
     NS_WARNING("Invalid plane size");
     return nullptr;
   }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13048,8 +13048,34 @@ nsDocShell::IsInvisible()
     return mInvisible;
 }
 
 void
 nsDocShell::SetInvisible(bool aInvisible)
 {
     mInvisible = aInvisible;
 }
+
+void
+nsDocShell::SetOpener(nsITabParent* aOpener)
+{
+  mOpener = do_GetWeakReference(aOpener);
+}
+
+nsITabParent*
+nsDocShell::GetOpener()
+{
+  nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
+  return opener;
+}
+
+void
+nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote)
+{
+  mOpenedRemote = do_GetWeakReference(aOpenedRemote);
+}
+
+nsITabParent*
+nsDocShell::GetOpenedRemote()
+{
+  nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
+  return openedRemote;
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -37,16 +37,17 @@
 #include "nsIWebPageDescriptor.h"
 #include "nsIWebProgressListener.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsILoadContext.h"
 #include "nsIWebShellServices.h"
 #include "nsILinkHandler.h"
 #include "nsIClipboardCommands.h"
+#include "nsITabParent.h"
 #include "nsCRT.h"
 #include "prtime.h"
 #include "nsRect.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
 }
@@ -897,16 +898,18 @@ private:
     nsCString         mForcedCharset;
     nsCString         mParentCharset;
     int32_t           mParentCharsetSource;
     nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
     nsTObserverArray<nsWeakPtr> mPrivacyObservers;
     nsTObserverArray<nsWeakPtr> mReflowObservers;
     nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
+    nsWeakPtr mOpener;
+    nsWeakPtr mOpenedRemote;
 
     // Separate function to do the actual name (i.e. not _top, _self etc.)
     // searching for FindItemWithName.
     nsresult DoFindItemWithName(const char16_t* aName,
                                 nsISupports* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
                                 nsIDocShellTreeItem** _retval);
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -37,20 +37,21 @@ interface nsISecureBrowserUI;
 interface nsIScriptGlobalObject;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
-
+interface nsITabParent;
+ 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(3ca96c12-b69d-4b54-83c5-25a18d32a22b)]
+[scriptable, builtinclass, uuid(9c65a466-9814-48f8-a4ca-c4600b03b15d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -953,9 +954,25 @@ interface nsIDocShell : nsIDocShellTreeI
    *
    * Used by the Responsive Design View and B2G Simulator.
    *
    * Default is False.
    * Default value can be overriden with
    * docshell.device_size_is_page_size pref.
    */
   [infallible] attribute boolean deviceSizeIsPageSize;
+
+  /**
+   * Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
+   * for notxpcom, so we're relegated to using explicit gets / sets. This
+   * should be fine, considering that these methods should only ever be
+   * called from native code.
+   */
+  [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
+  [noscript,notxpcom,nostdcall] nsITabParent getOpener();
+
+  /**
+   * See the documentation for setOpener and getOpener about why we
+   * don't use attribute here instead.
+   */
+  [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote);
+  [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote();
 };
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -11639,17 +11639,17 @@ nsGlobalWindow::OpenInternal(const nsASt
     // dialog is open.
     nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
     if (!aCalledNoScript) {
       // We asserted at the top of this function that aNavigate is true for
       // !aCalledNoScript.
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ true,
-                                aDialog, aNavigate, argv,
+                                aDialog, aNavigate, nullptr, argv,
                                 getter_AddRefs(domReturn));
     } else {
       // Force a system caller here so that the window watcher won't screw us
       // up.  We do NOT want this case looking at the JS context on the stack
       // when searching.  Compare comments on
       // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
 
       // Note: Because nsWindowWatcher is so broken, it's actually important
@@ -11659,17 +11659,17 @@ nsGlobalWindow::OpenInternal(const nsASt
       Maybe<AutoNoJSAPI> nojsapi;
       if (!aContentModal) {
         nojsapi.construct();
       }
 
 
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ false,
-                                aDialog, aNavigate, aExtraArgument,
+                                aDialog, aNavigate, nullptr, aExtraArgument,
                                 getter_AddRefs(domReturn));
 
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // success!
--- a/dom/bindings/CallbackInterface.cpp
+++ b/dom/bindings/CallbackInterface.cpp
@@ -8,25 +8,28 @@
 #include "jsapi.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
-CallbackInterface::GetCallableProperty(JSContext* cx, const char* aPropName,
+CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
                                        JS::MutableHandle<JS::Value> aCallable)
 {
-  if (!JS_GetProperty(cx, CallbackPreserveColor(), aPropName, aCallable)) {
+  if (!JS_GetPropertyById(cx, CallbackPreserveColor(), aPropId, aCallable)) {
     return false;
   }
   if (!aCallable.isObject() ||
       !JS_ObjectIsCallable(cx, &aCallable.toObject())) {
-    nsPrintfCString description("Property '%s'", aPropName);
+    char* propName =
+      JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
+    nsPrintfCString description("Property '%s'", propName);
+    JS_free(cx, propName);
     ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
     return false;
   }
 
   return true;
 }
 
 } // namespace dom
--- a/dom/bindings/CallbackInterface.h
+++ b/dom/bindings/CallbackInterface.h
@@ -26,17 +26,17 @@ class CallbackInterface : public Callbac
 public:
   explicit CallbackInterface(JS::Handle<JSObject*> aCallback,
                              nsIGlobalObject *aIncumbentGlobal)
     : CallbackObject(aCallback, aIncumbentGlobal)
   {
   }
 
 protected:
-  bool GetCallableProperty(JSContext* cx, const char* aPropName,
+  bool GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
                            JS::MutableHandle<JS::Value> aCallable);
 
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CallbackFunction_h
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -237,17 +237,17 @@ class CGNativePropertyHooks(CGThing):
             // We declare this as an array so that retrieving a pointer to this
             // binding's property hooks only requires compile/link-time resolvable
             // address arithmetic.  Declaring it as a pointer instead would require
             // doing a run-time load to fetch a pointer to this binding's property
             // hooks.  And then structures which embedded a pointer to this structure
             // would require a run-time load for proper initialization, which would
             // then induce static constructors.  Lots of static constructors.
             extern const NativePropertyHooks sNativePropertyHooks[];
-            """).rstrip()  # BOGUS strip newline from the last line here (!)
+            """)
 
     def define(self):
         if self.descriptor.workers:
             return ""
         if self.descriptor.concrete and self.descriptor.proxy:
             resolveOwnProperty = "ResolveOwnProperty"
             enumerateOwnProperties = "EnumerateOwnProperties"
         elif self.descriptor.needsXrayResolveHooks():
@@ -372,18 +372,17 @@ JS_NULL_OBJECT_OPS
   nullptr, /* getGeneric  */
   nullptr, /* getProperty */
   nullptr, /* getElement */
   nullptr, /* setGeneric */
   nullptr, /* setProperty */
   nullptr, /* setElement */
   nullptr, /* getGenericAttributes */
   nullptr, /* setGenericAttributes */
-  nullptr, /* deleteProperty */
-  nullptr, /* deleteElement */
+  nullptr, /* deleteGeneric */
   nullptr, /* watch */
   nullptr, /* unwatch */
   nullptr, /* slice */
   nullptr, /* enumerate */
   JS_ObjectToOuterObject /* thisObject */
 }
 """
         else:
@@ -396,19 +395,18 @@ JS_NULL_OBJECT_OPS
         elif self.descriptor.interface.getExtendedAttribute("Global"):
             newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = "mozilla::dom::EnumerateGlobal"
         else:
             newResolveHook = "JS_ResolveStub"
             enumerateHook = "JS_EnumerateStub"
 
-        return fill(  # BOGUS extra blank line at the top
-            """
-
+        return fill(
+            """
             static const DOMJSClass Class = {
               { "${name}",
                 ${flags},
                 ${addProperty}, /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
                 JS_PropertyStub,       /* getProperty */
                 JS_StrictPropertyStub, /* setProperty */
                 ${enumerate}, /* enumerate */
@@ -458,19 +456,18 @@ class CGDOMProxyJSClass(CGThing):
     def define(self):
         flags = ["JSCLASS_IS_DOMJSCLASS"]
         # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
         # we don't want people ever adding that to any interface other than
         # HTMLAllCollection.  So just hardcode it here.
         if self.descriptor.interface.identifier.name == "HTMLAllCollection":
             flags.append("JSCLASS_EMULATES_UNDEFINED")
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
-        return fill(  # BOGUS extra blank line at the top
-            """
-
+        return fill(
+            """
             static const DOMJSClass Class = {
               PROXY_CLASS_DEF("${name}",
                               0, /* extra slots */
                               ${flags},
                               ${call}, /* call */
                               nullptr  /* construct */),
               $*{descriptor}
             };
@@ -632,19 +629,18 @@ class CGInterfaceObjectJSClass(CGThing):
             hasinstance = "InterfaceHasInstance"
         else:
             hasinstance = "nullptr"
         prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
         slotCount = "DOM_INTERFACE_SLOTS_BASE"
         if len(self.descriptor.interface.namedConstructors) > 0:
             slotCount += (" + %i /* slots for the named constructors */" %
                           len(self.descriptor.interface.namedConstructors))
-        return fill(  # BOGUS extra newline at the top
-            """
-
+        return fill(
+            """
             static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
               {
                 "Function",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 JS_PropertyStub,       /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
                 JS_PropertyStub,       /* getProperty */
                 JS_StrictPropertyStub, /* setProperty */
@@ -1415,18 +1411,17 @@ class CGAbstractClassHook(CGAbstractStat
     Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
     'this' unwrapping as it assumes that the unwrapped type is always known.
     """
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
                                         args)
 
     def definition_body_prologue(self):
-        return ("\n"  # BOGUS extra blank line at start of function
-                "%s* self = UnwrapDOMObject<%s>(obj);\n" %
+        return ("%s* self = UnwrapDOMObject<%s>(obj);\n" %
                 (self.descriptor.nativeType, self.descriptor.nativeType))
 
     def definition_body(self):
         return self.definition_body_prologue() + self.generate_code()
 
     def generate_code(self):
         assert False  # Override me!
 
@@ -1537,19 +1532,18 @@ class CGClassConstructor(CGAbstractStati
         # the name JS sees is the interface name; for named constructors
         # identifier.name is the actual name.
         name = self._ctor.identifier.name
         if name != "constructor":
             ctorName = name
         else:
             ctorName = self.descriptor.interface.identifier.name
 
-        preamble = fill(  # BOGUS extra blank line at beginning of function body
-            """
-
+        preamble = fill(
+            """
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
             JS::Rooted<JSObject*> obj(cx, &args.callee());
             $*{chromeOnlyCheck}
             bool mayInvoke = args.isConstructing();
             #ifdef RELEASE_BUILD
             mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome();
             #endif // RELEASE_BUILD
             if (!mayInvoke) {
@@ -1561,17 +1555,17 @@ class CGClassConstructor(CGAbstractStati
             chromeOnlyCheck=chromeOnlyCheck,
             ctorName=ctorName)
 
         name = self._ctor.identifier.name
         nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
         callGenerator = CGMethodCall(nativeName, True, self.descriptor,
                                      self._ctor, isConstructor=True,
                                      constructorName=ctorName)
-        return preamble + callGenerator.define()
+        return preamble + "\n" + callGenerator.define()
 
 
 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
 class CGConstructNavigatorObjectHelper(CGAbstractStaticMethod):
     """
     Construct a new JS-implemented WebIDL DOM object, for use on navigator.
     """
     def __init__(self, descriptor):
@@ -1703,19 +1697,17 @@ class CGClassHasInstanceHook(CGAbstractS
         if not NeedsGeneratedHasInstance(self.descriptor):
             return ""
         return CGAbstractStaticMethod.define(self)
 
     def definition_body(self):
         return self.generate_code()
 
     def generate_code(self):
-        # BOGUS extra blank line at start of function
         header = dedent("""
-
             if (!vp.isObject()) {
               *bp = false;
               return true;
             }
 
             JS::Rooted<JSObject*> instance(cx, &vp.toObject());
             """)
         if self.descriptor.interface.hasInterfacePrototypeObject():
@@ -1734,18 +1726,17 @@ class CGClassHasInstanceHook(CGAbstractS
 
                     // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
                     nsISupports* native =
                       nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
                                                                       js::UncheckedUnwrap(instance, /* stopAtOuter = */ false));
                     nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
                     *bp = !!qiResult;
                     return true;
-
-                    """,  # BOGUS extra blank line at end of function
+                    """,
                     nativeType=self.descriptor.nativeType,
                     name=self.descriptor.interface.identifier.name))
 
         hasInstanceCode = dedent("""
 
             const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtOuter = */ false));
             *bp = false;
             if (!domClass) {
@@ -2351,18 +2342,17 @@ class CGNativeProperties(CGList):
                 generateNativeProperties("sChromeOnlyNativeProperties", True))
 
         CGList.__init__(self, nativeProperties, "\n")
 
     def declare(self):
         return ""
 
     def define(self):
-        # BOGUSly strip off a newline
-        return CGList.define(self).rstrip()
+        return CGList.define(self)
 
 
 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
     """
     Generate the CreateInterfaceObjects method for an interface descriptor.
 
     properties should be a PropertyArrays instance.
     """
@@ -2581,25 +2571,23 @@ class CGGetPerInterfaceObject(CGAbstract
     def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aGlobal')] + extraArgs
         CGAbstractMethod.__init__(self, descriptor, name,
                                   'JS::Handle<JSObject*>', args)
         self.id = idPrefix + "id::" + self.descriptor.name
 
     def definition_body(self):
-        # BOGUS extra blank line at the beginning of the code below
-        # BOGUS - should be a blank line between an if-block and following comment below
         return fill(
             """
-
             /* Make sure our global is sane.  Hopefully we can remove this sometime */
             if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
               return JS::NullPtr();
             }
+
             /* Check to see whether the interface objects are already installed */
             ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
             if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
               CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal);
             }
 
             /*
              * The object might _still_ be null, but that's OK.
@@ -2617,41 +2605,39 @@ class CGGetProtoObjectMethod(CGGetPerInt
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
                                          "prototypes::")
 
     def definition_body(self):
-        # BOGUS extra blank line at start of method
         return dedent("""
-
             /* Get the interface prototype object for this class.  This will create the
                object as needed. */
             bool aDefineOnGlobal = true;
+
             """) + CGGetPerInterfaceObject.definition_body(self)
 
 
 class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface constructor object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(
             self, descriptor, "GetConstructorObject",
             "constructors::",
             extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
 
     def definition_body(self):
-        # BOGUS extra blank line at start of method
         return dedent("""
-
             /* Get the interface object for this class.  This will create the object as
                needed. */
+
             """) + CGGetPerInterfaceObject.definition_body(self)
 
 
 class CGDefineDOMInterfaceMethod(CGAbstractMethod):
     """
     A method for resolve hooks to try to lazily define the interface object for
     a given interface.
     """
@@ -2817,18 +2803,17 @@ def InitUnforgeableProperties(descriptor
     """
     unforgeableAttrs = properties.unforgeableAttrs
     if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
         return ""
 
     if descriptor.proxy:
         unforgeableProperties = CGGeneric(
             "// Unforgeable properties on proxy-based bindings are stored in an object held\n"
-            "// by the interface prototype object.\n"
-            "\n")  # BOGUS extra blank line
+            "// by the interface prototype object.\n")
     else:
         unforgeableProperties = CGWrapper(
             InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
             pre=(
                 "// Important: do unforgeable property setup after we have handed\n"
                 "// over ownership of the C++ object to obj as needed, so that if\n"
                 "// we fail and it ends up GCed it won't have problems in the\n"
                 "// finalizer trying to drop its ownership of the C++ object.\n"))
@@ -2854,17 +2839,17 @@ def InitMemberSlots(descriptor, wrapperC
     Initialize member slots on our JS object if we're supposed to have some.
 
     Note that this is called after the SetWrapper() call in the
     wrapperCache case, since that can affect how our getters behave
     and we plan to invoke them here.  So if we fail, we need to
     ClearWrapper.
     """
     if not descriptor.interface.hasMembersInSlots():
-        return "\n"  # BOGUS blank line only if this returns empty
+        return ""
     if wrapperCache:
         clearWrapper = "  aCache->ClearWrapper();\n"
     else:
         clearWrapper = ""
     return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n"
             "%s"
             "  return nullptr;\n"
             "}\n" % clearWrapper)
@@ -4216,18 +4201,17 @@ def getJSToNativeConversionInfo(type, de
             return handleJSObjectType(type, isMember, failureCode)
 
         if descriptor.interface.isCallback():
             name = descriptor.interface.identifier.name
             if type.nullable() or isCallbackReturnValue:
                 declType = CGGeneric("nsRefPtr<%s>" % name)
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
-            # BOGUS extra blank line here turns out to be at the end of a block:
-            conversion = indent(CGCallbackTempRoot(name).define()) + "\n"
+            conversion = indent(CGCallbackTempRoot(name).define())
 
             template = wrapObjectTemplate(conversion, type,
                                           "${declName} = nullptr;\n",
                                           failureCode)
             return JSToNativeConversionInfo(template, declType=declType,
                                             dealWithOptional=isOptional)
 
         # This is an interface that we implement as a concrete class
@@ -4360,18 +4344,17 @@ def getJSToNativeConversionInfo(type, de
         # Again, this is a bit strange since we are actually building a
         # template string here. ${objRef} and $*{badType} below are filled in
         # right now; $${val} expands to ${val}, to be filled in later.
         template = fill(
             """
             if (!${objRef}.Init(&$${val}.toObject())) {
               $*{badType}
             }
-
-            """,  # BOGUS extra blank line
+            """,
             objRef=objRef,
             badType=onFailureBadType(failureCode, type.name).define())
         template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
                                       failureCode)
         if not isMember:
             # This is a bit annoying.  In a union we don't want to have a
             # holder, since unions don't support that.  But if we're optional we
             # want to have a holder, so that the callee doesn't see
@@ -4450,18 +4433,17 @@ def getJSToNativeConversionInfo(type, de
             return JSToNativeConversionInfo(
                 fill(
                     """
                     {
                       binding_detail::FakeDependentString str;
                       $*{convert}
                       $*{assign}
                     }
-
-                    """,  # BOGUS extra newline
+                    """,
                     convert=getConversionCode("str"),
                     assign=assignString),
                 declType=declType,
                 dealWithOptional=isOptional)
 
         if isOptional:
             declType = "Optional<nsAString>"
             holderType = CGGeneric("binding_detail::FakeDependentString")
@@ -4591,17 +4573,17 @@ def getJSToNativeConversionInfo(type, de
                 "}\n")
         elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
             if not isDefinitelyObject:
                 haveObject = "${val}.isObject()"
                 if defaultValue is not None:
                     assert(isinstance(defaultValue, IDLNullValue))
                     haveObject = "${haveValue} && " + haveObject
                 template = CGIfElseWrapper(haveObject,
-                                           CGGeneric(conversion + "\n"),  # BOGUS extra blank line
+                                           CGGeneric(conversion),
                                            CGGeneric("${declName} = nullptr;\n")).define()
             else:
                 template = conversion
         else:
             template = wrapObjectTemplate(
                 "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" +
                 conversion +
                 "} else {\n" +
@@ -6354,19 +6336,18 @@ class CGMethodCall(CGThing):
             # Special case: we can just do a per-signature method call
             # here for our one signature and not worry about switching
             # on anything.
             signature = signatures[0]
             self.cgRoot = CGList([getPerSignatureCall(signature)])
             requiredArgs = requiredArgCount(signature)
 
             if requiredArgs > 0:
-                code = fill(  # BOGUS extra blank line
+                code = fill(
                     """
-
                     if (args.length() < ${requiredArgs}) {
                       return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
                     }
                     """,
                     requiredArgs=requiredArgs,
                     methodName=methodName)
                 self.cgRoot.prepend(CGGeneric(code))
             return
@@ -6699,39 +6680,42 @@ class CGMethodCall(CGThing):
 
         overloadCGThings = []
         overloadCGThings.append(
             CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
                       maxArgCount))
         overloadCGThings.append(
             CGSwitch("argcount",
                      argCountCases,
-                     # BOGUS extra blank line at end of default block
-                     CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' %
+                     CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n' %
                                methodName)))
         overloadCGThings.append(
             CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
                       'return false;\n'))
-        self.cgRoot = CGWrapper(CGList(overloadCGThings), pre="\n")
+        self.cgRoot = CGList(overloadCGThings)
 
     def define(self):
         return self.cgRoot.define()
 
 
 class CGGetterCall(CGPerSignatureCall):
     """
     A class to generate a native object getter call for a particular IDL
     getter.
     """
     def __init__(self, returnType, nativeMethodName, descriptor, attr):
         CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
                                     attr.isStatic(), descriptor, attr,
                                     getter=True)
 
 
+class FakeIdentifier():
+    def __init__(self, name):
+        self.name = name
+
 class FakeArgument():
     """
     A class that quacks like an IDLArgument.  This is used to make
     setters look like method calls or for special operations.
     """
     def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False):
         self.type = type
         self.optional = False
@@ -6741,20 +6725,17 @@ class FakeArgument():
         self.treatNullAs = interfaceMember.treatNullAs
         if isinstance(interfaceMember, IDLAttribute):
             self.enforceRange = interfaceMember.enforceRange
             self.clamp = interfaceMember.clamp
         else:
             self.enforceRange = False
             self.clamp = False
 
-        class FakeIdentifier():
-            def __init__(self):
-                self.name = name
-        self.identifier = FakeIdentifier()
+        self.identifier = FakeIdentifier(name)
 
     def allowTreatNonCallableAsNull(self):
         return self._allowTreatNonCallableAsNull
 
 
 class CGSetterCall(CGPerSignatureCall):
     """
     A class to generate a native object setter call for a particular IDL
@@ -7854,46 +7835,42 @@ class CGEnum(CGThing):
 
     def stringsNamespace(self):
         return self.enum.identifier.name + "Values"
 
     def nEnumStrings(self):
         return len(self.enum.values()) + 1
 
     def declare(self):
-        decl = fill(  # BOGUS extra newline at top
-            """
-
+        decl = fill(
+            """
             MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t)
               $*{enums}
               EndGuard_
             MOZ_END_ENUM_CLASS(${name})
             """,
             name=self.enum.identifier.name,
             enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n")
         strings = CGNamespace(self.stringsNamespace(),
                               CGGeneric(declare="extern const EnumEntry %s[%d];\n"
                                         % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())))
         return decl + "\n" + strings.declare()
 
     def define(self):
-        strings = fill(  # BOGUS extra newline at top
-            """
-
+        strings = fill(
+            """
             extern const EnumEntry ${name}[${count}] = {
               $*{entries}
               { nullptr, 0 }
             };
             """,
             name=ENUM_ENTRY_VARIABLE_NAME,
             count=self.nEnumStrings(),
             entries=''.join('{"%s", %d},\n' % (val, len(val))
                             for val in self.enum.values()))
-        # BOGUS - CGNamespace automatically indents; the extra indent() below causes
-        # the output to be indented 4 spaces.
         return CGNamespace(self.stringsNamespace(),
                            CGGeneric(define=indent(strings))).define()
 
     def deps(self):
         return self.enum.getDeps()
 
 
 def getUnionAccessorSignatureType(type, descriptorProvider):
@@ -8261,22 +8238,20 @@ class CGUnionStruct(CGThing):
                 body=CGSwitch("mType", toJSValCases,
                               default=CGGeneric("return false;\n")).define(),
                 const=True))
 
         constructors = [ctor]
         selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
         if self.ownsMembers:
             if traceCases:
-                # BOGUS blank line in default case
                 traceBody = CGSwitch("mType", traceCases,
-                                     default=CGGeneric("\n")).define()
-            else:
-                # BOGUS blank line in method
-                traceBody = "\n"
+                                     default=CGGeneric("")).define()
+            else:
+                traceBody = ""
             methods.append(ClassMethod("TraceUnion", "void",
                                        [Argument("JSTracer*", "trc")],
                                        body=traceBody))
             if CGUnionStruct.isUnionCopyConstructible(self.type):
                 constructors.append(
                     ClassConstructor(
                         [Argument("const %s&" % selfName, "aOther")],
                         bodyInHeader=True,
@@ -8711,17 +8686,17 @@ class ClassDestructor(ClassItem):
             ${decorators}
             ${className}::~${className}()
             {
               $*{body}
             }
             """,
             decorators=self.getDecorators(False),
             className=cgClass.getNameString(),
-            body=self.getBody() or "\n")  # BOGUS extra blank line if empty
+            body=self.getBody())
 
 
 class ClassMember(ClassItem):
     def __init__(self, name, type, visibility="private", static=False,
                  body=None):
         self.type = type
         self.static = static
         self.body = body
@@ -8953,18 +8928,17 @@ class CGResolveOwnProperty(CGAbstractSta
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::Handle<jsid>', 'id'),
                 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
                 ]
         CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty",
                                         "bool", args)
 
     def definition_body(self):
-        # BOGUS extra blank line at end of function
-        return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n"
+        return "return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n"
 
 
 class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod):
     """
     An implementation of Xray ResolveOwnProperty stuff for things that have a
     newresolve hook.
     """
     def __init__(self, descriptor):
@@ -9012,18 +8986,17 @@ class CGEnumerateOwnProperties(CGAbstrac
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'wrapper'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::AutoIdVector&', 'props')]
         CGAbstractStaticMethod.__init__(self, descriptor,
                                         "EnumerateOwnProperties", "bool", args)
 
     def definition_body(self):
-        # BOGUS extra newline
-        return "return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n"
+        return "return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n"
 
 
 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
     """
     An implementation of Xray EnumerateOwnProperties stuff for things
     that have a newresolve hook.
     """
     def __init__(self, descriptor):
@@ -9520,39 +9493,37 @@ class CGDOMJSProxyHandler_defineProperty
                 name=self.descriptor.name)
 
         if UseHolderForUnforgeable(self.descriptor):
             defineOnUnforgeable = ("bool hasUnforgeable;\n"
                                    "if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
                                    "  return false;\n"
                                    "}\n"
                                    "if (hasUnforgeable) {\n"
-                                   "  *defined = true;"  # SUPER BOGUS missing newline
+                                   "  *defined = true;\n"
                                    "  bool unused;\n"
                                    "  return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n"
-                                   "}\n"
-                                   "\n")  # BOGUS extra blank line at end of block or method
+                                   "}\n")
             set += CallOnUnforgeableHolder(self.descriptor,
                                            defineOnUnforgeable,
                                            "xpc::WrapperFactory::IsXrayWrapper(proxy)")
 
         namedSetter = self.descriptor.operations['NamedSetter']
         if namedSetter:
             if self.descriptor.operations['NamedCreator'] is not namedSetter:
                 raise TypeError("Can't handle creator that's different from the setter")
             # If we support indexed properties, we won't get down here for
             # indices, so we can just do our setter unconditionally here.
             set += fill(
                 """
                 *defined = true;
                 $*{callSetter}
 
                 return true;
-
-                """,  # BOGUS extra blank line at end of method
+                """,
                 callSetter=CGProxyNamedSetter(self.descriptor).define())
         else:
             if self.descriptor.supportsNamedProperties():
                 set += fill(
                     """
                     $*{presenceChecker}
 
                     if (found) {
@@ -9647,22 +9618,20 @@ class CGDOMJSProxyHandler_delete(ClassMe
         namedBody = getDeleterBody("Named")
         if namedBody is not None:
             # We always return above for an index id in the case when we support
             # indexed properties, so we can just treat the id as a name
             # unconditionally here.
             delete += (namedBody +
                        "if (found) {\n"
                        "  return true;\n"
-                       "}\n\n")  # BOGUS extra blank line
+                       "}\n")
             if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
                 delete = CGIfWrapper(CGGeneric(delete),
                                      "!HasPropertyOnPrototype(cx, proxy, id)").define()
-        else:
-            delete += "\n"  # BOGUS extra blank line
 
         delete += dedent("""
 
             return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);
             """)
 
         return delete
 
@@ -9785,18 +9754,17 @@ class CGDOMJSProxyHandler_hasOwn(ClassMe
 
         if self.descriptor.supportsNamedProperties():
             # If we support indexed properties we always return above for index
             # property names, so no need to check for those here.
             named = (CGProxyNamedPresenceChecker(self.descriptor).define() +
                      "\n" +
                      "*bp = found;\n")
             if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
-                # BOGUS extra blank line at end of block
-                named = CGIfWrapper(CGGeneric(named + "return true;\n\n"),
+                named = CGIfWrapper(CGGeneric(named + "return true;\n"),
                                     "!HasPropertyOnPrototype(cx, proxy, id)").define()
                 named += "*bp = false;\n"
             else:
                 named += "\n"
         else:
             named = "*bp = false;\n"
 
         return fill(
@@ -10470,17 +10438,39 @@ class CGNamespacedEnum(CGThing):
         self.node = curr
 
     def declare(self):
         return self.node.declare()
 
     def define(self):
         return ""
 
-
+def initIdsClassMethod(identifiers, atomCacheName):
+    idinit = ['!atomsCache->%s.init(cx, "%s")' %
+              (CGDictionary.makeIdName(id),
+               id)
+              for id in identifiers]
+    idinit.reverse()
+    body = fill(
+        """
+        MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
+
+        // Initialize these in reverse order so that any failure leaves the first one
+        // uninitialized.
+        if (${idinit}) {
+          return false;
+        }
+        return true;
+        """,
+        idinit=" ||\n    ".join(idinit))
+    return ClassMethod("InitIds", "bool", [
+        Argument("JSContext*", "cx"),
+        Argument("%s*" % atomCacheName, "atomsCache")
+    ], static=True, body=body, visibility="private")
+        
 class CGDictionary(CGThing):
     def __init__(self, dictionary, descriptorProvider):
         self.dictionary = dictionary
         self.descriptorProvider = descriptorProvider
         self.needToInitIds = len(dictionary.members) > 0
         self.memberInfo = [
             (member,
              getJSToNativeConversionInfo(
@@ -10635,65 +10625,40 @@ class CGDictionary(CGThing):
                 }
                 rval.set(JS::ObjectValue(*obj));
 
                 """)
 
         if self.memberInfo:
             body += "\n".join(self.getMemberDefinition(m).define()
                               for m in self.memberInfo)
-        else:
-            body += "\n"  # BOGUS extra blank line
         body += "\nreturn true;\n"
 
         return ClassMethod("ToObject", "bool", [
             Argument('JSContext*', 'cx'),
             Argument('JS::MutableHandle<JS::Value>', 'rval'),
         ], const=True, body=body)
 
     def initIdsMethod(self):
         assert self.needToInitIds
-        idinit = ['!atomsCache->%s.init(cx, "%s")' %
-                  (CGDictionary.makeIdName(m.identifier.name),
-                   m.identifier.name)
-                  for m in self.dictionary.members]
-        idinit.reverse()
-        body = fill(
-            """
-            MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
-
-            // Initialize these in reverse order so that any failure leaves the first one
-            // uninitialized.
-            if (${idinit}) {
-              return false;
-            }
-            return true;
-            """,
-            idinit=" ||\n    ".join(idinit))
-
-        return ClassMethod("InitIds", "bool", [
-            Argument("JSContext*", "cx"),
-            Argument("%sAtoms*" % self.makeClassName(self.dictionary),
-                     "atomsCache"),
-        ], static=True, body=body, visibility="private")
+        return initIdsClassMethod([m.identifier.name for m in self.dictionary.members],
+                                  "%sAtoms" % self.makeClassName(self.dictionary))             
 
     def traceDictionaryMethod(self):
         body = ""
         if self.dictionary.parent:
             cls = self.makeClassName(self.dictionary.parent)
             body += "%s::TraceDictionary(trc);\n" % cls
 
         memberTraces = [self.getMemberTrace(m)
                         for m in self.dictionary.members
                         if typeNeedsRooting(m.type)]
 
         if memberTraces:
             body += "\n".join(memberTraces)
-        else:
-            body += "\n"  # BOGUS extra newline
 
         return ClassMethod("TraceDictionary", "void", [
             Argument("JSTracer*", "trc"),
         ], body=body)
 
     def assignmentOperator(self):
         body = CGList([])
         if self.dictionary.parent:
@@ -10716,17 +10681,17 @@ class CGDictionary(CGThing):
             else:
                 memberAssign = CGGeneric(
                     "%s = aOther.%s;\n" % (memberName, memberName))
             body.append(memberAssign)
         return ClassMethod(
             "operator=", "void",
             [Argument("const %s&" % self.makeClassName(self.dictionary),
                       "aOther")],
-            body=body.define() or "\n")  # BOGUS blank line when empty
+            body=body.define())
 
     def getStructs(self):
         d = self.dictionary
         selfName = self.makeClassName(d)
         members = [ClassMember(self.makeMemberName(m[0].identifier.name),
                                self.getMemberType(m),
                                visibility="public",
                                body=self.getMemberInitializer(m))
@@ -11212,29 +11177,33 @@ class CGForwardDeclarations(CGWrapper):
 
         # Needed for at least Wrap.
         for d in descriptors:
             builder.add(d.nativeType)
 
         # We just about always need NativePropertyHooks
         builder.addInMozillaDom("NativePropertyHooks")
         builder.addInMozillaDom("ProtoAndIfaceCache")
+        # Add the atoms cache type, even if we don't need it.
+        for d in descriptors:
+            builder.add(d.nativeType + "Atoms", isStruct=True)
 
         for callback in mainCallbacks:
             forwardDeclareForType(callback)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='mainthreadonly')
 
         for callback in workerCallbacks:
             forwardDeclareForType(callback)
             for t in getTypesFromCallback(callback):
                 forwardDeclareForType(t, workerness='workeronly')
 
         for d in callbackInterfaces:
             builder.add(d.nativeType)
+            builder.add(d.nativeType + "Atoms", isStruct=True)
             for t in getTypesFromDescriptor(d):
                 forwardDeclareForType(t)
 
         for d in dictionaries:
             if len(d.members) > 0:
                 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
             for t in getTypesFromDictionary(d):
                 forwardDeclareForType(t)
@@ -11278,27 +11247,28 @@ class CGBindingRoot(CGThing):
         bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
         hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
                                                    workers=True)) != 0
         bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff
         bindingHeaders["nsThreadUtils.h"] = hasWorkerStuff
 
         dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
         bindingHeaders["nsCxPusher.h"] = dictionaries
-        bindingHeaders["AtomList.h"] = any(
+        hasNonEmptyDictionaries = any(
             len(dict.members) > 0 for dict in dictionaries)
         mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                             workers=False)
         workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                               workers=True)
         callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
                                                     isCallback=True)
         jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
                                               isJSImplemented=True)
         bindingHeaders["nsPIDOMWindow.h"] = jsImplemented
+        bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
 
         def addHeaderBasedOnTypes(header, typeChecker):
             bindingHeaders[header] = (
                 bindingHeaders.get(header, False) or
                 any(map(typeChecker,
                         getAllTypes(descriptors + callbackDescriptors,
                                     dictionaries,
                                     mainCallbacks + workerCallbacks))))
@@ -12564,17 +12534,17 @@ class CGCallback(CGClass):
         # For our public methods that needThisHandling we want most of the
         # same args and the same return type as what CallbackMember
         # generates.  So we want to take advantage of all its
         # CGNativeMember infrastructure, but that infrastructure can't deal
         # with templates and most especially template arguments.  So just
         # cheat and have CallbackMember compute all those things for us.
         realMethods = []
         for method in methods:
-            if not method.needThisHandling:
+            if not isinstance(method, CallbackMember) or not method.needThisHandling:
                 realMethods.append(method)
             else:
                 realMethods.extend(self.getMethodImpls(method))
         realMethods.append(
             ClassMethod("operator==", "bool",
                         [Argument("const %s&" % name, "aOther")],
                         inline=True, bodyInHeader=True,
                         const=True,
@@ -12704,23 +12674,31 @@ class CGCallbackInterface(CGCallback):
                    if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
         methods = [CallbackOperation(m, sig, descriptor) for m in methods
                    for sig in m.signatures()]
         if iface.isJSImplemented() and iface.ctor():
             sigs = descriptor.interface.ctor().signatures()
             if len(sigs) != 1:
                 raise TypeError("We only handle one constructor.  See bug 869268.")
             methods.append(CGJSImplInitOperation(sigs[0], descriptor))
+        if any(m.isAttr() or m.isMethod() for m in iface.members) or (iface.isJSImplemented() and iface.ctor()):
+            methods.append(initIdsClassMethod([descriptor.binaryNameFor(m.identifier.name)
+                                               for m in iface.members
+                                               if m.isAttr() or m.isMethod()] +
+                                              (["__init"] if iface.isJSImplemented() and iface.ctor() else []),
+                                              iface.identifier.name + "Atoms"))
         CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
                             methods, getters=getters, setters=setters)
 
 
 class FakeMember():
-    def __init__(self):
+    def __init__(self, name=None):
         self.treatNullAs = "Default"
+        if name is not None:
+            self.identifier = FakeIdentifier(name)
 
     def isStatic(self):
         return False
 
     def isAttr(self):
         return False
 
     def isMethod(self):
@@ -12821,30 +12799,26 @@ class CallbackMember(CGNativeMember):
                                         exceptionCode=self.exceptionCode,
                                         isCallbackReturnValue=isCallbackReturnValue,
                                         sourceDescription=sourceDescription),
             replacements)
         assignRetval = string.Template(
             self.getRetvalInfo(self.retvalType,
                                False)[2]).substitute(replacements)
         type = convertType.define()
-        if type == "":
-            type = "\n"  # BOGUS extra blank line
-        if assignRetval == "":
-            assignRetval = "\n"  # BOGUS extra blank line
         return type + assignRetval
 
     def getArgConversions(self):
         # Just reget the arglist from self.originalSig, because our superclasses
         # just have way to many members they like to clobber, so I can't find a
         # safe member name to store it in.
         argConversions = [self.getArgConversion(i, arg)
                           for i, arg in enumerate(self.originalSig[1])]
         if not argConversions:
-            return "\n\n"  # BOGUS extra blank line
+            return "\n"
 
         # Do them back to front, so our argc modifications will work
         # correctly, because we examine trailing arguments first.
         argConversions.reverse()
         # Wrap each one in a scope so that any locals it has don't leak out, and
         # also so that we can just "break;" for our successCode.
         argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
                                     pre="do {\n",
@@ -13067,22 +13041,25 @@ class CallbackOperationBase(CallbackMeth
             """)
 
     def getThisVal(self):
         return "thisValue"
 
     def getCallableDecl(self):
         getCallableFromProp = fill(
             """
-            if (!GetCallableProperty(cx, "${methodName}", &callable)) {
+            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+            if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+                !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
-            methodName=self.methodName,
+            methodAtomName=CGDictionary.makeIdName(self.methodName),
+            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
             errorReturn=self.getDefaultRetval())
         if not self.singleOperation:
             return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
         return fill(
             """
             bool isCallable = JS_ObjectIsCallable(cx, mCallback);
             JS::Rooted<JS::Value> callable(cx);
             if (isCallable) {
@@ -13141,22 +13118,25 @@ class CallbackGetter(CallbackAccessor):
 
     def getRvalDecl(self):
         return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
 
     def getCall(self):
         return fill(
             """
             JS::Rooted<JSObject *> callback(cx, mCallback);
-            if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) {
+            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+            if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+                !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
-            attrName=self.descriptorProvider.binaryNameFor(self.attrName),
+            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+            attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
             errorReturn=self.getDefaultRetval())
 
 
 class CallbackSetter(CallbackAccessor):
     def __init__(self, attr, descriptor):
         CallbackAccessor.__init__(self, attr,
                                   (BuiltinTypes[IDLBuiltinType.Types.void],
                                    [FakeArgument(attr.type, attr)]),
@@ -13166,22 +13146,25 @@ class CallbackSetter(CallbackAccessor):
     def getRvalDecl(self):
         # We don't need an rval
         return ""
 
     def getCall(self):
         return fill(
             """
             MOZ_ASSERT(argv.length() == 1);
-            if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv[0])) {
+            ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
+            if ((!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) ||
+                !JS_SetPropertyById(cx, CallbackPreserveColor(), atomsCache->${attrAtomName}, argv[0])) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
-            attrName=self.descriptorProvider.binaryNameFor(self.attrName),
+            atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
+            attrAtomName=CGDictionary.makeIdName(self.descriptorProvider.binaryNameFor(self.attrName)),
             errorReturn=self.getDefaultRetval())
 
     def getArgcDecl(self):
         return None
 
 
 class CGJSImplInitOperation(CallbackOperationBase):
     """
@@ -13206,31 +13189,48 @@ class GlobalGenRoots():
 
     @staticmethod
     def GeneratedAtomList(config):
         # Atom enum
         dictionaries = config.dictionaries
 
         structs = []
 
+        def memberToAtomCacheMember(binaryNameFor, m):
+            binaryMemberName = binaryNameFor(m.identifier.name)
+            return ClassMember(CGDictionary.makeIdName(binaryMemberName),
+                               "InternedStringId", visibility="public")
+        def buildAtomCacheStructure(idlobj, binaryNameFor, members):
+            classMembers = [memberToAtomCacheMember(binaryNameFor, m)
+                            for m in members]
+            structName = idlobj.identifier.name + "Atoms"
+            return (structName,
+                    CGWrapper(CGClass(structName,
+                                      bases=None,
+                                      isStruct=True,
+                                      members=classMembers), post='\n'))
+
         for dict in dictionaries:
-            dictMembers = dict.members
-            if len(dictMembers) == 0:
+            if len(dict.members) == 0:
                 continue
 
-            classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name),
-                                        "InternedStringId",
-                                        visibility="public") for m in dictMembers]
-
-            structName = dict.identifier.name + "Atoms"
-            structs.append((structName,
-                            CGWrapper(CGClass(structName,
-                                              bases=None,
-                                              isStruct=True,
-                                              members=classMembers), post='\n')))
+            structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members))
+
+        for d in (config.getDescriptors(isJSImplemented=True) +
+                  config.getDescriptors(isCallback=True)):
+            members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
+            if d.interface.isJSImplemented() and d.interface.ctor():
+                # We'll have an __init() method.
+                members.append(FakeMember('__init'))
+            if len(members) == 0:
+                continue
+
+            structs.append(buildAtomCacheStructure(d.interface,
+                                                   lambda x: d.binaryNameFor(x),
+                                                   members))
 
         structs.sort()
         generatedStructs = [struct for structName, struct in structs]
         structNames = [structName for structName, struct in structs]
 
         mainStruct = CGWrapper(CGClass("PerThreadAtomCache",
                                        bases=[ClassBase(structName) for structName in structNames],
                                        isStruct=True),
--- a/dom/bindings/OwningNonNull.h
+++ b/dom/bindings/OwningNonNull.h
@@ -38,16 +38,21 @@ public:
     return mPtr;
   }
 
   void operator=(T* aValue)
   {
     init(aValue);
   }
 
+  void operator=(T& aValue)
+  {
+    init(&aValue);
+  }
+
   void operator=(const already_AddRefed<T>& aValue)
   {
     init(aValue);
   }
 
   already_AddRefed<T> forget()
   {
 #ifdef DEBUG
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -195,17 +195,21 @@ private:
 
 namespace mozilla {
 namespace dom {
 
 TabParent* sEventCapturer;
 
 TabParent *TabParent::mIMETabParent = nullptr;
 
-NS_IMPL_ISUPPORTS(TabParent, nsITabParent, nsIAuthPromptProvider, nsISecureBrowserUI)
+NS_IMPL_ISUPPORTS(TabParent,
+                  nsITabParent,
+                  nsIAuthPromptProvider,
+                  nsISecureBrowserUI,
+                  nsISupportsWeakReference)
 
 TabParent::TabParent(ContentParent* aManager, const TabContext& aContext, uint32_t aChromeFlags)
   : TabContext(aContext)
   , mFrameElement(nullptr)
   , mIMESelectionAnchor(0)
   , mIMESelectionFocus(0)
   , mIMEComposing(false)
   , mIMECompositionEnding(false)
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -12,16 +12,17 @@
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "nsIXULBrowserWindow.h"
+#include "nsWeakReference.h"
 #include "Units.h"
 #include "js/TypeDecls.h"
 
 class nsFrameLoader;
 class nsIContent;
 class nsIPrincipal;
 class nsIURI;
 class nsIWidget;
@@ -45,16 +46,17 @@ class ClonedMessageData;
 class ContentParent;
 class Element;
 struct StructuredCloneData;
 
 class TabParent : public PBrowserParent 
                 , public nsITabParent 
                 , public nsIAuthPromptProvider
                 , public nsISecureBrowserUI
+                , public nsSupportsWeakReference
                 , public TabContext
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
 
 public:
     // nsITabParent
     NS_DECL_NSITABPARENT
--- a/embedding/base/nsIWindowCreator2.idl
+++ b/embedding/base/nsIWindowCreator2.idl
@@ -12,20 +12,21 @@
  * @see nsIWindowCreator
  * @see nsIWindowWatcher
  *
  * @status
  */
 
 #include "nsIWindowCreator.idl"
 
+interface nsITabParent;
 interface nsIURI;
 interface nsIWebBrowserChrome;
 
-[scriptable, uuid(f673ec81-a4b0-11d6-964b-eb5a2bf216fc)]
+[scriptable, uuid(e28f810b-9b49-4927-a4be-62a74fadfe21)]
 
 interface nsIWindowCreator2 : nsIWindowCreator {
 
   /**
    * Definitions for contextFlags
    */
 
   // Likely that the window is an advertising popup. 
@@ -39,20 +40,23 @@ interface nsIWindowCreator2 : nsIWindowC
                     to the underlying OS).
       @param chromeFlags Chrome features from nsIWebBrowserChrome
       @param contextFlags Flags about the context of the window being created.
       @param uri The URL for which this window is intended. It can be null
                  or zero-length. The implementation of this interface
                  may use the URL to help determine what sort of window
                  to open or whether to cancel window creation. It will not
                  load the URL.
+      @param aOpeningTab The TabParent that is trying to open this new chrome
+                         window. Can be nullptr.
       @param cancel Return |true| to reject window creation. If true the
                     implementation has determined the window should not
                     be created at all. The caller should not default
                     to any possible backup scheme for creating the window.
       @return the new window. Will be null if canceled or an error occurred.
   */
   nsIWebBrowserChrome createChromeWindow2(in nsIWebBrowserChrome parent,
                                           in uint32_t chromeFlags,
                                           in uint32_t contextFlags,
                                           in nsIURI uri,
+                                          in nsITabParent aOpeningTab,
                                           out boolean cancel);
 };
--- a/embedding/components/windowwatcher/public/nsPIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/public/nsPIWindowWatcher.idl
@@ -10,18 +10,19 @@
 
 #include "nsISupports.idl"
 
 interface nsIDOMWindow;
 interface nsISimpleEnumerator;
 interface nsIWebBrowserChrome;
 interface nsIDocShellTreeItem;
 interface nsIArray;
+interface nsITabParent;
 
-[uuid(00788A84-152F-4BD8-A814-FD8EB545DB29)]
+[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d2)]
 
 interface nsPIWindowWatcher : nsISupports
 {
   /** A window has been created. Add it to our list.
       @param aWindow the window to add
       @param aChrome the corresponding chrome window. The DOM window
                      and chrome will be mapped together, and the corresponding
                      chrome can be retrieved using the (not private)
@@ -46,31 +47,35 @@ interface nsPIWindowWatcher : nsISupport
       @param aName window name from JS window.open. can be null.  If a window
              with this name already exists, the openWindow call may just load
              aUrl in it (if aUrl is not null) and return it.
       @param aFeatures window features from JS window.open. can be null.
       @param aCalledFromScript true if we were called from script.
       @param aDialog use dialog defaults (see nsIDOMWindow::openDialog)
       @param aNavigate true if we should navigate the new window to the
              specified URL.
+      @param aOpeningTab the nsITabParent that is opening the new window. The
+                         nsITabParent is a remote tab belonging to aParent. Can
+                         be nullptr if this window is not being opened from a tab.
       @param aArgs Window argument
       @return the new window
 
       @note This method may examine the JS context stack for purposes of
             determining the security context to use for the search for a given
             window named aName.
       @note This method should try to set the default charset for the new
             window to the default charset of the document in the calling window
             (which is determined based on the JS stack and the value of
             aParent).  This is not guaranteed, however.
   */
   nsIDOMWindow openWindow2(in nsIDOMWindow aParent, in string aUrl,
                            in string aName, in string aFeatures,
                            in boolean aCalledFromScript, in boolean aDialog,
-                           in boolean aNavigate, in nsISupports aArgs);
+                           in boolean aNavigate, in nsITabParent aOpeningTab,
+                           in nsISupports aArgs);
 
   /**
    * Find a named docshell tree item amongst all windows registered
    * with the window watcher.  This may be a subframe in some window,
    * for example.
    *
    * @param aName the name of the window.  Must not be null.
    * @param aRequestor the tree item immediately making the request.
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -337,17 +337,17 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow
   uint32_t argc = 0;
   if (argv) {
     argv->GetLength(&argc);
   }
   bool dialog = (argc != 0);
 
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
                             /* calledFromJS = */ false, dialog,
-                            /* navigate = */ true, argv, _retval);
+                            /* navigate = */ true, nullptr, argv, _retval);
 }
 
 struct SizeSpec {
   SizeSpec() :
     mLeftSpecified(false),
     mTopSpecified(false),
     mOuterWidthSpecified(false),
     mOuterHeightSpecified(false),
@@ -389,16 +389,17 @@ struct SizeSpec {
 NS_IMETHODIMP
 nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent,
                               const char *aUrl,
                               const char *aName,
                               const char *aFeatures,
                               bool aCalledFromScript,
                               bool aDialog,
                               bool aNavigate,
+                              nsITabParent *aOpeningTab,
                               nsISupports *aArguments,
                               nsIDOMWindow **_retval)
 {
   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
 
   uint32_t argc = 0;
   if (argv) {
     argv->GetLength(&argc);
@@ -409,27 +410,28 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindo
   // called from script.  Fixing this is bug 779939.
   bool dialog = aDialog;
   if (!aCalledFromScript) {
     dialog = argc > 0;
   }
 
   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
                             aCalledFromScript, dialog,
-                            aNavigate, argv, _retval);
+                            aNavigate, aOpeningTab, argv, _retval);
 }
 
 nsresult
 nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
                                     const char *aUrl,
                                     const char *aName,
                                     const char *aFeatures,
                                     bool aCalledFromJS,
                                     bool aDialog,
                                     bool aNavigate,
+                                    nsITabParent *aOpeningTab,
                                     nsIArray *argv,
                                     nsIDOMWindow **_retval)
 {
   nsresult                        rv = NS_OK;
   bool                            nameSpecified,
                                   featuresSpecified,
                                   isNewToplevelWindow = false,
                                   windowIsNew = false,
@@ -693,17 +695,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
         }
 
         if (popupConditions)
           contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
 
         bool cancel = false;
         rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
                                                  contextFlags, uriToLoad,
-                                                 &cancel,
+                                                 aOpeningTab, &cancel,
                                                  getter_AddRefs(newChrome));
         if (NS_SUCCEEDED(rv) && cancel) {
           newChrome = 0; // just in case
           rv = NS_ERROR_ABORT;
         }
       }
       else
         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.h
@@ -10,16 +10,17 @@
 #define NS_WINDOWWATCHER_CID \
  {0xa21bfa01, 0xf349, 0x4394, {0xa8, 0x4c, 0x8d, 0xe5, 0xcf, 0x7, 0x37, 0xd0}}
 
 #include "nsCOMPtr.h"
 #include "mozilla/Mutex.h"
 #include "nsIWindowCreator.h" // for stupid compilers
 #include "nsIWindowWatcher.h"
 #include "nsIPromptFactory.h"
+#include "nsITabParent.h"
 #include "nsPIWindowWatcher.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 
 class  nsIURI;
 class  nsIDocShellTreeItem;
 class  nsIDocShellTreeOwner;
 class nsPIDOMWindow;
@@ -72,16 +73,17 @@ protected:
   // (which means called from script) or called via OpenWindow.
   nsresult OpenWindowInternal(nsIDOMWindow *aParent,
                               const char *aUrl,
                               const char *aName,
                               const char *aFeatures,
                               bool aCalledFromJS,
                               bool aDialog,
                               bool aNavigate,
+                              nsITabParent *aOpeningTab,
                               nsIArray *argv,
                               nsIDOMWindow **_retval);
 
   static JSContext *GetJSContextFromWindow(nsIDOMWindow *aWindow);
   static nsresult   URIfromURL(const char *aURL,
                                nsIDOMWindow *aParent,
                                nsIURI **aURI);
   
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -6,41 +6,46 @@
 #include "DrawTargetCairo.h"
 
 #include "SourceSurfaceCairo.h"
 #include "PathCairo.h"
 #include "HelpersCairo.h"
 #include "ScaledFontBase.h"
 #include "BorrowedContext.h"
 #include "FilterNodeSoftware.h"
+#include "mozilla/Scoped.h"
 
 #include "cairo.h"
 #include "cairo-tee.h"
 #include <string.h>
 
 #include "Blur.h"
 #include "Logging.h"
 #include "Tools.h"
 
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
 #include "cairo-quartz.h"
 #include <ApplicationServices/ApplicationServices.h>
 #endif
 
 #ifdef CAIRO_HAS_XLIB_SURFACE
 #include "cairo-xlib.h"
+#include "cairo-xlib-xrender.h"
 #endif
 
 #ifdef CAIRO_HAS_WIN32_SURFACE
 #include "cairo-win32.h"
 #endif
 
 #include <algorithm>
 
 namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
+
 namespace gfx {
 
 cairo_surface_t *DrawTargetCairo::mDummySurface;
 
 namespace {
 
 // An RAII class to prepare to draw a context and optional path. Saves and
 // restores the context on construction/destruction.
@@ -1052,19 +1057,116 @@ DrawTargetCairo::CreateSourceSurfaceFrom
   CopyDataToCairoSurface(surf, aData, aSize, aStride, BytesPerPixel(aFormat));
 
   RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
   cairo_surface_destroy(surf);
 
   return source_surf;
 }
 
+#ifdef CAIRO_HAS_XLIB_SURFACE
+static cairo_user_data_key_t gDestroyPixmapKey;
+
+struct DestroyPixmapClosure {
+  DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
+  ~DestroyPixmapClosure() {
+    XFreePixmap(DisplayOfScreen(mScreen), mPixmap);
+  }
+  Drawable mPixmap;
+  Screen *mScreen;
+};
+
+static void
+DestroyPixmap(void *data)
+{
+  delete static_cast<DestroyPixmapClosure*>(data);
+}
+#endif
+
 TemporaryRef<SourceSurface>
 DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
 {
+#ifdef CAIRO_HAS_XLIB_SURFACE
+  if (cairo_surface_get_type(mSurface) != CAIRO_SURFACE_TYPE_XLIB) {
+    return aSurface;
+  }
+
+  IntSize size = aSurface->GetSize();
+  if (!size.width || !size.height) {
+    return aSurface;
+  }
+
+  // Although the dimension parameters in the xCreatePixmapReq wire protocol are
+  // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
+  // either dimension cannot be represented by a 16-bit *signed* integer.
+  #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
+
+  if (size.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+      size.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+    return aSurface;
+  }
+
+  SurfaceFormat format = aSurface->GetFormat();
+  Screen *screen = cairo_xlib_surface_get_screen(mSurface);
+  Display *dpy = DisplayOfScreen(screen);
+  XRenderPictFormat* xrenderFormat = nullptr;
+  switch (format) {
+  case SurfaceFormat::B8G8R8A8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
+    break;
+  case SurfaceFormat::B8G8R8X8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardRGB24);
+    break;
+  case SurfaceFormat::A8:
+    xrenderFormat = XRenderFindStandardFormat(dpy, PictStandardA8);
+    break;
+  default:
+    return aSurface;
+  }
+  if (!xrenderFormat) {
+    return aSurface;
+  }
+
+  Drawable pixmap = XCreatePixmap(dpy, RootWindowOfScreen(screen),
+                                  size.width, size.height,
+                                  xrenderFormat->depth);
+  if (!pixmap) {
+    return aSurface;
+  }
+
+  ScopedDeletePtr<DestroyPixmapClosure> closure(
+    new DestroyPixmapClosure(pixmap, screen));
+
+  ScopedCairoSurface csurf(
+    cairo_xlib_surface_create_with_xrender_format(dpy, pixmap,
+                                                  screen, xrenderFormat,
+                                                  size.width, size.height));
+  if (!csurf || cairo_surface_status(csurf)) {
+    return aSurface;
+  }
+
+  cairo_surface_set_user_data(csurf, &gDestroyPixmapKey,
+                              closure.forget(), DestroyPixmap);
+
+  RefPtr<DrawTargetCairo> dt = new DrawTargetCairo();
+  if (!dt->Init(csurf, size, &format)) {
+    return aSurface;
+  }
+
+  dt->CopySurface(aSurface,
+                  IntRect(0, 0, size.width, size.height),
+                  IntPoint(0, 0));
+  dt->Flush();
+
+  RefPtr<SourceSurface> surf =
+    new SourceSurfaceCairo(csurf, size, format);
+
+  return surf;
+#endif
+
   return aSurface;
 }
 
 TemporaryRef<SourceSurface>
 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
   if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
     if (aSurface.mSize.width <= 0 ||
--- a/gfx/doc/MozSurface.md
+++ b/gfx/doc/MozSurface.md
@@ -9,59 +9,34 @@ We need to be able to safely and efficie
 MozSurface is a cross-process and backend-independent Surface API and not a stream API.
 
 ## Owner
 
 Nicolas Silva
 
 ## Definitions
 
-* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
-
 ## Use cases
 
 Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
 
 ## Requirement
 
 * It must be possible to efficiently share a MozSurface with a separate thread or process through IPDL
 * It must be possible to obtain read access a MozSurface on both the client and the host side at the same time.
 * The creation, update and destrution of surfaces must be safe and race-free. In particular, the ownership of the shared data must be clearly defined.
 * MozSurface must be a cross-backend/cross-platform abstraction that we will use on all of the supported platforms.
 * It must be possible to efficiently draw into a MozSurface using Moz2D.
 * While it should be possible to share MozSurfaces accross processes, it should not be limited to that. MozSurface should also be the preferred abstraction for use with surfaces that are not shared with the compositor process.
 
 ## TextureClient and TextureHost
 
-TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
-Inline documentation about TextureClient and TextureHost can be found in:
-
-* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
-* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
-
-TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
-
-A MozSurface containing data that is shared between a client process and a host process exists in the following form:
+TextureClient and TextureHost are the closest abstractions we currently have to MozSurface. The current plan is to evolve TextureClient into MozSurface. In its current state, TextureClient doesn't meet all the requirements and desisgn decisions of MozSurface yet.
 
-```
-                                 .
-            Client process       .      Host process
-                                 .
-     ________________      ______________      ______________
-    |                |    |              |    |              |
-    | TextureClient  +----+ <SharedData> +----+ TextureHost  |
-    |________________|    |______________|    |______________|
-                                 .
-                                 .
-                                 .
-    Figure 1) A Surface as seen by the client and the host processes
-```
-
-The above figure is a logical representation, not a class diagram.
-`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
+In particular, TextureClient/TextureHost are designed around cross-process sharing specifically. See the SharedMozSurface design document for more information about TextureClient and TextureHost.
 
 ## Locking semantics
 
 In order to access the shared surface data users of MozSurface must acquire and release a lock on the surface, specifying the open mode (read/write/read+write).
 
     bool Lock(OpenMode aMode);
     void Unlock();
 
@@ -104,125 +79,44 @@ While MozSurface handles memory manageme
 TODO: We need to figure this out. Right now we have a Gonk specific implementation, but no cross-platform abstraction/design.
 
 ## Ownership of the shared data
 
 MozSurface (TextureClient/TextureHost in its current form) defines ownership rules that depend on the configuration of the surface, in order to satisy efficiency and safety requirements.
 
 These rules rely on the fact that the underlying shared data is strictly owned by the MozSurface. This means that keeping direct references to the shared data is illegal and unsafe.
 
-## Deallocation protocol
-
-The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
-The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
-When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
-
-
-```
-           client side                .         host side
-                                      .
-    (A) Client: Send Remove     -.    .
-                                  \   .
-                                   \  .   ... can receive and send ...
-                                    \
-        Can receive                  `--> (B) Host: Receive Remove
-        Can't send                         |
-                                      .-- (C) Host: Send Delete
-                                     /
-                                    / .   ... can't receive nor send ...
-                                   /  .
-    (D) Client: Receive Delete <--'   .
-                                      .
-    Figure 2) MozSurface deallocation handshake
-```
-
-This handshake protocol is twofold:
-
-* It defines where and when it is possible to deallocate the shared data without races
-* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
-
-### Deallocating on the host side
-
-In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
-
-### Deallocating on the client side
-
-In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
-
-In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
-Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
-
-### Sharing state
-
-The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
-
-The sharing state of a MozSurface can be one of the following:
-
-* (1) Uninitialized (it doesn't have any shared data)
-* (2) Local (it isn't shared with the another thread/process)
-* (3) Shared (the state you would expect it to be most of the time)
-* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
-
-Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
-
-The deallocation protocol above, applies to the Shared state (3).
-In the other cases:
-
-* (1) Unitilialized: There is nothing to do.
-* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
-* (4) Invalid: There is nothing to do (deallocation has already happenned).
-
 ## Internal buffers / direct texturing
 
 Some MozSurface implementations use CPU-side shared memory to share the texture data accross processes, and require a GPU texture upload when interfacing with a TextureSource. In this case we say that the surface has an internal buffer (because it is implicitly equivalent to double buffering where the shared data is the back buffer and the GPU side texture is the front buffer). We also say that it doesn't do "direct texturing" meaning that we don't draw directly into the GPU-side texture.
 
 Examples:
 
  * Shmem MozSurface + OpenGL TextureSource: Has an internal buffer (no direct texturing)
  * Gralloc MozSurface + Gralloc TextureSource: No internal buffer (direct texturing)
 
 While direct texturing is usually the most efficient way, it is not always available depending on the platform and the required allocation size or format. Textures with internal buffers have less restrictions around locking since the host side will only need to read from the MozSurface once per update, meaning that we can often get away with single buffering where we would need double buffering with direct texturing.
 
 ## Alternative solutions
 
-### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
-
-The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
-
-Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
-
-We need a given surface to be accessible on both the client and host for the following reasons:
-
-* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
-* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
-* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
-
 ## Backends
 
 We have MozSurface implementaions (classes inheriting from TextureClient/TextureHost) for OpenGL, Software, D3D9, and D3D11 backends.
 Some implemtations can be used with any backend (ex. ShmemTextureClient/Host).
 
 ## Users of MozSurface
 
 MozSurface is the mechanism used by layers to share surfaces with the compositor, but it is not limited to layers. It should be used by anything that draws into a surface that may be shared with the compositor thread.
 
 ## Testing
 
 TODO - How can we make MozSurface more testable and what should we test?
 
 ## Future work
 
-### Rename TextureClient/TextureHost
-
-The current terminology is very confusing.
-
-### Unify TextureClient and TextureHost
-
-TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
-
 ### Using a MozSurface as a source for Drawing
 
 MozSurface should be able to expose a borrowed Moz2D SourceSurface that is valid between Lock and Unlock similarly to how it exposes a DrawTarget.
 
 ## Comparison with other APIs
 
 MozSurface is somewhat equivalent to Gralloc on Android/Gonk: it is a reference counted cross-process surface with locking semantics. While Gralloc can interface itself with OpenGL textures for compositing, MozSurface can interface itself to TextureSource objects.
 
new file mode 100644
--- /dev/null
+++ b/gfx/doc/SharedMozSurface.md
@@ -0,0 +1,147 @@
+Shared MozSurface {#mozsurface}
+==========
+
+**This document is work in progress.  Some information may be missing or incomplete.**
+
+Shared MozSurfaces represent an important use case of MozSurface, anything that is in the MozSurface design document also applies to shared MozSurfaces.
+
+## Goals
+
+We need to be able to safely and efficiently render web content into surfaces that may be shared accross processes.
+MozSurface is a cross-process and backend-independent Surface API and not a stream API.
+
+## Owner
+
+Nicolas Silva
+
+## Definitions
+
+* Client and Host: In Gecko's compositing architecture, the client process is the producer, while the host process is the consumer side, where compositing takes place.
+
+## Use cases
+
+Drawing web content into a surface and share it with the compositor process to display it on the screen without copies.
+
+## Requirement
+
+Shared MozSurfaces represent an important use case of MozSurface, it has the same requirements as MozSurface.
+
+## TextureClient and TextureHost
+
+TextureClient and TextureHost are the closest abstractions we currently have to MozSurface.
+Inline documentation about TextureClient and TextureHost can be found in:
+
+* [gfx/layers/client/TextureClient.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/client/TextureClient.h)
+* [gfx/layers/composite/TextureHost.h](http://dxr.mozilla.org/mozilla-central/source/gfx/layers/composite/TextureHost.h)
+
+TextureClient is the client-side handle on a MozSurface, while TextureHost is the equivalent host-side representation. There can only be one TextureClient for a given TextureHost, and one TextureHost for a given TextureClient. Likewise, there can only be one shared object for a given TextureClient/TextureHost pair.
+
+A MozSurface containing data that is shared between a client process and a host process exists in the following form:
+
+```
+                                 .
+            Client process       .      Host process
+                                 .
+     ________________      ______________      ______________
+    |                |    |              |    |              |
+    | TextureClient  +----+ <SharedData> +----+ TextureHost  |
+    |________________|    |______________|    |______________|
+                                 .
+                                 .
+                                 .
+    Figure 1) A Surface as seen by the client and the host processes
+```
+
+The above figure is a logical representation, not a class diagram.
+`<SharedData>` is a placeholder for whichever platform specific surface type we are sharing, for example a Gralloc buffer on Gonk or a D3D11 texture on Windows.
+
+## Deallocation protocol
+
+The shared data is accessible by both the client-side and the host-side of the MozSurface. A deallocation protocol must be defined to handle which side deallocates the data, and to ensure that it doesn't cause any race condition.
+The client side, which contains the web content's logic, always "decides" when a surface is needed or not. So the life time of a MozSurface is driven by the reference count of it's client-side handle (TextureClient).
+When a TextureClient's reference count reaches zero, a "Remove" message is sent in order to let the host side that the shared data is not accessible on the client side and that it si safe for it to be deleted. The host side responds with a "Delete" message.
+
+
+```
+           client side                .         host side
+                                      .
+    (A) Client: Send Remove     -.    .
+                                  \   .
+                                   \  .   ... can receive and send ...
+                                    \
+        Can receive                  `--> (B) Host: Receive Remove
+        Can't send                         |
+                                      .-- (C) Host: Send Delete
+                                     /
+                                    / .   ... can't receive nor send ...
+                                   /  .
+    (D) Client: Receive Delete <--'   .
+                                      .
+    Figure 2) MozSurface deallocation handshake
+```
+
+This handshake protocol is twofold:
+
+* It defines where and when it is possible to deallocate the shared data without races
+* It makes it impossible for asynchronous messages to race with the destruction of the MozSurface.
+
+### Deallocating on the host side
+
+In the common case, the shared data is deallocated asynchronously on the host side. In this case the deallocation takes place at the point (C) of figure 2.
+
+### Deallocating on the client side
+
+In some rare cases, for instance if the underlying implementation requires it, the shared data must be deallocated on the client side. In such cases, deallocation happens at the point (D) of figure 2.
+
+In some exceptional cases, this needs to happen synchronously, meaning that the client-side thread will block until the Delete message is received. This is supported but it is terrible for performance, so it should be avoided as much as possible.
+Currently this is needed when shutting down a hardware-decoded video stream with libstagefright on Gonk, because the libstagefright unfortunately assumes it has full ownership over the shared data (gralloc buffers) and crashes if there are still users of the buffers.
+
+### Sharing state
+
+The above deallocation protocol of a MozSurface applies to the common case that is when the surface is shared between two processes. A Surface can also be deallocated while it is not shared.
+
+The sharing state of a MozSurface can be one of the following:
+
+* (1) Uninitialized (it doesn't have any shared data)
+* (2) Local (it isn't shared with the another thread/process)
+* (3) Shared (the state you would expect it to be most of the time)
+* (4) Invalid (when for some rare cases we needed to force the deallocation of the shared data before the destruction of the TextureClient object).
+
+Surfaces can move from state N to state N+1 and be deallocated in any of these states. It could be possible to move from Shared to Local, but we currently don't have a use case for it.
+
+The deallocation protocol above, applies to the Shared state (3).
+In the other cases:
+
+* (1) Unitilialized: There is nothing to do.
+* (2) Local: The shared data is deallocated by the client side without need for a handshake, since it is not shared with other threads.
+* (4) Invalid: There is nothing to do (deallocation has already happenned).
+
+## Alternative solutions
+
+### Sending ownership back and forth between the client and host sides through message passing, intead of sharing.
+
+The current design of MozSurface makes the surface accessible from both sides at the same time, forcing us to do Locking and have a hand shake around deallocating the shared data, while using pure message passing and making the surface accessible only from one side at a time would avoid these complications.
+
+Using pure message passing was actually the first approach we tried when we created the first version of TextureClient and TextureHost. This strategy failed in several places, partly because of some legacy in Gecko's architecture, and partly because of some of optimizations we do to avoid copying surfaces.
+
+We need a given surface to be accessible on both the client and host for the following reasons:
+
+* Gecko can at any time require read access on the client side to a surface that is shared with the host process, for example to build a temporary layer manager and generate a screenshot. This is mostly a legacy problem.
+* We do some copy-on-write optimizations on surfaces that are shared with the compositor in order to keep invalid regions as small as possible. Out tiling implementation is an example of that.
+* Our buffer rotation code on scrollable non-tiled layers also requires a synchronization on the client side between the front and back buffers, while the front buffer is used on the host side.
+
+## Testing
+
+TODO - How can we make shared MozSurfaces more testable and what should we test?
+
+## Future work
+
+### Rename TextureClient/TextureHost
+
+The current terminology is very confusing.
+
+### Unify TextureClient and TextureHost
+
+TextureClient and TextureHost should live under a common interface to better hide the IPC details. The base classe should only expose the non-ipc related methods such as Locking, access through a DrawTarget, access to a TextureSource.
+
+## Comparison with other APIs
--- a/gfx/layers/ipc/CompositorBench.cpp
+++ b/gfx/layers/ipc/CompositorBench.cpp
@@ -10,16 +10,20 @@
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/Effects.h"
 #include "mozilla/TimeStamp.h"
 #include "gfxColor.h"
 #include "gfxPrefs.h"
 #include <math.h>
 #include "GeckoProfiler.h"
 
+#ifdef MOZ_WIDGET_GONK
+#include "mozilla/layers/GrallocTextureHost.h"
+#endif
+
 #define TEST_STEPS 1000
 #define DURATION_THRESHOLD 30
 #define THRESHOLD_ABORT_COUNT 5
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
@@ -41,16 +45,55 @@ public:
   virtual void Teardown(Compositor* aCompositor) {}
   virtual void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) = 0;
 
   const char* ToString() { return mTestName; }
 private:
   const char* mTestName;
 };
 
+static void
+DrawFrameTrivialQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects) {
+  for (size_t i = 0; i < aStep * 10; i++) {
+    const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
+                                      (int)(i / aScreenRect.height),
+                                      1, 1);
+    const gfx::Rect& clipRect = aScreenRect;
+
+    float opacity = 1.f;
+
+    gfx::Matrix transform2d;
+
+    gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+    aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+  }
+}
+
+static void
+DrawFrameStressQuad(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep, const EffectChain& effects)
+{
+  for (size_t i = 0; i < aStep * 10; i++) {
+    const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
+                                      aScreenRect.height * SimplePseudoRandom(i, 1),
+                                      aScreenRect.width * SimplePseudoRandom(i, 2),
+                                      aScreenRect.height * SimplePseudoRandom(i, 3));
+    const gfx::Rect& clipRect = aScreenRect;
+
+    float opacity = 1.f;
+
+    gfx::Matrix transform2d;
+    transform2d = transform2d.Rotate(SimplePseudoRandom(i, 4) * 70.f);
+
+    gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
+
+    aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
+  }
+}
+
 class EffectSolidColorBench : public BenchTest {
 public:
   EffectSolidColorBench()
     : BenchTest("EffectSolidColorBench (clear frame with EffectSolidColor)")
   {}
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
     float red;
@@ -75,76 +118,58 @@ public:
 
 class EffectSolidColorTrivialBench : public BenchTest {
 public:
   EffectSolidColorTrivialBench()
     : BenchTest("EffectSolidColorTrivialBench (10s 1x1 EffectSolidColor)")
   {}
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
-    for (size_t i = 0; i < aStep * 10; i++) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  TemporaryRef<Effect> CreateEffect(size_t i) {
       float red;
       float tmp;
       red = modf(i * 0.03f, &tmp);
       EffectChain effects;
       gfxRGBA color(red, 0.4f, 0.4f, 1.0f);
-      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r,
-                                                               color.g,
-                                                               color.b,
-                                                               color.a));
-
-      const gfx::Rect& rect = gfx::Rect(i % (int)aScreenRect.width,
-                                        (int)(i / aScreenRect.height),
-                                        1, 1);
-      const gfx::Rect& clipRect = aScreenRect;
-
-      float opacity = 1.f;
-
-      gfx::Matrix transform2d;
-
-      gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
-
-      aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
-    }
+      return new EffectSolidColor(gfx::Color(color.r,
+                                             color.g,
+                                             color.b,
+                                             color.a));
   }
 };
 
 class EffectSolidColorStressBench : public BenchTest {
 public:
   EffectSolidColorStressBench()
     : BenchTest("EffectSolidColorStressBench (10s various EffectSolidColor)")
   {}
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
-    for (size_t i = 0; i < aStep * 10; i++) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  TemporaryRef<Effect> CreateEffect(size_t i) {
       float red;
       float tmp;
       red = modf(i * 0.03f, &tmp);
       EffectChain effects;
       gfxRGBA color(red, 0.4f, 0.4f, 1.0f);
-      effects.mPrimaryEffect = new EffectSolidColor(gfx::Color(color.r,
-                                                               color.g,
-                                                               color.b,
-                                                               color.a));
-
-      const gfx::Rect& rect = gfx::Rect(aScreenRect.width * SimplePseudoRandom(i, 0),
-                                        aScreenRect.height * SimplePseudoRandom(i, 1),
-                                        aScreenRect.width * SimplePseudoRandom(i, 2),
-                                        aScreenRect.height * SimplePseudoRandom(i, 3));
-      const gfx::Rect& clipRect = aScreenRect;
-
-      float opacity = 0.3f;
-
-      gfx::Matrix transform2d;
-      transform2d = transform2d.Rotate(SimplePseudoRandom(i, 4) * 70.f);
-
-      gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
-
-      aCompositor->DrawQuad(rect, clipRect, effects, opacity, transform);
-    }
+      return new EffectSolidColor(gfx::Color(color.r,
+                                             color.g,
+                                             color.b,
+                                             color.a));
   }
 };
 
 class UploadBench : public BenchTest {
 public:
   UploadBench()
     : BenchTest("Upload Bench (10s 256x256 upload)")
   {}
@@ -172,24 +197,204 @@ public:
 
   void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
     for (size_t i = 0; i < aStep * 10; i++) {
       mTexture->Update(mSurface);
     }
   }
 };
 
+class TrivialTexturedQuadBench : public BenchTest {
+public:
+  TrivialTexturedQuadBench()
+    : BenchTest("Trvial Textured Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  RefPtr<DataSourceSurface> mSurface;
+  RefPtr<DataTextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    int bytesPerPixel = 4;
+    size_t w = 256;
+    size_t h = 256;
+    mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+    for (size_t i = 0; i < w * h; i++) {
+      mBuf[i] = 0xFF00008F;
+    }
+
+    mSurface = Factory::CreateWrappingDataSourceSurface(
+      reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+    mTexture = aCompositor->CreateDataTextureSource();
+    mTexture->Update(mSurface);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mSurface = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+
+class StressTexturedQuadBench : public BenchTest {
+public:
+  StressTexturedQuadBench()
+    : BenchTest("Stress Textured Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  RefPtr<DataSourceSurface> mSurface;
+  RefPtr<DataTextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    int bytesPerPixel = 4;
+    size_t w = 256;
+    size_t h = 256;
+    mBuf = (uint32_t *) malloc(w * h * sizeof(uint32_t));
+    for (size_t i = 0; i < w * h; i++) {
+      mBuf[i] = 0xFF00008F;
+    }
+
+    mSurface = Factory::CreateWrappingDataSourceSurface(
+      reinterpret_cast<uint8_t*>(mBuf), w * bytesPerPixel, IntSize(w, h), SurfaceFormat::B8G8R8A8);
+    mTexture = aCompositor->CreateDataTextureSource();
+    mTexture->Update(mSurface);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mSurface = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  virtual TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+
+#ifdef MOZ_WIDGET_GONK
+class TrivialGrallocQuadBench : public BenchTest {
+public:
+  TrivialGrallocQuadBench()
+    : BenchTest("Travial Gralloc Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  android::sp<android::GraphicBuffer> mGralloc;
+  RefPtr<TextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    mBuf = nullptr;
+    int w = 256;
+    int h = 256;
+    int32_t format = android::PIXEL_FORMAT_RGBA_8888;;
+    mGralloc = new android::GraphicBuffer(w, h,
+                                 format,
+                                 android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                 android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                                 android::GraphicBuffer::USAGE_HW_TEXTURE);
+    mTexture = new mozilla::layers::GrallocTextureSourceOGL((CompositorOGL*)aCompositor, mGralloc.get(), SurfaceFormat::B8G8R8A8);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameTrivialQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mGralloc = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  virtual TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+
+class StressGrallocQuadBench : public BenchTest {
+public:
+  StressGrallocQuadBench()
+    : BenchTest("Stress Gralloc Quad (10s 256x256 quads)")
+  {}
+
+  uint32_t* mBuf;
+  android::sp<android::GraphicBuffer> mGralloc;
+  RefPtr<TextureSource> mTexture;
+
+  virtual void Setup(Compositor* aCompositor, size_t aStep) {
+    mBuf = nullptr;
+    int w = 256;
+    int h = 256;
+    int32_t format = android::PIXEL_FORMAT_RGBA_8888;;
+    mGralloc = new android::GraphicBuffer(w, h,
+                                 format,
+                                 android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                 android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                                 android::GraphicBuffer::USAGE_HW_TEXTURE);
+    mTexture = new mozilla::layers::GrallocTextureSourceOGL((CompositorOGL*)aCompositor, mGralloc.get(), SurfaceFormat::B8G8R8A8);
+  }
+
+  void DrawFrame(Compositor* aCompositor, const gfx::Rect& aScreenRect, size_t aStep) {
+    EffectChain effects;
+    effects.mPrimaryEffect = CreateEffect(aStep);
+
+    DrawFrameStressQuad(aCompositor, aScreenRect, aStep, effects);
+  }
+
+  virtual void Teardown(Compositor* aCompositor) {
+    mGralloc = nullptr;
+    mTexture = nullptr;
+    free(mBuf);
+  }
+
+  virtual TemporaryRef<Effect> CreateEffect(size_t i) {
+    RefPtr<TexturedEffect> effect = CreateTexturedEffect(SurfaceFormat::B8G8R8A8, mTexture, Filter::POINT);
+    return effect;
+  }
+};
+#endif
+
 static void RunCompositorBench(Compositor* aCompositor, const gfx::Rect& aScreenRect)
 {
   std::vector<BenchTest*> tests;
 
   tests.push_back(new EffectSolidColorBench());
+  tests.push_back(new UploadBench());
   tests.push_back(new EffectSolidColorTrivialBench());
   tests.push_back(new EffectSolidColorStressBench());
-  tests.push_back(new UploadBench());
+  tests.push_back(new TrivialTexturedQuadBench());
+  tests.push_back(new StressTexturedQuadBench());
+#ifdef MOZ_WIDGET_GONK
+  tests.push_back(new TrivialGrallocQuadBench());
+  tests.push_back(new StressGrallocQuadBench());
+#endif
 
   for (size_t i = 0; i < tests.size(); i++) {
     BenchTest* test = tests[i];
     std::vector<TimeDuration> results;
     int testsOverThreshold = 0;
     PROFILER_MARKER(test->ToString());
     for (size_t j = 0; j < TEST_STEPS; j++) {
       test->Setup(aCompositor, j);
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -192,17 +192,17 @@ OptimalFillOperator()
 // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
 // the subimage of pixels we're allowed to sample.
 static already_AddRefed<gfxDrawable>
 CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
                                  gfxContext* aContext,
                                  const gfxMatrix& aUserSpaceToImageSpace,
                                  const gfxRect& aSourceRect,
                                  const gfxRect& aSubimage,
-                                 const gfxImageFormat aFormat)
+                                 const SurfaceFormat aFormat)
 {
     PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
       js::ProfileEntry::Category::GRAPHICS);
 
     gfxRect userSpaceClipExtents = aContext->GetClipExtents();
     // This isn't optimal --- if aContext has a rotation then GetClipExtents
     // will have to do a bounding-box computation, and TransformBounds might
     // too, so we could get a better result if we computed image space clip
@@ -228,19 +228,19 @@ CreateSamplingRestrictedDrawable(gfxDraw
     nsRefPtr<gfxDrawable> drawable;
     gfxIntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
 
     nsRefPtr<gfxImageSurface> image = aDrawable->GetAsImageSurface();
     if (image && gfxRect(0, 0, image->GetSize().width, image->GetSize().height).Contains(needed)) {
       nsRefPtr<gfxASurface> temp = image->GetSubimage(needed);
       drawable = new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.TopLeft()));
     } else {
-      mozilla::RefPtr<mozilla::gfx::DrawTarget> target =
+      RefPtr<DrawTarget> target =
         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(ToIntSize(size),
-                                                                     ImageFormatToSurfaceFormat(aFormat));
+                                                                     aFormat);
       if (!target) {
         return nullptr;
       }
 
       nsRefPtr<gfxContext> tmpCtx = new gfxContext(target);
       tmpCtx->SetOperator(OptimalFillOperator());
       aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true,
                       GraphicsFilter::FILTER_FAST, gfxMatrix().Translate(needed.TopLeft()));
@@ -398,17 +398,17 @@ static GraphicsFilter ReduceResamplingFi
 /* static */ void
 gfxUtils::DrawPixelSnapped(gfxContext*      aContext,
                            gfxDrawable*     aDrawable,
                            const gfxMatrix& aUserSpaceToImageSpace,
                            const gfxRect&   aSubimage,
                            const gfxRect&   aSourceRect,
                            const gfxRect&   aImageRect,
                            const gfxRect&   aFill,
-                           const gfxImageFormat aFormat,
+                           const SurfaceFormat aFormat,
                            GraphicsFilter aFilter,
                            uint32_t         aImageFlags)
 {
     PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
       js::ProfileEntry::Category::GRAPHICS);
 
     bool doTile = !aImageRect.Contains(aSourceRect) &&
                   !(aImageFlags & imgIContainer::FLAG_CLAMP);
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -60,17 +60,17 @@ public:
      */
     static void DrawPixelSnapped(gfxContext*      aContext,
                                  gfxDrawable*     aDrawable,
                                  const gfxMatrix& aUserSpaceToImageSpace,
                                  const gfxRect&   aSubimage,
                                  const gfxRect&   aSourceRect,
                                  const gfxRect&   aImageRect,
                                  const gfxRect&   aFill,
-                                 const gfxImageFormat aFormat,
+                                 const mozilla::gfx::SurfaceFormat aFormat,
                                  GraphicsFilter aFilter,
                                  uint32_t         aImageFlags = imgIContainer::FLAG_NONE);
 
     /**
      * Clip aContext to the region aRegion.
      */
     static void ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion);
 
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -154,21 +154,21 @@ void nsGIFDecoder2::BeginGIF()
   mGIFOpen = true;
 
   PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
 }
 
 //******************************************************************************
 void nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
 {
-  gfxImageFormat format;
+  gfx::SurfaceFormat format;
   if (mGIFStruct.is_transparent)
-    format = gfxImageFormat::ARGB32;
+    format = gfx::SurfaceFormat::B8G8R8A8;
   else
-    format = gfxImageFormat::RGB24;
+    format = gfx::SurfaceFormat::B8G8R8X8;
 
   MOZ_ASSERT(HasSize());
 
   // Use correct format, RGB for first frame, PAL for following frames
   // and include transparency to allow for optimization of opaque images
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette
     NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
@@ -183,17 +183,17 @@ void nsGIFDecoder2::BeginImageFrame(uint
                                                                 mGIFStruct.width,
                                                                 mGIFStruct.height))) {
     // Regardless of depth of input, image is decoded into 24bit RGB
     NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
                  mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
                  format);
   } else {
     // Our preallocated frame matches up, with the possible exception of alpha.
-    if (format == gfxImageFormat::RGB24) {
+    if (format == gfx::SurfaceFormat::B8G8R8X8) {
       GetCurrentFrame()->SetHasNoAlpha();
     }
   }
 
   mCurrentFrame = mGIFStruct.images_decoded;
 }
 
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -135,27 +135,27 @@ nsPNGDecoder::~nsPNGDecoder()
     if (mTransform)
       qcms_transform_release(mTransform);
   }
 }
 
 // CreateFrame() is used for both simple and animated images
 void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                                int32_t width, int32_t height,
-                               gfxImageFormat format)
+                               gfx::SurfaceFormat format)
 {
   // Our first full frame is automatically created by the image decoding
   // infrastructure. Just use it as long as it matches up.
   MOZ_ASSERT(HasSize());
   if (mNumFrames != 0 ||
       !GetCurrentFrame()->GetRect().IsEqualEdges(nsIntRect(x_offset, y_offset, width, height))) {
     NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
   } else if (mNumFrames == 0) {
     // Our preallocated frame matches up, with the possible exception of alpha.
-    if (format == gfxImageFormat::RGB24) {
+    if (format == gfx::SurfaceFormat::B8G8R8X8) {
       GetCurrentFrame()->SetHasNoAlpha();
     }
   }
 
   mFrameRect.x = x_offset;
   mFrameRect.y = y_offset;
   mFrameRect.width = width;
   mFrameRect.height = height;
@@ -624,19 +624,19 @@ nsPNGDecoder::info_callback(png_structp 
       }
     } else {
       alpha_bits = 8;
     }
   }
 #endif
 
   if (channels == 1 || channels == 3)
-    decoder->format = gfxImageFormat::RGB24;
+    decoder->format = gfx::SurfaceFormat::B8G8R8X8;
   else if (channels == 2 || channels == 4)
-    decoder->format = gfxImageFormat::ARGB32;
+    decoder->format = gfx::SurfaceFormat::B8G8R8A8;
 
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
     png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
                                  nullptr);
 
   if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
     decoder->mFrameIsHidden = true;
@@ -740,17 +740,17 @@ nsPNGDecoder::row_callback(png_structp p
         }
         line = decoder->mCMSLine;
       } else {
         qcms_transform_data(decoder->mTransform, line, line, iwidth);
        }
      }
 
     switch (decoder->format) {
-      case gfxImageFormat::RGB24:
+      case gfx::SurfaceFormat::B8G8R8X8:
       {
         // counter for while() loops below
         uint32_t idx = iwidth;
 
         // copy as bytes until source pointer is 32-bit-aligned
         for (; (NS_PTR_TO_UINT32(line) & 0x3) && idx; --idx) {
           *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]);
           line += 3;
@@ -767,17 +767,17 @@ nsPNGDecoder::row_callback(png_structp p
         // copy remaining pixel(s)
         while (idx--) {
           // 32-bit read of final pixel will exceed buffer, so read bytes
           *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]);
           line += 3;
         }
       }
       break;
-      case gfxImageFormat::ARGB32:
+      case gfx::SurfaceFormat::B8G8R8A8:
       {
         if (!decoder->mDisablePremultipliedAlpha) {
           for (uint32_t x=width; x>0; --x) {
             *cptr32++ = gfxPackedPixel(line[3], line[0], line[1], line[2]);
             if (line[3] != 0xff)
               rowHasNoAlpha = false;
             line += 4;
           }
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -28,17 +28,17 @@ public:
   virtual ~nsPNGDecoder();
 
   virtual void InitInternal();
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy);
   virtual Telemetry::ID SpeedHistogram();
 
   void CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                    int32_t width, int32_t height,
-                   gfxImageFormat format);
+                   gfx::SurfaceFormat format);
   void EndImageFrame();
 
   // Check if PNG is valid ICO (32bpp RGBA)
   // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
   bool IsValidICO() const
   {
     // If there are errors in the call to png_get_IHDR, the error_callback in
     // nsPNGDecoder.cpp is called.  In this error callback we do a longjmp, so
@@ -71,17 +71,17 @@ public:
   png_structp mPNG;
   png_infop mInfo;
   nsIntRect mFrameRect;
   uint8_t *mCMSLine;
   uint8_t *interlacebuf;
   qcms_profile *mInProfile;
   qcms_transform *mTransform;
 
-  gfxImageFormat format;
+  gfx::SurfaceFormat format;
 
   // For size decodes
   uint8_t mSizeBytes[8]; // Space for width and height, both 4 bytes
   uint32_t mHeaderBytesRead;
 
   // whether CMS or premultiplied alpha are forced off
   uint32_t mCMSMode;
 
--- a/image/src/ClippedImage.cpp
+++ b/image/src/ClippedImage.cpp
@@ -250,17 +250,17 @@ ClippedImage::GetFrameInternal(const nsI
       new DrawSingleTileCallback(this, mClip, aViewportSize, aSVGContext, aWhichFrame, aFlags);
     nsRefPtr<gfxDrawable> drawable =
       new gfxCallbackDrawable(drawTileCallback, mClip.Size());
 
     // Actually draw. The callback will end up invoking DrawSingleTile.
     gfxRect imageRect(0, 0, mClip.width, mClip.height);
     gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
                                imageRect, imageRect, imageRect, imageRect,
-                               gfxImageFormat::ARGB32,
+                               SurfaceFormat::B8G8R8A8,
                                GraphicsFilter::FILTER_FAST);
 
     // Cache the resulting surface.
     mCachedSurface = new ClippedImageCachedSurface(target->Snapshot(),
                                                    aViewportSize,
                                                    aSVGContext,
                                                    frameToDraw,
                                                    aFlags);
@@ -335,17 +335,17 @@ ClippedImage::Draw(gfxContext* aContext,
     nsRefPtr<gfxSurfaceDrawable> drawable =
       new gfxSurfaceDrawable(surface, gfxIntSize(mClip.width, mClip.height));
 
     // Draw.
     gfxRect imageRect(0, 0, mClip.width, mClip.height);
     gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
     gfxUtils::DrawPixelSnapped(aContext, drawable, aUserSpaceToImageSpace,
                                subimage, sourceRect, imageRect, aFill,
-                               gfxImageFormat::ARGB32, aFilter);
+                               SurfaceFormat::B8G8R8A8, aFilter);
 
     return NS_OK;
   }
 
   // Determine the appropriate subimage for the inner image.
   nsIntRect innerSubimage(aSubimage);
   innerSubimage.MoveBy(mClip.x, mClip.y);
   innerSubimage.Intersect(mClip);
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -198,18 +198,16 @@ Decoder::FinishSharedDecoder()
 }
 
 nsresult
 Decoder::AllocateFrame()
 {
   MOZ_ASSERT(mNeedsNewFrame);
   MOZ_ASSERT(NS_IsMainThread());
 
-  MarkFrameDirty();
-
   nsresult rv;
   imgFrame* frame = nullptr;
   if (mNewFrameData.mPaletteDepth) {
     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
                             mNewFrameData.mHeight, mNewFrameData.mFormat,
                             mNewFrameData.mPaletteDepth,
                             &mImageData, &mImageDataLength,
@@ -416,33 +414,23 @@ Decoder::PostDecoderError(nsresult aFail
   // XXXbholley - we should report the image URI here, but imgContainer
   // needs to know its URI first
   NS_WARNING("Image decoding error - This is probably a bug!");
 }
 
 void
 Decoder::NeedNewFrame(uint32_t framenum, uint32_t x_offset, uint32_t y_offset,
                       uint32_t width, uint32_t height,
-                      gfxImageFormat format,
+                      gfx::SurfaceFormat format,
                       uint8_t palette_depth /* = 0 */)
 {
   // Decoders should never call NeedNewFrame without yielding back to Write().
   MOZ_ASSERT(!mNeedsNewFrame);
 
   // We don't want images going back in time or skipping frames.
   MOZ_ASSERT(framenum == mFrameCount || framenum == (mFrameCount - 1));
 
   mNewFrameData = NewFrameData(framenum, x_offset, y_offset, width, height, format, palette_depth);
   mNeedsNewFrame = true;
 }
 
-void
-Decoder::MarkFrameDirty()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mCurrentFrame) {
-    mCurrentFrame->ApplyDirtToSurfaces();
-  }
-}
-
 } // namespace image
 } // namespace mozilla
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -146,29 +146,25 @@ public:
   // is created as an ARGB frame with no offset and with size width * height.
   // If decoders need something different, they must ask for it.
   // This is called by decoders when they need a new frame. These decoders
   // must then save the data they have been sent but not yet processed and
   // return from WriteInternal. When the new frame is created, WriteInternal
   // will be called again with nullptr and 0 as arguments.
   void NeedNewFrame(uint32_t frameNum, uint32_t x_offset, uint32_t y_offset,
                     uint32_t width, uint32_t height,
-                    gfxImageFormat format,
+                    gfx::SurfaceFormat format,
                     uint8_t palette_depth = 0);
 
   virtual bool NeedsNewFrame() const { return mNeedsNewFrame; }
 
   // Try to allocate a frame as described in mNewFrameData and return the
   // status code from that attempt. Clears mNewFrameData.
   virtual nsresult AllocateFrame();
 
-  // Called when a chunk of decoding has been done and the frame needs to be
-  // marked as dirty. Must be called only on the main thread.
-  void MarkFrameDirty();
-
   imgFrame* GetCurrentFrame() const { return mCurrentFrame; }
 
 protected:
 
   /*
    * Internal hooks. Decoder implementations may override these and
    * only these methods.
    */
@@ -245,31 +241,31 @@ private:
 
   struct NewFrameData
   {
     NewFrameData()
     {}
 
     NewFrameData(uint32_t num, uint32_t offsetx, uint32_t offsety,
                  uint32_t width, uint32_t height,
-                 gfxImageFormat format, uint8_t paletteDepth)
+                 gfx::SurfaceFormat format, uint8_t paletteDepth)
       : mFrameNum(num)
       , mOffsetX(offsetx)
       , mOffsetY(offsety)
       , mWidth(width)
       , mHeight(height)
       , mFormat(format)
       , mPaletteDepth(paletteDepth)
     {}
     uint32_t mFrameNum;
     uint32_t mOffsetX;
     uint32_t mOffsetY;
     uint32_t mWidth;
     uint32_t mHeight;
-    gfxImageFormat mFormat;
+    gfx::SurfaceFormat mFormat;
     uint8_t mPaletteDepth;
   };
   NewFrameData mNewFrameData;
   bool mNeedsNewFrame;
   bool mInitialized;
   bool mSizeDecode;
   bool mInFrame;
   bool mIsAnimated;
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -256,17 +256,17 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   }
 
   bool needToBlankComposite = false;
 
   // Create the Compositing Frame
   if (!mAnim->compositingFrame) {
     mAnim->compositingFrame.SetFrame(new imgFrame());
     nsresult rv = mAnim->compositingFrame->Init(0, 0, mSize.width, mSize.height,
-                                                gfxImageFormat::ARGB32);
+                                                gfx::SurfaceFormat::B8G8R8A8);
     if (NS_FAILED(rv)) {
       mAnim->compositingFrame.SetFrame(nullptr);
       return false;
     }
     mAnim->compositingFrame.LockAndGetData();
     needToBlankComposite = true;
   } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
 
@@ -386,17 +386,17 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
       (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
     // We are storing the whole image.
     // It would be better if we just stored the area that nextFrame is going to
     // overwrite.
     if (!mAnim->compositingPrevFrame) {
       mAnim->compositingPrevFrame.SetFrame(new imgFrame());
       nsresult rv = mAnim->compositingPrevFrame->Init(0, 0, mSize.width, mSize.height,
-                                                      gfxImageFormat::ARGB32);
+                                                      gfx::SurfaceFormat::B8G8R8A8);
       if (NS_FAILED(rv)) {
         mAnim->compositingPrevFrame.SetFrame(nullptr);
         return false;
       }
 
       mAnim->compositingPrevFrame.LockAndGetData();
     }
 
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -95,23 +95,20 @@ OrientedImage::GetFrame(uint32_t aWhichF
   NS_ENSURE_SUCCESS(rv, nullptr);
   if (mOrientation.SwapsWidthAndHeight()) {
     swap(width, height);
   }
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   // Determine an appropriate format for the surface.
   gfx::SurfaceFormat surfaceFormat;
-  gfxImageFormat imageFormat;
   if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
-    imageFormat = gfxImageFormat::ARGB32;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
-    imageFormat = gfxImageFormat::ARGB32;
   }
 
   // Create a surface to draw into.
   mozilla::RefPtr<DrawTarget> target =
     gfxPlatform::GetPlatform()->
       CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
   if (!target) {
     NS_ERROR("Could not create a DrawTarget");
@@ -126,17 +123,17 @@ OrientedImage::GetFrame(uint32_t aWhichF
   nsRefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
 
   // Draw.
   nsRefPtr<gfxContext> ctx = new gfxContext(target);
   gfxRect imageRect(0, 0, width, height);
   gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
                              imageRect, imageRect, imageRect, imageRect,
-                             imageFormat, GraphicsFilter::FILTER_FAST);
+                             surfaceFormat, GraphicsFilter::FILTER_FAST);
   
   return target->Snapshot();
 }
 
 NS_IMETHODIMP
 OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
 {
   // XXX(seth): We currently don't have a way of orienting the result of
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -207,43 +207,37 @@ public:
     nsRefPtr<RasterImage> image = weakImage.get();
     if (!image) {
       return false;
     }
 
     bool success = false;
     if (!dstLocked) {
       bool srcLocked = NS_SUCCEEDED(srcFrame->LockImageData());
+      srcSurface = srcFrame->GetSurface();
+
       dstLocked = NS_SUCCEEDED(dstFrame->LockImageData());
-
-      nsRefPtr<gfxASurface> dstASurf;
-      nsRefPtr<gfxASurface> srcASurf;
-      success = srcLocked && NS_SUCCEEDED(srcFrame->GetSurface(getter_AddRefs(srcASurf)));
-      success = success && dstLocked && NS_SUCCEEDED(dstFrame->GetSurface(getter_AddRefs(dstASurf)));
-
-      success = success && srcLocked && dstLocked && srcASurf && dstASurf;
+      dstSurface = dstFrame->GetSurface();
+
+      success = srcLocked && dstLocked && srcSurface && dstSurface;
 
       if (success) {
-        srcSurface = srcASurf->GetAsImageSurface();
-        dstSurface = dstASurf->GetAsImageSurface();
-        srcData = srcSurface->Data();
-        dstData = dstSurface->Data();
-        srcStride = srcSurface->Stride();
-        dstStride = dstSurface->Stride();
-        srcFormat = mozilla::gfx::ImageFormatToSurfaceFormat(srcFrame->GetFormat());
+        srcData = srcFrame->GetImageData();
+        dstData = dstFrame->GetImageData();
+        srcStride = srcFrame->GetImageBytesPerRow();
+        dstStride = dstFrame->GetImageBytesPerRow();
+        srcFormat = srcFrame->GetFormat();
       }
 
-      // We have references to the Thebes surfaces, so we don't need to leave
+      // We have references to the surfaces, so we don't need to leave
       // the source frame (that we don't own) locked. We'll unlock the
       // destination frame in ReleaseSurfaces(), below.
       if (srcLocked) {
         success = NS_SUCCEEDED(srcFrame->UnlockImageData()) && success;
       }
-
-      success = success && srcSurface && dstSurface;
     }
 
     return success;
   }
 
   // This can only be called on the main thread.
   bool ReleaseSurfaces()
   {
@@ -267,28 +261,28 @@ public:
       dstSurface = nullptr;
     }
     return success;
   }
 
   // These values may only be touched on the main thread.
   WeakPtr<RasterImage> weakImage;
   nsAutoPtr<imgFrame> dstFrame;
-  nsRefPtr<gfxImageSurface> srcSurface;
-  nsRefPtr<gfxImageSurface> dstSurface;
+  RefPtr<SourceSurface> srcSurface;
+  RefPtr<SourceSurface> dstSurface;
 
   // Below are the values that may be touched on the scaling thread.
   gfxSize scale;
   uint8_t* srcData;
   uint8_t* dstData;
   nsIntRect srcRect;
   gfxIntSize dstSize;
   uint32_t srcStride;
   uint32_t dstStride;
-  mozilla::gfx::SurfaceFormat srcFormat;
+  SurfaceFormat srcFormat;
   bool dstLocked;
   bool done;
   // This boolean is accessed from both threads simultaneously without locking.
   // That's safe because stopping a ScaleRequest is strictly an optimization;
   // if we're not cache-coherent, at worst we'll do extra work.
   bool stopped;
 };
 
@@ -330,17 +324,17 @@ public:
   ScaleRunner(RasterImage* aImage, const gfxSize& aScale, imgFrame* aSrcFrame)
   {
     nsAutoPtr<ScaleRequest> request(new ScaleRequest(aImage, aScale, aSrcFrame));
 
     // Destination is unconditionally ARGB32 because that's what the scaler
     // outputs.
     request->dstFrame = new imgFrame();
     nsresult rv = request->dstFrame->Init(0, 0, request->dstSize.width, request->dstSize.height,
-                                          gfxImageFormat::ARGB32);
+                                          SurfaceFormat::B8G8R8A8);
 
     if (NS_FAILED(rv) || !request->GetSurfaces(aSrcFrame)) {
       return;
     }
 
     aImage->ScalingStart(request);
 
     mScaleRequest = request;
@@ -696,20 +690,16 @@ RasterImage::GetDrawableImgFrame(uint32_
     frame = GetImgFrame(framenum);
   }
 
   // We will return a paletted frame if it's not marked as compositing failed
   // so we can catch crashes for reasons we haven't investigated.
   if (frame && frame->GetCompositingFailed())
     return nullptr;
 
-  if (frame) {
-    frame->ApplyDirtToSurfaces();
-  }
-
   return frame;
 }
 
 uint32_t
 RasterImage::GetCurrentImgFrameIndex() const
 {
   if (mAnim)
     return mAnim->GetCurrentAnimationFrameIndex();
@@ -826,73 +816,85 @@ RasterImage::GetFirstFrameDelay()
 
   bool animated = false;
   if (NS_FAILED(GetAnimated(&animated)) || !animated)
     return -1;
 
   return mFrameBlender.GetTimeoutForFrame(0);
 }
 
-nsresult
+TemporaryRef<SourceSurface>
 RasterImage::CopyFrame(uint32_t aWhichFrame,
-                       uint32_t aFlags,
-                       gfxImageSurface **_retval)
+                       uint32_t aFlags)
 {
   if (aWhichFrame > FRAME_MAX_VALUE)
-    return NS_ERROR_INVALID_ARG;
+    return nullptr;
 
   if (mError)
-    return NS_ERROR_FAILURE;
+    return nullptr;
 
   // Disallowed in the API
   if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
-    return NS_ERROR_FAILURE;
+    return nullptr;
 
   nsresult rv;
 
   if (!ApplyDecodeFlags(aFlags, aWhichFrame))
-    return NS_ERROR_NOT_AVAILABLE;
+    return nullptr;
 
   // If requested, synchronously flush any data we have lying around to the decoder
   if (aFlags & FLAG_SYNC_DECODE) {
     rv = SyncDecode();
-    CONTAINER_ENSURE_SUCCESS(rv);
+    CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
   }
 
-  NS_ENSURE_ARG_POINTER(_retval);
-
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE
   uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
                         0 : GetCurrentImgFrameIndex();
   imgFrame *frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
-    *_retval = nullptr;
-    return NS_ERROR_FAILURE;
+    return nullptr;
   }
 
-  nsRefPtr<gfxPattern> pattern;
-  frame->GetPattern(getter_AddRefs(pattern));
-  nsIntRect intframerect = frame->GetRect();
-  gfxRect framerect(intframerect.x, intframerect.y, intframerect.width, intframerect.height);
-
   // Create a 32-bit image surface of our size, but draw using the frame's
   // rect, implicitly padding the frame out to the image's size.
-  nsRefPtr<gfxImageSurface> imgsurface = new gfxImageSurface(gfxIntSize(mSize.width, mSize.height),
-                                                             gfxImageFormat::ARGB32);
-  gfxContext ctx(imgsurface);
-  ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
-  ctx.Rectangle(framerect);
-  ctx.Translate(framerect.TopLeft());
-  ctx.SetPattern(pattern);
-  ctx.Fill();
-
-  imgsurface.forget(_retval);
-  return NS_OK;
+
+  IntSize size(mSize.width, mSize.height);
+  RefPtr<DataSourceSurface> surf =
+    Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+
+  DataSourceSurface::MappedSurface mapping;
+  DebugOnly<bool> success =
+    surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+  NS_ASSERTION(success, "Failed to map surface");
+  RefPtr<DrawTarget> target =
+    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                     mapping.mData,
+                                     size,
+                                     mapping.mStride,
+                                     SurfaceFormat::B8G8R8A8);
+
+  nsIntRect intframerect = frame->GetRect();
+  Rect rect(intframerect.x, intframerect.y,
+            intframerect.width, intframerect.height);
+  if (frame->IsSinglePixel()) {
+    target->FillRect(rect, ColorPattern(frame->SinglePixelColor()),
+                     DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+  } else {
+    RefPtr<SourceSurface> srcsurf = frame->GetSurface();
+    Rect srcrect(0, 0, intframerect.width, intframerect.height);
+    target->DrawSurface(srcsurf, srcrect, rect);
+  }
+
+  target->Flush();
+  surf->Unmap();
+
+  return surf;
 }
 
 //******************************************************************************
 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
  *                                   in uint32_t aFlags); */
 NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 RasterImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
@@ -923,25 +925,25 @@ RasterImage::GetFrame(uint32_t aWhichFra
   // FLAG_SYNC_DECODE
   uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
                           0 : GetCurrentImgFrameIndex();
   imgFrame *frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return nullptr;
   }
 
-  nsRefPtr<gfxASurface> framesurf;
+  RefPtr<SourceSurface> framesurf;
 
   // If this frame covers the entire image, we can just reuse its existing
   // surface.
   nsIntRect framerect = frame->GetRect();
   if (framerect.x == 0 && framerect.y == 0 &&
       framerect.width == mSize.width &&
       framerect.height == mSize.height) {
-    frame->GetSurface(getter_AddRefs(framesurf));
+    framesurf = frame->GetSurface();
     if (!framesurf && !frame->IsSinglePixel()) {
       // No reason to be optimized away here - the OS threw out the data
       if (!(aFlags & FLAG_SYNC_DECODE))
         return nullptr;
 
       // Unconditionally call ForceDiscard() here because GetSurface can only
       // return null when we can forcibly discard and redecode. There are two
       // other cases where GetSurface() can return null - when it is a single
@@ -952,37 +954,20 @@ RasterImage::GetFrame(uint32_t aWhichFra
       ForceDiscard();
       return GetFrame(aWhichFrame, aFlags);
     }
   }
 
   // The image doesn't have a surface because it's been optimized away. Create
   // one.
   if (!framesurf) {
-    nsRefPtr<gfxImageSurface> imgsurf;
-    CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
-    framesurf = imgsurf;
+    framesurf = CopyFrame(aWhichFrame, aFlags);
   }
 
-  RefPtr<SourceSurface> result;
-
-  // As far as Moz2D is concerned, SourceSurface contains premultiplied alpha.
-  // If we're abusing it to contain non-premultiplied alpha then we want to
-  // avoid having Moz2D do any conversions on it (like copy to another
-  // surface). Hence why we try to wrap framesurf's data here for
-  // FLAG_DECODE_NO_PREMULTIPLY_ALPHA.
-  if ((aFlags & FLAG_WANT_DATA_SURFACE) != 0 ||
-      (aFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA) != 0) {
-    result = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(framesurf);
-  }
-  if (!result) {
-    result = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
-                                                                    framesurf);
-  }
-  return result.forget();
+  return framesurf;
 }
 
 already_AddRefed<layers::Image>
 RasterImage::GetCurrentImage()
 {
   if (!mDecoded) {
     // We can't call StartDecoding because that can synchronously notify
     // which can cause DOM modification
@@ -1177,17 +1162,17 @@ RasterImage::InternalAddFrameHelper(uint
 
   return NS_OK;
 }
 
 nsresult
 RasterImage::InternalAddFrame(uint32_t framenum,
                               int32_t aX, int32_t aY,
                               int32_t aWidth, int32_t aHeight,
-                              gfxImageFormat aFormat,
+                              SurfaceFormat aFormat,
                               uint8_t aPaletteDepth,
                               uint8_t **imageData,
                               uint32_t *imageLength,
                               uint32_t **paletteData,
                               uint32_t *paletteLength,
                               imgFrame** aRetFrame)
 {
   // We assume that we're in the middle of decoding because we unlock the
@@ -1314,17 +1299,17 @@ RasterImage::SetSize(int32_t aWidth, int
   mFrameBlender.SetSize(mSize);
 
   return NS_OK;
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFrameNum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
-                         gfxImageFormat aFormat,
+                         SurfaceFormat aFormat,
                          uint8_t aPaletteDepth,
                          uint8_t **imageData, uint32_t *imageLength,
                          uint32_t **paletteData, uint32_t *paletteLength,
                          imgFrame** aRetFrame)
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
@@ -1389,17 +1374,17 @@ RasterImage::EnsureFrame(uint32_t aFrame
   return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData,
                                 imageLength, paletteData, paletteLength,
                                 aRetFrame);
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
-                         gfxImageFormat aFormat,
+                         SurfaceFormat aFormat,
                          uint8_t** imageData, uint32_t* imageLength,
                          imgFrame** aFrame)
 {
   return EnsureFrame(aFramenum, aX, aY, aWidth, aHeight, aFormat,
                      /* aPaletteDepth = */ 0, imageData, imageLength,
                      /* aPaletteData = */ nullptr,
                      /* aPaletteLength = */ nullptr,
                      aFrame);
@@ -2107,17 +2092,17 @@ RasterImage::InitDecoder(bool aDoSizeDec
   mDecoder->SetObserver(mDecodeRequest->mStatusTracker->GetDecoderObserver());
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
   if (!aDoSizeDecode) {
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
     mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
-                           gfxImageFormat::ARGB32);
+                           SurfaceFormat::B8G8R8A8);
     mDecoder->AllocateFrame();
   }
   mDecoder->Init();
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
   if (!aDoSizeDecode) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
@@ -2566,17 +2551,16 @@ RasterImage::ScalingDone(ScaleRequest* r
   MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
   MOZ_ASSERT(request);
 
   if (status == SCALE_DONE) {
     MOZ_ASSERT(request->done);
 
     imgFrame *scaledFrame = request->dstFrame.forget();
     scaledFrame->ImageUpdated(scaledFrame->GetRect());
-    scaledFrame->ApplyDirtToSurfaces();
 
     if (mStatusTracker) {
       mStatusTracker->FrameChanged(&request->srcRect);
     }
 
     mScaleResult.status = SCALE_DONE;
     mScaleResult.frame = scaledFrame;
     mScaleResult.scale = request->scale;
@@ -2604,31 +2588,31 @@ RasterImage::DrawWithPreDownscaleIfNeede
 {
   imgFrame *frame = aFrame;
   nsIntRect framerect = frame->GetRect();
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
   gfxSize scale = imageSpaceToUserSpace.ScaleFactors(true);
   nsIntRect subimage = aSubimage;
-  nsRefPtr<gfxASurface> surf;
+  RefPtr<SourceSurface> surf;
 
   if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
     // If scale factor is still the same that we scaled for and
     // ScaleWorker isn't still working, then we can use pre-downscaled frame.
     // If scale factor has changed, order new request.
     // FIXME: Current implementation doesn't support pre-downscale
     // mechanism for multiple sizes from same src, since we cache
     // pre-downscaled frame only for the latest requested scale.
     // The solution is to cache more than one scaled image frame
     // for each RasterImage.
     bool needScaleReq;
     if (mScaleResult.status == SCALE_DONE && mScaleResult.scale == scale) {
       // Grab and hold the surface to make sure the OS didn't destroy it
-      mScaleResult.frame->GetSurface(getter_AddRefs(surf));
+      surf = mScaleResult.frame->GetSurface();
       needScaleReq = !surf;
       if (surf) {
         frame = mScaleResult.frame;
         userSpaceToImageSpace.Multiply(gfxMatrix().Scale(scale.width,
                                                          scale.height));
 
         // Since we're switching to a scaled image, we need to transform the
         // area of the subimage to draw accordingly, since imgFrame::Draw()
@@ -3093,18 +3077,16 @@ RasterImage::FinishedSomeDecoding(eShutd
   // destroy it by destroying the decoder.
   nsRefPtr<RasterImage> image(this);
 
   bool done = false;
   bool wasSize = false;
   nsresult rv = NS_OK;
 
   if (image->mDecoder) {
-    image->mDecoder->MarkFrameDirty();
-
     if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
     }
 
     if (!image->mHasSize && image->mDecoder->HasSize()) {
       image->mDecoder->SetSizeOnImage();
     }
 
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -201,31 +201,31 @@ public:
   /**
    * Ensures that a given frame number exists with the given parameters, and
    * returns pointers to the data storage for that frame.
    * It is not possible to create sparse frame arrays; you can only append
    * frames to the current frame array.
    */
   nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                        int32_t aWidth, int32_t aHeight,
-                       gfxImageFormat aFormat,
+                       gfx::SurfaceFormat aFormat,
                        uint8_t aPaletteDepth,
                        uint8_t** imageData,
                        uint32_t* imageLength,
                        uint32_t** paletteData,
                        uint32_t* paletteLength,
                        imgFrame** aFrame);
 
   /**
    * A shorthand for EnsureFrame, above, with aPaletteDepth = 0 and paletteData
    * and paletteLength set to null.
    */
   nsresult EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                        int32_t aWidth, int32_t aHeight,
-                       gfxImageFormat aFormat,
+                       gfx::SurfaceFormat aFormat,
                        uint8_t** imageData,
                        uint32_t* imageLength,
                        imgFrame** aFrame);
 
   /* notification that the entire image has been decoded */
   nsresult DecodingComplete();
 
   /**
@@ -552,19 +552,18 @@ private:
   bool DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage,
                                     uint32_t aFlags);
 
-  nsresult CopyFrame(uint32_t aWhichFrame,
-                     uint32_t aFlags,
-                     gfxImageSurface **_retval);
+  TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
+                                             uint32_t aFlags);
 
   /**
    * Deletes and nulls out the frame in mFrames[framenum].
    *
    * Does not change the size of mFrames.
    *
    * @param framenum The index of the frame to be deleted.
    *                 Must lie in [0, mFrames.Length() )
@@ -582,17 +581,17 @@ private:
 
   void EnsureAnimExists();
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
                                   uint8_t **imageData, uint32_t *imageLength,
                                   uint32_t **paletteData, uint32_t *paletteLength,
                                   imgFrame** aRetFrame);
   nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
-                            gfxImageFormat aFormat, uint8_t aPaletteDepth,
+                            gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth,
                             uint8_t **imageData, uint32_t *imageLength,
                             uint32_t **paletteData, uint32_t *paletteLength,
                             imgFrame** aRetFrame);
 
   nsresult DoImageDataComplete();
 
   bool ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame);
 
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -908,17 +908,17 @@ VectorImage::CreateDrawableAndShow(const
   nsRefPtr<gfxContext> ctx = new gfxContext(target);
 
   // Actually draw. (We use FILTER_NEAREST since we never scale here.)
   gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
                              ThebesIntRect(aParams.imageRect),
-                             gfxImageFormat::ARGB32,
+                             SurfaceFormat::B8G8R8A8,
                              GraphicsFilter::FILTER_NEAREST, aParams.flags);
 
   // Attempt to cache the resulting surface.
   SurfaceCache::Insert(target,
                        ImageKey(this),
                        SurfaceKey(aParams.imageRect.Size(), aParams.scale,
                                   aParams.svgContext, aParams.animationTime,
                                   aParams.flags));
@@ -935,17 +935,17 @@ VectorImage::CreateDrawableAndShow(const
 void
 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
 {
   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
   gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
                              aParams.userSpaceToImageSpace,
                              aParams.subimage, aParams.sourceRect,
                              ThebesIntRect(aParams.imageRect), aParams.fill,
-                             gfxImageFormat::ARGB32,
+                             SurfaceFormat::B8G8R8A8,
                              aParams.filter, aParams.flags);
 
   MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
   mRenderingObserver->ResumeHonoringInvalidations();
 }
 
 //******************************************************************************
 /* void requestDecode() */
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -17,64 +17,55 @@
 static bool gDisableOptimize = false;
 
 #include "cairo.h"
 #include "GeckoProfiler.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsMargin.h"
 #include "mozilla/CheckedInt.h"
-
-#if defined(XP_WIN)
-
-#include "gfxWindowsPlatform.h"
-
-/* Whether to use the windows surface; only for desktop win32 */
-#define USE_WIN_SURFACE 1
-
-#endif
+#include "mozilla/gfx/Tools.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
-static cairo_user_data_key_t kVolatileBuffer;
+static UserDataKey kVolatileBuffer;
 
 static void
 VolatileBufferRelease(void *vbuf)
 {
   delete static_cast<VolatileBufferPtr<unsigned char>*>(vbuf);
 }
 
-gfxImageSurface *
-LockedImageSurface::CreateSurface(VolatileBuffer *vbuf,
-                                  const gfxIntSize& size,
-                                  gfxImageFormat format)
+static TemporaryRef<DataSourceSurface>
+CreateLockedSurface(VolatileBuffer *vbuf,
+                    const IntSize& size,
+                    SurfaceFormat format)
 {
   VolatileBufferPtr<unsigned char> *vbufptr =
     new VolatileBufferPtr<unsigned char>(vbuf);
   MOZ_ASSERT(!vbufptr->WasBufferPurged(), "Expected image data!");
 
-  long stride = gfxImageSurface::ComputeStride(size, format);
-  gfxImageSurface *img = new gfxImageSurface(*vbufptr, size, stride, format);
-  if (!img || img->CairoStatus()) {
-    delete img;
+  int32_t stride = size.width * BytesPerPixel(format);
+  RefPtr<DataSourceSurface> surf =
+    Factory::CreateWrappingDataSourceSurface(*vbufptr, stride, size, format);
+  if (!surf) {
     delete vbufptr;
     return nullptr;
   }
 
-  img->SetData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
-  return img;
+  surf->AddUserData(&kVolatileBuffer, vbufptr, VolatileBufferRelease);
+  return surf;
 }
 
-TemporaryRef<VolatileBuffer>
-LockedImageSurface::AllocateBuffer(const gfxIntSize& size,
-                                   gfxImageFormat format)
+static TemporaryRef<VolatileBuffer>
+AllocateBufferForImage(const IntSize& size, SurfaceFormat format)
 {
-  long stride = gfxImageSurface::ComputeStride(size, format);
+  int32_t stride = size.width * BytesPerPixel(format);
   RefPtr<VolatileBuffer> buf = new VolatileBuffer();
   if (buf->Init(stride * size.height,
                 1 << gfxAlphaRecovery::GoodAlignmentLog2()))
     return buf;
 
   return nullptr;
 }
 
@@ -104,61 +95,29 @@ static bool AllowedImageSize(int32_t aWi
   if (MOZ_UNLIKELY(aHeight > SHRT_MAX)) {
     NS_WARNING("image too big");
     return false;
   }
 #endif
   return true;
 }
 
-// Returns whether we should, at this time, use image surfaces instead of
-// optimized platform-specific surfaces.
-static bool ShouldUseImageSurfaces()
-{
-#if defined(USE_WIN_SURFACE)
-  static const DWORD kGDIObjectsHighWaterMark = 7000;
-
-  if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
-      gfxWindowsPlatform::RENDER_DIRECT2D) {
-    return true;
-  }
-
-  // at 7000 GDI objects, stop allocating normal images to make sure
-  // we never hit the 10k hard limit.
-  // GetCurrentProcess() just returns (HANDLE)-1, it's inlined afaik
-  DWORD count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
-  if (count == 0 ||
-      count > kGDIObjectsHighWaterMark)
-  {
-    // either something's broken (count == 0),
-    // or we hit our high water mark; disable
-    // image allocations for a bit.
-    return true;
-  }
-#endif
-
-  return false;
-}
-
 imgFrame::imgFrame() :
   mDecoded(0, 0, 0, 0),
-  mDirtyMutex("imgFrame::mDirty"),
+  mDecodedMutex("imgFrame::mDecoded"),
   mPalettedImageData(nullptr),
-  mSinglePixelColor(0),
   mTimeout(100),
   mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
   mLockCount(0),
   mBlendMethod(1), /* imgIContainer::kBlendOver */
   mSinglePixel(false),
-  mFormatChanged(false),
   mCompositingFailed(false),
   mNonPremult(false),
   mDiscardable(false),
-  mInformedDiscardTracker(false),
-  mDirty(false)
+  mInformedDiscardTracker(false)
 {
   static bool hasCheckedOptimize = false;
   if (!hasCheckedOptimize) {
     if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
       gDisableOptimize = true;
     }
     hasCheckedOptimize = true;
   }
@@ -170,17 +129,17 @@ imgFrame::~imgFrame()
   mPalettedImageData = nullptr;
 
   if (mInformedDiscardTracker) {
     DiscardTracker::InformDeallocation(4 * mSize.height * mSize.width);
   }
 }
 
 nsresult imgFrame::Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
-                        gfxImageFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
+                        SurfaceFormat aFormat, uint8_t aPaletteDepth /* = 0 */)
 {
   // assert for properties that should be verified by decoders, warn for properties related to bad content
   if (!AllowedImageSize(aWidth, aHeight)) {
     NS_WARNING("Should have legal image size");
     return NS_ERROR_FAILURE;
   }
 
   mOffset.MoveTo(aX, aY);
@@ -203,65 +162,32 @@ nsresult imgFrame::Init(int32_t aX, int3
       NS_WARNING("moz_malloc for paletted image data should succeed");
     NS_ENSURE_TRUE(mPalettedImageData, NS_ERROR_OUT_OF_MEMORY);
   } else {
     // Inform the discard tracker that we are going to allocate some memory.
     if (!DiscardTracker::TryAllocation(4 * mSize.width * mSize.height)) {
       NS_WARNING("Exceed the hard limit of decode image size");
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    // For Windows, we must create the device surface first (if we're
-    // going to) so that the image surface can wrap it.  Can't be done
-    // the other way around.
-#ifdef USE_WIN_SURFACE
-    if (!ShouldUseImageSurfaces()) {
-      mWinSurface = new gfxWindowsSurface(gfxIntSize(mSize.width, mSize.height), mFormat);
-      if (mWinSurface && mWinSurface->CairoStatus() == 0) {
-        // no error
-        mImageSurface = mWinSurface->GetAsImageSurface();
-      } else {
-        mWinSurface = nullptr;
-      }
-    }
-#endif
-
-    // For other platforms, space for the image surface is first allocated in
-    // a volatile buffer and then wrapped by a LockedImageSurface.
-    // This branch is also used on Windows if we're not using device surfaces
-    // or if we couldn't create one.
     if (!mImageSurface) {
-      mVBuf = LockedImageSurface::AllocateBuffer(mSize, mFormat);
+      mVBuf = AllocateBufferForImage(mSize, mFormat);
       if (!mVBuf) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
-      mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
+      mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
     }
 
-    if (!mImageSurface || mImageSurface->CairoStatus()) {
-      mImageSurface = nullptr;
-      // guess
-      if (!mImageSurface) {
-        NS_WARNING("Allocation of gfxImageSurface should succeed");
-      } else if (!mImageSurface->CairoStatus()) {
-        NS_WARNING("gfxImageSurface should have good CairoStatus");
-      }
-
+    if (!mImageSurface) {
+      NS_WARNING("Failed to create VolatileDataSourceSurface");
       // Image surface allocation is failed, need to return
       // the booked buffer size.
       DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
       return NS_ERROR_OUT_OF_MEMORY;
     }
-
     mInformedDiscardTracker = true;
-
-#ifdef XP_MACOSX
-    if (!ShouldUseImageSurfaces()) {
-      mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
-    }
-#endif
   }
 
   return NS_OK;
 }
 
 nsresult imgFrame::Optimize()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -276,193 +202,157 @@ nsresult imgFrame::Optimize()
   // Cairo doesn't support non-premult single-colors.
   if (mNonPremult)
     return NS_OK;
 
   /* Figure out if the entire image is a constant color */
 
   // this should always be true
   if (mImageSurface->Stride() == mSize.width * 4) {
-    uint32_t *imgData = (uint32_t*) mImageSurface->Data();
+    uint32_t *imgData = (uint32_t*) ((uint8_t *)mVBufPtr);
     uint32_t firstPixel = * (uint32_t*) imgData;
     uint32_t pixelCount = mSize.width * mSize.height + 1;
 
     while (--pixelCount && *imgData++ == firstPixel)
       ;
 
     if (pixelCount == 0) {
       // all pixels were the same
-      if (mFormat == gfxImageFormat::ARGB32 ||
-          mFormat == gfxImageFormat::RGB24)
-      {
-        // Should already be premult if desired.
-        gfxRGBA::PackedColorType inputType = gfxRGBA::PACKED_XRGB;
-        if (mFormat == gfxImageFormat::ARGB32)
-          inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED;
-
-        mSinglePixelColor = gfxRGBA(firstPixel, inputType);
-
+      if (mFormat == SurfaceFormat::B8G8R8A8 ||
+          mFormat == SurfaceFormat::B8G8R8X8) {
         mSinglePixel = true;
+        mSinglePixelColor.a = ((firstPixel >> 24) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.r = ((firstPixel >> 16) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.g = ((firstPixel >>  8) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.b = ((firstPixel >>  0) & 0xFF) * (1.0f / 255.0f);
+        mSinglePixelColor.r /= mSinglePixelColor.a;
+        mSinglePixelColor.g /= mSinglePixelColor.a;
+        mSinglePixelColor.b /= mSinglePixelColor.a;
 
         // blow away the older surfaces (if they exist), to release their memory
         mVBuf = nullptr;
+        mVBufPtr = nullptr;
         mImageSurface = nullptr;
         mOptSurface = nullptr;
-#ifdef USE_WIN_SURFACE
-        mWinSurface = nullptr;
-#endif
-#ifdef XP_MACOSX
-        mQuartzSurface = nullptr;
-#endif
-        mDrawSurface = nullptr;
 
         // We just dumped most of our allocated memory, so tell the discard
         // tracker that we're not using any at all.
         if (mInformedDiscardTracker) {
           DiscardTracker::InformDeallocation(4 * mSize.width * mSize.height);
           mInformedDiscardTracker = false;
         }
 
         return NS_OK;
       }
     }
 
     // if it's not RGB24/ARGB32, don't optimize, but we never hit this at the moment
   }
 
-  // if we're being forced to use image surfaces due to
-  // resource constraints, don't try to optimize beyond same-pixel.
-  if (ShouldUseImageSurfaces())
-    return NS_OK;
-
-  mOptSurface = nullptr;
+#ifdef ANDROID
+  SurfaceFormat optFormat =
+    gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR);
 
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface) {
-    if (!mFormatChanged) {
-      // just use the DIB if the format has not changed
-      mOptSurface = mWinSurface;
-    }
-  }
-#endif
-
-#ifdef XP_MACOSX
-  if (mQuartzSurface) {
-    mQuartzSurface->Flush();
-  }
-#endif
-
-#ifdef ANDROID
-  gfxImageFormat optFormat =
-    gfxPlatform::GetPlatform()->
-      OptimalFormatForContent(gfxASurface::ContentFromFormat(mFormat));
-
-  if (optFormat == gfxImageFormat::RGB16_565) {
+  if (!GetHasAlpha() && optFormat == SurfaceFormat::R5G6B5) {
     RefPtr<VolatileBuffer> buf =
-      LockedImageSurface::AllocateBuffer(mSize, optFormat);
+      AllocateBufferForImage(mSize, optFormat);
     if (!buf)
       return NS_OK;
 
-    nsRefPtr<gfxImageSurface> surf =
-      LockedImageSurface::CreateSurface(buf, mSize, optFormat);
+    RefPtr<DataSourceSurface> surf =
+      CreateLockedSurface(buf, mSize, optFormat);
+    if (!surf)
+      return NS_ERROR_OUT_OF_MEMORY;
 
-    gfxContext ctx(surf);
-    ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
-    ctx.SetSource(mImageSurface);
-    ctx.Paint();
+    DataSourceSurface::MappedSurface mapping;
+    DebugOnly<bool> success =
+      surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+    NS_ASSERTION(success, "Failed to map surface");
+    RefPtr<DrawTarget> target =
+      Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                       mapping.mData,
+                                       mSize,
+                                       mapping.mStride,
+                                       optFormat);
+
+    Rect rect(0, 0, mSize.width, mSize.height);
+    target->DrawSurface(mImageSurface, rect, rect);
+    target->Flush();
+    surf->Unmap();
 
     mImageSurface = surf;
     mVBuf = buf;
     mFormat = optFormat;
-    mDrawSurface = nullptr;
   }
 #else
-  if (mOptSurface == nullptr)
-    mOptSurface = gfxPlatform::GetPlatform()->OptimizeImage(mImageSurface, mFormat);
+  mOptSurface = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->OptimizeSourceSurface(mImageSurface);
+  if (mOptSurface == mImageSurface)
+    mOptSurface = nullptr;
 #endif
 
   if (mOptSurface) {
     mVBuf = nullptr;
+    mVBufPtr = nullptr;
     mImageSurface = nullptr;
-#ifdef USE_WIN_SURFACE
-    mWinSurface = nullptr;
-#endif
-#ifdef XP_MACOSX
-    mQuartzSurface = nullptr;
-#endif
-    mDrawSurface = nullptr;
   }
 
   return NS_OK;
 }
 
-static void
-DoSingleColorFastPath(gfxContext*    aContext,
-                      const gfxRGBA& aSinglePixelColor,
-                      const gfxRect& aFill)
-{
-  // if a == 0, it's a noop
-  if (aSinglePixelColor.a == 0.0)
-    return;
-
-  gfxContext::GraphicsOperator op = aContext->CurrentOperator();
-  if (op == gfxContext::OPERATOR_OVER && aSinglePixelColor.a == 1.0) {
-    aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
-  }
-
-  aContext->SetDeviceColor(aSinglePixelColor);
-  aContext->NewPath();
-  aContext->Rectangle(aFill);
-  aContext->Fill();
-  aContext->SetOperator(op);
-  aContext->SetDeviceColor(gfxRGBA(0,0,0,0));
-}
-
 imgFrame::SurfaceWithFormat
 imgFrame::SurfaceForDrawing(bool               aDoPadding,
                             bool               aDoPartialDecode,
                             bool               aDoTile,
                             const nsIntMargin& aPadding,
                             gfxMatrix&         aUserSpaceToImageSpace,
                             gfxRect&           aFill,
                             gfxRect&           aSubimage,
                             gfxRect&           aSourceRect,
                             gfxRect&           aImageRect,
-                            gfxASurface*       aSurface)
+                            SourceSurface*     aSurface)
 {
   IntSize size(int32_t(aImageRect.Width()), int32_t(aImageRect.Height()));
   if (!aDoPadding && !aDoPartialDecode) {
     NS_ASSERTION(!mSinglePixel, "This should already have been handled");
     return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, ThebesIntSize(size)), mFormat);
   }
 
   gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.width, mDecoded.height);
 
   if (aDoTile || mSinglePixel) {
     // Create a temporary surface.
     // Give this surface an alpha channel because there are
     // transparent pixels in the padding or undecoded area
-    gfxImageFormat format = gfxImageFormat::ARGB32;
-    nsRefPtr<gfxASurface> surface =
-      gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxImageSurface::ContentFromFormat(format));
-    if (!surface || surface->CairoStatus())
+    RefPtr<DrawTarget> target =
+      gfxPlatform::GetPlatform()->
+        CreateOffscreenContentDrawTarget(size, SurfaceFormat::B8G8R8A8);
+    if (!target)
       return SurfaceWithFormat();
 
+    Rect fillRect(aFill.x, aFill.y, aFill.width, aFill.height);
     // Fill 'available' with whatever we've got
-    gfxContext tmpCtx(surface);
-    tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
     if (mSinglePixel) {
-      tmpCtx.SetDeviceColor(mSinglePixelColor);
+      target->FillRect(fillRect, ColorPattern(mSinglePixelColor),
+                       DrawOptions(1.0f, CompositionOp::OP_SOURCE));
     } else {
-      tmpCtx.SetSource(aSurface, gfxPoint(aPadding.left, aPadding.top));
+      gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
+      imageSpaceToUserSpace.Invert();
+      SurfacePattern pattern(aSurface,
+                             ExtendMode::REPEAT,
+                             Matrix(imageSpaceToUserSpace.xx,
+                                    imageSpaceToUserSpace.xy,
+                                    imageSpaceToUserSpace.yx,
+                                    imageSpaceToUserSpace.yy,
+                                    imageSpaceToUserSpace.x0,
+                                    imageSpaceToUserSpace.y0));
+      target->FillRect(fillRect, pattern);
     }
-    tmpCtx.Rectangle(available);
-    tmpCtx.Fill();
 
-    return SurfaceWithFormat(new gfxSurfaceDrawable(surface, ThebesIntSize(size)), format);
+    RefPtr<SourceSurface> newsurf = target->Snapshot();
+    return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, ThebesIntSize(size)), target->GetFormat());
   }
 
   // Not tiling, and we have a surface, so we can account for
   // padding and/or a partial decode just by twiddling parameters.
   // First, update our user-space fill rect.
   aSourceRect = aSourceRect.Intersect(available);
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
   imageSpaceToUserSpace.Invert();
@@ -488,53 +378,42 @@ bool imgFrame::Draw(gfxContext *aContext
 
   NS_ASSERTION(!aFill.IsEmpty(), "zero dest size --- fix caller");
   NS_ASSERTION(!aSubimage.IsEmpty(), "zero source size --- fix caller");
   NS_ASSERTION(!mPalettedImageData, "Directly drawing a paletted image!");
 
   bool doPadding = aPadding != nsIntMargin(0,0,0,0);
   bool doPartialDecode = !ImageComplete();
 
+  RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
+
   if (mSinglePixel && !doPadding && !doPartialDecode) {
-    DoSingleColorFastPath(aContext, mSinglePixelColor, aFill);
+    if (mSinglePixelColor.a == 0.0) {
+      return true;
+    }
+
+    Rect target(aFill.x, aFill.y, aFill.width, aFill.height);
+    dt->FillRect(target, ColorPattern(mSinglePixelColor),
+                 DrawOptions(1.0f, CompositionOpForOp(aContext->CurrentOperator())));
     return true;
   }
 
   gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace;
   gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill);
   gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(),
                     mSize.height + aPadding.TopBottom());
   gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
   gfxRect fill = aFill;
 
   NS_ASSERTION(!sourceRect.Intersect(subimage).IsEmpty(),
                "We must be allowed to sample *some* source pixels!");
 
-  nsRefPtr<gfxASurface> surf = CachedThebesSurface();
-  VolatileBufferPtr<unsigned char> ref(mVBuf);
-  if (!mSinglePixel && !surf) {
-    if (ref.WasBufferPurged()) {
-      return false;
-    }
-
-    surf = mDrawSurface;
-    if (!surf) {
-      long stride = gfxImageSurface::ComputeStride(mSize, mFormat);
-      nsRefPtr<gfxImageSurface> imgSurf =
-        new gfxImageSurface(ref, mSize, stride, mFormat);
-#if defined(XP_MACOSX)
-      surf = mDrawSurface = new gfxQuartzImageSurface(imgSurf);
-#else
-      surf = mDrawSurface = imgSurf;
-#endif
-    }
-    if (!surf || surf->CairoStatus()) {
-      mDrawSurface = nullptr;
-      return true;
-    }
+  RefPtr<SourceSurface> surf = GetSurface();
+  if (!surf) {
+    return false;
   }
 
   bool doTile = !imageRect.Contains(sourceRect) &&
                 !(aImageFlags & imgIContainer::FLAG_CLAMP);
   SurfaceWithFormat surfaceResult =
     SurfaceForDrawing(doPadding, doPartialDecode, doTile, aPadding,
                       userSpaceToImageSpace, fill, subimage, sourceRect,
                       imageRect, surf);
@@ -546,79 +425,66 @@ bool imgFrame::Draw(gfxContext *aContext
                                surfaceResult.mFormat, aFilter, aImageFlags);
   }
   return true;
 }
 
 // This can be called from any thread, but not simultaneously.
 nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
 {
-  MutexAutoLock lock(mDirtyMutex);
+  MutexAutoLock lock(mDecodedMutex);
 
   mDecoded.UnionRect(mDecoded, aUpdateRect);
 
   // clamp to bounds, in case someone sends a bogus updateRect (I'm looking at
   // you, gif decoder)
-  nsIntRect boundsRect(mOffset, mSize);
+  nsIntRect boundsRect(mOffset, nsIntSize(mSize.width, mSize.height));
   mDecoded.IntersectRect(mDecoded, boundsRect);
 
-  mDirty = true;
-
   return NS_OK;
 }
 
-bool imgFrame::GetIsDirty() const
+nsIntRect imgFrame::GetRect() const
 {
-  MutexAutoLock lock(mDirtyMutex);
-  return mDirty;
+  return nsIntRect(mOffset, nsIntSize(mSize.width, mSize.height));
 }
 
-nsIntRect imgFrame::GetRect() const
-{
-  return nsIntRect(mOffset, mSize);
-}
-
-gfxImageFormat imgFrame::GetFormat() const
+SurfaceFormat imgFrame::GetFormat() const
 {
   return mFormat;
 }
 
 bool imgFrame::GetNeedsBackground() const
 {
   // We need a background painted if we have alpha or we're incomplete.
-  return (mFormat == gfxImageFormat::ARGB32 || !ImageComplete());
+  return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
 }
 
 uint32_t imgFrame::GetImageBytesPerRow() const
 {
-  if (mImageSurface)
-    return mImageSurface->Stride();
-
   if (mVBuf)
-    return gfxImageSurface::ComputeStride(mSize, mFormat);
+    return mSize.width * BytesPerPixel(mFormat);
 
   if (mPaletteDepth)
     return mSize.width;
 
-  NS_ERROR("GetImageBytesPerRow called with mImageSurface == null, mVBuf == null and mPaletteDepth == 0");
-
   return 0;
 }
 
 uint32_t imgFrame::GetImageDataLength() const
 {
   return GetImageBytesPerRow() * mSize.height;
 }
 
 void imgFrame::GetImageData(uint8_t **aData, uint32_t *length) const
 {
   NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetImageData unless frame is locked");
 
   if (mImageSurface)
-    *aData = mImageSurface->Data();
+    *aData = mVBufPtr;
   else if (mPalettedImageData)
     *aData = mPalettedImageData + PaletteDataLength();
   else
     *aData = nullptr;
 
   *length = GetImageDataLength();
 }
 
@@ -632,17 +498,17 @@ uint8_t* imgFrame::GetImageData() const
 
 bool imgFrame::GetIsPaletted() const
 {
   return mPalettedImageData != nullptr;
 }
 
 bool imgFrame::GetHasAlpha() const
 {
-  return mFormat == gfxImageFormat::ARGB32;
+  return mFormat == SurfaceFormat::B8G8R8A8;
 }
 
 void imgFrame::GetPaletteData(uint32_t **aPalette, uint32_t *length) const
 {
   NS_ABORT_IF_FALSE(mLockCount != 0, "Can't GetPaletteData unless frame is locked");
 
   if (!mPalettedImageData) {
     *aPalette = nullptr;
@@ -682,76 +548,69 @@ nsresult imgFrame::LockImageData()
     return NS_OK;
 
   if (!mImageSurface) {
     if (mVBuf) {
       VolatileBufferPtr<uint8_t> ref(mVBuf);
       if (ref.WasBufferPurged())
         return NS_ERROR_FAILURE;
 
-      mImageSurface = LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
-      if (!mImageSurface || mImageSurface->CairoStatus())
+      mImageSurface = CreateLockedSurface(mVBuf, mSize, mFormat);
+      if (!mImageSurface)
         return NS_ERROR_OUT_OF_MEMORY;
     }
-    if (mOptSurface || mSinglePixel || mFormat == gfxImageFormat::RGB16_565) {
-      gfxImageFormat format = mFormat;
-      if (mFormat == gfxImageFormat::RGB16_565)
-        format = gfxImageFormat::ARGB32;
+    if (mOptSurface || mSinglePixel || mFormat == SurfaceFormat::R5G6B5) {
+      SurfaceFormat format = mFormat;
+      if (mFormat == SurfaceFormat::R5G6B5)
+        format = SurfaceFormat::B8G8R8A8;
 
       // Recover the pixels
       RefPtr<VolatileBuffer> buf =
-        LockedImageSurface::AllocateBuffer(mSize, format);
+        AllocateBufferForImage(mSize, format);
       if (!buf) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
 
-      RefPtr<gfxImageSurface> surf =
-        LockedImageSurface::CreateSurface(buf, mSize, mFormat);
-      if (!surf || surf->CairoStatus())
+      RefPtr<DataSourceSurface> surf =
+        CreateLockedSurface(buf, mSize, format);
+      if (!surf)
         return NS_ERROR_OUT_OF_MEMORY;
 
-      gfxContext context(surf);
-      context.SetOperator(gfxContext::OPERATOR_SOURCE);
+      DataSourceSurface::MappedSurface mapping;
+      DebugOnly<bool> success =
+        surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
+      NS_ASSERTION(success, "Failed to map surface");
+      RefPtr<DrawTarget> target =
+        Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                         mapping.mData,
+                                         mSize,
+                                         mapping.mStride,
+                                         format);
+
+      Rect rect(0, 0, mSize.width, mSize.height);
       if (mSinglePixel)
-        context.SetDeviceColor(mSinglePixelColor);
-      else if (mFormat == gfxImageFormat::RGB16_565)
-        context.SetSource(mImageSurface);
+        target->FillRect(rect, ColorPattern(mSinglePixelColor),
+                         DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+      else if (mFormat == SurfaceFormat::R5G6B5)
+        target->DrawSurface(mImageSurface, rect, rect);
       else
-        context.SetSource(mOptSurface);
-      context.Paint();
+        target->DrawSurface(mOptSurface, rect, rect,
+                            DrawSurfaceOptions(),
+                            DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+      target->Flush();
+      surf->Unmap();
 
       mFormat = format;
       mVBuf = buf;
       mImageSurface = surf;
       mOptSurface = nullptr;
-#ifdef USE_WIN_SURFACE
-      mWinSurface = nullptr;
-#endif
-#ifdef XP_MACOSX
-      mQuartzSurface = nullptr;
-#endif
     }
   }
 
-  // We might write to the bits in this image surface, so we need to make the
-  // surface ready for that.
-  if (mImageSurface)
-    mImageSurface->Flush();
-
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface)
-    mWinSurface->Flush();
-#endif
-
-#ifdef XP_MACOSX
-  if (!mQuartzSurface && !ShouldUseImageSurfaces()) {
-    mQuartzSurface = new gfxQuartzImageSurface(mImageSurface);
-  }
-#endif
-
+  mVBufPtr = mVBuf;
   return NS_OK;
 }
 
 nsresult imgFrame::UnlockImageData()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ABORT_IF_FALSE(mLockCount != 0, "Unlocking an unlocked image!");
@@ -770,98 +629,56 @@ nsresult imgFrame::UnlockImageData()
   if (mLockCount != 0) {
     return NS_OK;
   }
 
   // Paletted images don't have surfaces, so there's nothing to do.
   if (mPalettedImageData)
     return NS_OK;
 
-  // FIXME: Bug 795737
-  // If this image has been drawn since we were locked, it has had snapshots
-  // added, and we need to remove them before calling MarkDirty.
-  if (mImageSurface)
-    mImageSurface->Flush();
-
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface)
-    mWinSurface->Flush();
-#endif
-
-  // Assume we've been written to.
-  if (mImageSurface)
-    mImageSurface->MarkDirty();
-
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface)
-    mWinSurface->MarkDirty();
-#endif
-
-#ifdef XP_MACOSX
-  // The quartz image surface (ab)uses the flush method to get the
-  // cairo_image_surface data into a CGImage, so we have to call Flush() here.
-  if (mQuartzSurface)
-    mQuartzSurface->Flush();
-#endif
-
+  mVBufPtr = nullptr;
   if (mVBuf && mDiscardable) {
     mImageSurface = nullptr;
-#ifdef XP_MACOSX
-    mQuartzSurface = nullptr;
-#endif
   }
 
   return NS_OK;
 }
 
-void imgFrame::ApplyDirtToSurfaces()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MutexAutoLock lock(mDirtyMutex);
-  if (mDirty) {
-    // FIXME: Bug 795737
-    // If this image has been drawn since we were locked, it has had snapshots
-    // added, and we need to remove them before calling MarkDirty.
-    if (mImageSurface)
-      mImageSurface->Flush();
-
-#ifdef USE_WIN_SURFACE
-    if (mWinSurface)
-      mWinSurface->Flush();
-#endif
-
-    if (mImageSurface)
-      mImageSurface->MarkDirty();
-
-#ifdef USE_WIN_SURFACE
-    if (mWinSurface)
-      mWinSurface->MarkDirty();
-#endif
-
-#ifdef XP_MACOSX
-    // The quartz image surface (ab)uses the flush method to get the
-    // cairo_image_surface data into a CGImage, so we have to call Flush() here.
-    if (mQuartzSurface)
-      mQuartzSurface->Flush();
-#endif
-
-    mDirty = false;
-  }
-}
-
 void imgFrame::SetDiscardable()
 {
   MOZ_ASSERT(mLockCount, "Expected to be locked when SetDiscardable is called");
   // Disabled elsewhere due to the cost of calling GetSourceSurfaceForSurface.
 #ifdef MOZ_WIDGET_ANDROID
   mDiscardable = true;
 #endif
 }
 
+TemporaryRef<SourceSurface>
+imgFrame::GetSurface()
+{
+  if (mOptSurface) {
+    if (mOptSurface->IsValid())
+      return mOptSurface;
+    else
+      mOptSurface = nullptr;
+  }
+
+  if (mImageSurface)
+    return mImageSurface;
+
+  if (!mVBuf)
+    return nullptr;
+
+  VolatileBufferPtr<char> buf(mVBuf);
+  if (buf.WasBufferPurged())
+    return nullptr;
+
+  return CreateLockedSurface(mVBuf, mSize, mFormat);
+}
+
 int32_t imgFrame::GetRawTimeout() const
 {
   return mTimeout;
 }
 
 void imgFrame::SetRawTimeout(int32_t aTimeout)
 {
   mTimeout = aTimeout;
@@ -885,33 +702,32 @@ int32_t imgFrame::GetBlendMethod() const
 void imgFrame::SetBlendMethod(int32_t aBlendMethod)
 {
   mBlendMethod = (int8_t)aBlendMethod;
 }
 
 // This can be called from any thread.
 bool imgFrame::ImageComplete() const
 {
-  MutexAutoLock lock(mDirtyMutex);
+  MutexAutoLock lock(mDecodedMutex);
 
-  return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
+  return mDecoded.IsEqualInterior(nsIntRect(mOffset.x, mOffset.y,
+                                            mSize.width, mSize.height));
 }
 
 // A hint from the image decoders that this image has no alpha, even
 // though we created is ARGB32.  This changes our format to RGB24,
 // which in turn will cause us to Optimize() to RGB24.  Has no effect
 // after Optimize() is called, though in all cases it will be just a
 // performance win -- the pixels are still correct and have the A byte
 // set to 0xff.
 void imgFrame::SetHasNoAlpha()
 {
-  if (mFormat == gfxImageFormat::ARGB32) {
-      mFormat = gfxImageFormat::RGB24;
-      mFormatChanged = true;
-      ThebesSurface()->SetOpaqueRect(gfxRect(0, 0, mSize.width, mSize.height));
+  if (mFormat == SurfaceFormat::B8G8R8A8) {
+    mFormat = SurfaceFormat::B8G8R8X8;
   }
 }
 
 void imgFrame::SetAsNonPremult(bool aIsNonPremult)
 {
   mNonPremult = aIsNonPremult;
 }
 
@@ -943,53 +759,26 @@ imgFrame::SizeOfExcludingThisWithCompute
   if (mPalettedImageData && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
     size_t n2 = aMallocSizeOf(mPalettedImageData);
     if (n2 == 0) {
       n2 = GetImageDataLength() + PaletteDataLength();
     }
     n += n2;
   }
 
-#ifdef USE_WIN_SURFACE
-  if (mWinSurface && aLocation == mWinSurface->GetMemoryLocation()) {
-    n += mWinSurface->KnownMemoryUsed();
-  } else
-#endif
-#ifdef XP_MACOSX
-  if (mQuartzSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mQuartzSurface);
+  if (mImageSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
+    n += aMallocSizeOf(mImageSurface);
   }
-#endif
-  if (mImageSurface && aLocation == mImageSurface->GetMemoryLocation()) {
-    size_t n2 = 0;
-    if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) { // HEAP: measure
-      n2 = mImageSurface->SizeOfIncludingThis(aMallocSizeOf);
-    }
-    if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
-      n2 = mImageSurface->KnownMemoryUsed();
-    }
-    n += n2;
+  if (mOptSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
+    n += aMallocSizeOf(mOptSurface);
   }
 
   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
     n += aMallocSizeOf(mVBuf);
     n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
   }
 
   if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) {
     n += mVBuf->NonHeapSizeOfExcludingThis();
   }
 
-  if (mOptSurface && aLocation == mOptSurface->GetMemoryLocation()) {
-    size_t n2 = 0;
-    if (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&
-        mOptSurface->SizeOfIsMeasured()) {
-      // HEAP: measure (but only if the sub-class is capable of measuring)
-      n2 = mOptSurface->SizeOfIncludingThis(aMallocSizeOf);
-    }
-    if (n2 == 0) {  // non-HEAP or computed fallback for HEAP
-      n2 = mOptSurface->KnownMemoryUsed();
-    }
-    n += n2;
-  }
-
   return n;
 }
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -5,65 +5,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef imgFrame_h
 #define imgFrame_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/VolatileBuffer.h"
-#include "nsRect.h"
-#include "nsPoint.h"
-#include "nsSize.h"
-#include "gfxPattern.h"
 #include "gfxDrawable.h"
-#include "gfxImageSurface.h"
-#if defined(XP_WIN)
-#include "gfxWindowsSurface.h"
-#elif defined(XP_MACOSX)
-#include "gfxQuartzImageSurface.h"
-#endif
-#include "nsAutoPtr.h"
 #include "imgIContainer.h"
-#include "gfxColor.h"
-
-/*
- * This creates a gfxImageSurface which will unlock the buffer on destruction
- */
-
-class LockedImageSurface
-{
-public:
-  static gfxImageSurface *
-  CreateSurface(mozilla::VolatileBuffer *vbuf,
-                const gfxIntSize& size,
-                gfxImageFormat format);
-  static mozilla::TemporaryRef<mozilla::VolatileBuffer>
-  AllocateBuffer(const gfxIntSize& size, gfxImageFormat format);
-};
 
 class imgFrame
 {
+  typedef mozilla::gfx::Color Color;
+  typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
+  typedef mozilla::gfx::IntSize IntSize;
+  typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
 public:
   imgFrame();
   ~imgFrame();
 
-  nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, gfxImageFormat aFormat, uint8_t aPaletteDepth = 0);
+  nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, mozilla::gfx::SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
   nsresult Optimize();
 
   bool Draw(gfxContext *aContext, GraphicsFilter aFilter,
             const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
             const nsIntMargin &aPadding, const nsIntRect &aSubimage,
             uint32_t aImageFlags = imgIContainer::FLAG_NONE);
 
   nsresult ImageUpdated(const nsIntRect &aUpdateRect);
-  bool GetIsDirty() const;
 
   nsIntRect GetRect() const;
-  gfxImageFormat GetFormat() const;
+  mozilla::gfx::SurfaceFormat GetFormat() const;
   bool GetNeedsBackground() const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;
   bool GetIsPaletted() const;
   bool GetHasAlpha() const;
   void GetImageData(uint8_t **aData, uint32_t *length) const;
   uint8_t* GetImageData() const;
   void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
@@ -81,88 +59,33 @@ public:
   void SetHasNoAlpha();
   void SetAsNonPremult(bool aIsNonPremult);
 
   bool GetCompositingFailed() const;
   void SetCompositingFailed(bool val);
 
   nsresult LockImageData();
   nsresult UnlockImageData();
-  void ApplyDirtToSurfaces();
 
   void SetDiscardable();
 
-  nsresult GetSurface(gfxASurface **aSurface)
-  {
-    *aSurface = ThebesSurface();
-    NS_IF_ADDREF(*aSurface);
-    return NS_OK;
-  }
+  mozilla::TemporaryRef<SourceSurface> GetSurface();
 
-  nsresult GetPattern(gfxPattern **aPattern)
+  Color
+  SinglePixelColor()
   {
-    if (mSinglePixel)
-      *aPattern = new gfxPattern(mSinglePixelColor);
-    else
-      *aPattern = new gfxPattern(ThebesSurface());
-    NS_ADDREF(*aPattern);
-    return NS_OK;
+    return mSinglePixelColor;
   }
 
   bool IsSinglePixel()
   {
     return mSinglePixel;
   }
 
-  gfxASurface* CachedThebesSurface()
-  {
-    if (mOptSurface)
-      return mOptSurface;
-#if defined(XP_WIN)
-    if (mWinSurface)
-      return mWinSurface;
-#elif defined(XP_MACOSX)
-    if (mQuartzSurface)
-      return mQuartzSurface;
-#endif
-    if (mImageSurface)
-      return mImageSurface;
-    return nullptr;
-  }
-
-  gfxASurface* ThebesSurface()
-  {
-    gfxASurface *sur = CachedThebesSurface();
-    if (sur)
-      return sur;
-    if (mVBuf) {
-      mozilla::VolatileBufferPtr<uint8_t> ref(mVBuf);
-      if (ref.WasBufferPurged())
-        return nullptr;
-
-      gfxImageSurface *imgSur =
-        LockedImageSurface::CreateSurface(mVBuf, mSize, mFormat);
-#if defined(XP_MACOSX)
-      // Manually addref and release to make sure the cairo surface isn't lost
-      NS_ADDREF(imgSur);
-      gfxQuartzImageSurface *quartzSur = new gfxQuartzImageSurface(imgSur);
-      // quartzSur does not hold on to the gfxImageSurface
-      NS_RELEASE(imgSur);
-      return quartzSur;
-#else
-      return imgSur;
-#endif
-    }
-    // We can return null here if we're single pixel optimized
-    // or a paletted image. However, one has to check for paletted
-    // image data first before attempting to get a surface, so
-    // this is only valid for single pixel optimized images
-    MOZ_ASSERT(mSinglePixel, "No image surface and not a single pixel!");
-    return nullptr;
-  }
+  mozilla::TemporaryRef<SourceSurface> CachedSurface();
 
   size_t SizeOfExcludingThisWithComputedFallbackIfHeap(
            gfxMemoryLocation aLocation,
            mozilla::MallocSizeOf aMallocSizeOf) const;
 
   uint8_t GetPaletteDepth() const { return mPaletteDepth; }
   uint32_t PaletteDataLength() const {
     if (!mPaletteDepth)
@@ -170,82 +93,73 @@ public:
 
     return ((1 << mPaletteDepth) * sizeof(uint32_t));
   }
 
 private: // methods
 
   struct SurfaceWithFormat {
     nsRefPtr<gfxDrawable> mDrawable;
-    gfxImageFormat mFormat;
+    SurfaceFormat mFormat;
     SurfaceWithFormat() {}
-    SurfaceWithFormat(gfxDrawable* aDrawable, gfxImageFormat aFormat)
+    SurfaceWithFormat(gfxDrawable* aDrawable, SurfaceFormat aFormat)
      : mDrawable(aDrawable), mFormat(aFormat) {}
     bool IsValid() { return !!mDrawable; }
   };
 
   SurfaceWithFormat SurfaceForDrawing(bool               aDoPadding,
                                       bool               aDoPartialDecode,
                                       bool               aDoTile,
                                       const nsIntMargin& aPadding,
                                       gfxMatrix&         aUserSpaceToImageSpace,
                                       gfxRect&           aFill,
                                       gfxRect&           aSubimage,
                                       gfxRect&           aSourceRect,
                                       gfxRect&           aImageRect,
-                                      gfxASurface*       aSurface);
+                                      SourceSurface*     aSurface);
 
 private: // data
-  nsRefPtr<gfxImageSurface> mImageSurface;
-  nsRefPtr<gfxASurface> mOptSurface;
-#if defined(XP_WIN)
-  nsRefPtr<gfxWindowsSurface> mWinSurface;
-#elif defined(XP_MACOSX)
-  nsRefPtr<gfxQuartzImageSurface> mQuartzSurface;
-#endif
+  mozilla::RefPtr<DataSourceSurface> mImageSurface;
+  mozilla::RefPtr<SourceSurface> mOptSurface;
 
-  nsRefPtr<gfxASurface> mDrawSurface;
-
-  nsIntSize    mSize;
+  IntSize      mSize;
   nsIntPoint   mOffset;
 
   nsIntRect    mDecoded;
 
-  mutable mozilla::Mutex mDirtyMutex;
+  mutable mozilla::Mutex mDecodedMutex;
 
   // The palette and image data for images that are paletted, since Cairo
   // doesn't support these images.
   // The paletted data comes first, then the image data itself.
   // Total length is PaletteDataLength() + GetImageDataLength().
   uint8_t*     mPalettedImageData;
 
-  // Note that the data stored in gfxRGBA is *non-alpha-premultiplied*.
-  gfxRGBA      mSinglePixelColor;
+  // Note that the data stored in gfx::Color is *non-alpha-premultiplied*.
+  Color        mSinglePixelColor;
 
   int32_t      mTimeout; // -1 means display forever
   int32_t      mDisposalMethod;
 
   /** Indicates how many readers currently have locked this frame */
   int32_t mLockCount;
 
   mozilla::RefPtr<mozilla::VolatileBuffer> mVBuf;
+  mozilla::VolatileBufferPtr<uint8_t> mVBufPtr;
 
-  gfxImageFormat mFormat;
+  SurfaceFormat mFormat;
   uint8_t      mPaletteDepth;
   int8_t       mBlendMethod;
   bool mSinglePixel;
-  bool mFormatChanged;
   bool mCompositingFailed;
   bool mNonPremult;
   bool mDiscardable;
 
   /** Have we called DiscardTracker::InformAllocation()? */
   bool mInformedDiscardTracker;
-
-  bool mDirty;
 };
 
 namespace mozilla {
 namespace image {
   // An RAII class to ensure it's easy to balance locks and unlocks on
   // imgFrames.
   class AutoFrameLocker
   {
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -227,20 +227,17 @@ typedef bool
 (* StrictElementIdOp)(JSContext *cx, JS::HandleObject obj, uint32_t index,
                       JS::MutableHandleValue vp, bool strict);
 typedef bool
 (* GenericAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned *attrsp);
 typedef bool
 (* PropertyAttributesOp)(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
                          unsigned *attrsp);
 typedef bool
-(* DeletePropertyOp)(JSContext *cx, JS::HandleObject obj, JS::Handle<PropertyName*> name,
-                     bool *succeeded);
-typedef bool
-(* DeleteElementOp)(JSContext *cx, JS::HandleObject obj, uint32_t index, bool *succeeded);
+(* DeleteGenericOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *succeeded);
 
 typedef bool
 (* WatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
 
 typedef bool
 (* UnwatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 
 typedef bool
@@ -337,41 +334,39 @@ struct ObjectOps
     GenericIdOp         getGeneric;
     PropertyIdOp        getProperty;
     ElementIdOp         getElement;
     StrictGenericIdOp   setGeneric;
     StrictPropertyIdOp  setProperty;
     StrictElementIdOp   setElement;
     GenericAttributesOp getGenericAttributes;
     GenericAttributesOp setGenericAttributes;
-    DeletePropertyOp    deleteProperty;
-    DeleteElementOp     deleteElement;
+    DeleteGenericOp     deleteGeneric;
     WatchOp             watch;
     UnwatchOp           unwatch;
     SliceOp             slice; // Optimized slice, can be null.
-
     JSNewEnumerateOp    enumerate;
     ObjectOp            thisObject;
 };
 
 #define JS_NULL_OBJECT_OPS                                                    \
-    {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, \
-     nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr, \
-     nullptr,nullptr}
+    {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
+     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,  \
+     nullptr, nullptr, nullptr}
 
 } // namespace js
 
 // Classes, objects, and properties.
 
 typedef void (*JSClassInternal)();
 
 struct JSClass {
     JS_CLASS_MEMBERS(JSFinalizeOp);
 
-    void                *reserved[32];
+    void                *reserved[31];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_NEW_ENUMERATE           (1<<1)  // has JSNewEnumerateOp hook
 #define JSCLASS_NEW_RESOLVE             (1<<2)  // has JSNewResolveOp hook
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports *)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
 #define JSCLASS_IMPLEMENTS_BARRIERS     (1<<5)  // Correctly implements GC read
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -37,17 +37,17 @@ template <typename T> class AutoVectorRo
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
 template<typename T> class RootedGeneric;
 
 class MOZ_STACK_CLASS SourceBufferHolder;
 
 class HandleValueArray;
 
-class JS_PUBLIC_API(AutoCheckCannotGC);
+class AutoCheckCannotGC;
 
 }
 
 // Do the importing.
 namespace js {
 
 using JS::Value;
 using JS::BooleanValue;
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -655,16 +655,92 @@ function ArrayValues() {
 function ArrayEntries() {
     return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE);
 }
 
 function ArrayKeys() {
     return CreateArrayIterator(this, ITEM_KIND_KEY);
 }
 
+/* ES6 rev 25 (2014 May 22) 22.1.2.1 */
+function ArrayFrom(arrayLike, mapfn=undefined, thisArg=undefined) {
+    // Step 1.
+    var C = this;
+
+    // Steps 2-3.
+    var items = ToObject(arrayLike);
+
+    // Steps 4-5.
+    var mapping = (mapfn !== undefined);
+    if (mapping && !IsCallable(mapfn))
+        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn));
+
+    // All elements defined by this algorithm have the same attrs:
+    var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE;
+
+    // Steps 6-8.
+    var usingIterator = items["@@iterator"];
+    if (usingIterator !== undefined) {
+        // Steps 8.a-c.
+        var A = IsConstructor(C) ? new C() : [];
+
+        // Steps 8.d-e.
+        var iterator = callFunction(usingIterator, items);
+
+        // Step 8.f.
+        var k = 0;
+
+        // Steps 8.g.i-vi.
+        // These steps cannot be implemented using a for-of loop.
+        // See <https://bugs.ecmascript.org/show_bug.cgi?id=2883>.
+        var next;
+        while (true) {
+            // Steps 8.g.ii-vi.
+            next = iterator.next();
+            if (!IsObject(next))
+                ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE);
+            if (next.done)
+                break;  // Substeps of 8.g.iv are implemented below.
+            var nextValue = next.value;
+
+            // Steps 8.g.vii-viii.
+            var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
+
+            // Steps 8.g.ix-xi.
+            _DefineDataProperty(A, k++, mappedValue, attrs);
+        }
+    } else {
+        // Step 9 is an assertion: items is not an Iterator. Testing this is
+        // literally the very last thing we did, so we don't assert here.
+
+        // Steps 10-12.
+        // FIXME: Array operations should use ToLength (bug 924058).
+        var len = ToInteger(items.length);
+
+        // Steps 13-15.
+        var A = IsConstructor(C) ? new C(len) : NewDenseArray(len);
+
+        // Steps 16-17.
+        for (var k = 0; k < len; k++) {
+            // Steps 17.a-c.
+            var kValue = items[k];
+
+            // Steps 17.d-e.
+            var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
+
+            // Steps 17.f-g.
+            _DefineDataProperty(A, k, mappedValue, attrs);
+        }
+    }
+
+    // Steps 8.g.iv.1-3 and 18-20 are the same.
+    A.length = k;
+    return A;
+}
+
 #ifdef ENABLE_PARALLEL_JS
 
 /*
  * Strawman spec:
  *   http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism
  */
 
 /**
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -811,21 +811,21 @@ function SupportedLocales(availableLocal
 
     // Steps 2-3.
     var subset = (matcher === undefined || matcher === "best fit")
                  ? BestFitSupportedLocales(availableLocales, requestedLocales)
                  : LookupSupportedLocales(availableLocales, requestedLocales);
 
     // Step 4.
     for (var i = 0; i < subset.length; i++) {
-        _DefineValueProperty(subset, i, subset[i],
-                             ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
+        _DefineDataProperty(subset, i, subset[i],
+                            ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
     }
-    _DefineValueProperty(subset, "length", subset.length,
-                         ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
+    _DefineDataProperty(subset, "length", subset.length,
+                        ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
 
     // Step 5.
     return subset;
 }
 
 
 /**
  * Extracts a property value from the provided options object, converts it to
@@ -891,17 +891,17 @@ function GetNumberOption(options, proper
 /********** Property access for Intl objects **********/
 
 
 /**
  * Set a normal public property p of o to value v, but use Object.defineProperty
  * to avoid interference from setters on Object.prototype.
  */
 function defineProperty(o, p, v) {
-    _DefineValueProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
+    _DefineDataProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
 }
 
 
 /**
  * Weak map used to track the initialize-as-Intl status (and, if an object has
  * been so initialized, the Intl-specific internal properties) of all objects.
  * Presence of an object as a key within this map indicates that the object has
  * its [[initializedIntlObject]] internal property set to true.  The associated
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -366,17 +366,17 @@ enum DefineType { GetterAccessor, Setter
 template<DefineType Type>
 static bool
 DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!BoxNonStrictThis(cx, args))
         return false;
 
-    if (args.length() < 2 || !js_IsCallable(args[1])) {
+    if (args.length() < 2 || !IsCallable(args[1])) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_BAD_GETTER_OR_SETTER,
                              Type == GetterAccessor ? js_getter_str : js_setter_str);
         return false;
     }
 
     RootedId id(cx);
     if (!ValueToId<CanGC>(cx, args[0], &id))
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2088,50 +2088,28 @@ TypedObject::obj_setGenericAttributes(JS
         *attrsp = 0;
         return true;
     }
 
     return JSObject::setGenericAttributes(cx, proto, id, attrsp);
 }
 
 bool
-TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
-                                HandlePropertyName name, bool *succeeded)
+TypedObject::obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
 {
-    Rooted<jsid> id(cx, NameToId(name));
     if (IsOwnId(cx, obj, id))
         return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *succeeded = false;
         return true;
     }
 
-    return JSObject::deleteProperty(cx, proto, name, succeeded);
-}
-
-bool
-TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                               bool *succeeded)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-
-    if (IsOwnId(cx, obj, id))
-        return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
-
-    RootedObject proto(cx, obj->getProto());
-    if (!proto) {
-        *succeeded = false;
-        return true;
-    }
-
-    return JSObject::deleteElement(cx, proto, index, succeeded);
+    return JSObject::deleteGeneric(cx, proto, id, succeeded);
 }
 
 bool
 TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                            MutableHandleValue statep, MutableHandleId idp)
 {
     int32_t index;
 
@@ -2282,18 +2260,17 @@ const Class TransparentTypedObject::clas
         TypedObject::obj_getGeneric,
         TypedObject::obj_getProperty,
         TypedObject::obj_getElement,
         TypedObject::obj_setGeneric,
         TypedObject::obj_setProperty,
         TypedObject::obj_setElement,
         TypedObject::obj_getGenericAttributes,
         TypedObject::obj_setGenericAttributes,
-        TypedObject::obj_deleteProperty,
-        TypedObject::obj_deleteElement,
+        TypedObject::obj_deleteGeneric,
         nullptr, nullptr, // watch/unwatch
         nullptr,   /* slice */
         TypedObject::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
 static int32_t
@@ -2628,18 +2605,17 @@ const Class OpaqueTypedObject::class_ = 
         TypedObject::obj_getGeneric,
         TypedObject::obj_getProperty,
         TypedObject::obj_getElement,
         TypedObject::obj_setGeneric,
         TypedObject::obj_setProperty,
         TypedObject::obj_setElement,
         TypedObject::obj_getGenericAttributes,
         TypedObject::obj_setGenericAttributes,
-        TypedObject::obj_deleteProperty,
-        TypedObject::obj_deleteElement,
+        TypedObject::obj_deleteGeneric,
         nullptr, nullptr, // watch/unwatch
         nullptr, // slice
         TypedObject::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
 const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -571,20 +571,17 @@ class TypedObject : public ArrayBufferVi
     static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
                                MutableHandleValue vp, bool strict);
 
     static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj,
                                          HandleId id, unsigned *attrsp);
     static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj,
                                          HandleId id, unsigned *attrsp);
 
-    static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
-                                   bool *succeeded);
-    static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
-                                  bool *succeeded);
+    static bool obj_deleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded);
 
     static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
                               MutableHandleValue statep, MutableHandleId idp);
 
   public:
     static size_t offsetOfOwnerSlot();
 
     // Each typed object contains a void* pointer pointing at the
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -23,18 +23,23 @@
          JSMSG_EMPTY_ARRAY_REDUCE: false, JSMSG_CANT_CONVERT_TO: false,
 */
 
 #include "SelfHostingDefines.h"
 
 // Remove unsafe builtin functions.
 Object.defineProperty = null; // See bug 988416.
 
-// Cache builtin functions so using them doesn't require cloning the whole object they're 
+// Cache builtin functions so using them doesn't require cloning the whole object they're
 // installed on.
+//
+// WARNING: Do not make std_ references to builtin constructors (like Array and
+// Object) below. Setting `var std_Array = Array;`, for instance, would cause
+// the entire Array constructor, including its prototype and methods, to be
+// cloned into content compartments.
 var std_isFinite = isFinite;
 var std_isNaN = isNaN;
 var std_Array_indexOf = ArrayIndexOf;
 var std_Array_iterator = Array.prototype.iterator;
 var std_Array_join = Array.prototype.join;
 var std_Array_push = Array.prototype.push;
 var std_Array_pop = Array.prototype.pop;
 var std_Array_shift = Array.prototype.shift;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -260,16 +260,34 @@ Zone::hasMarkedCompartments()
 {
     for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
         if (comp->marked)
             return true;
     }
     return false;
 }
 
+bool
+Zone::canCollect()
+{
+    // Zones cannot be collected while in use by other threads.
+    if (usedByExclusiveThread)
+        return false;
+    JSRuntime *rt = runtimeFromAnyThread();
+    if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
+        return false;
+    return true;
+}
+
 JS::Zone *
 js::ZoneOfValue(const JS::Value &value)
 {
     JS_ASSERT(value.isMarkable());
     if (value.isObject())
         return value.toObject().zone();
     return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
 }
+
+bool
+js::ZonesIter::atAtomsZone(JSRuntime *rt)
+{
+    return rt->isAtomsZone(*it);
+}
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -132,25 +132,17 @@ struct Zone : public JS::shadow::Zone,
 
     void scheduleGC() { JS_ASSERT(!runtimeFromMainThread()->isHeapBusy()); gcScheduled_ = true; }
     void unscheduleGC() { gcScheduled_ = false; }
     bool isGCScheduled() { return gcScheduled_ && canCollect(); }
 
     void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
     bool isPreservingCode() const { return gcPreserveCode_; }
 
-    bool canCollect() {
-        // Zones cannot be collected while in use by other threads.
-        if (usedByExclusiveThread)
-            return false;
-        JSRuntime *rt = runtimeFromAnyThread();
-        if (rt->isAtomsZone(this) && rt->exclusiveThreadsPresent())
-            return false;
-        return true;
-    }
+    bool canCollect();
 
     enum GCState {
         NoGC,
         Mark,
         MarkGray,
         Sweep,
         Finished
     };
@@ -301,21 +293,23 @@ class ZonesIter
     JS::Zone **it, **end;
 
   public:
     ZonesIter(JSRuntime *rt, ZoneSelector selector) {
         it = rt->gc.zones.begin();
         end = rt->gc.zones.end();
 
         if (selector == SkipAtoms) {
-            JS_ASSERT(rt->isAtomsZone(*it));
+            MOZ_ASSERT(atAtomsZone(rt));
             it++;
         }
     }
 
+    bool atAtomsZone(JSRuntime *rt);
+
     bool done() const { return it == end; }
 
     void next() {
         JS_ASSERT(!done());
         do {
             it++;
         } while (!done() && (*it)->usedByExclusiveThread);
     }
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -31,107 +31,108 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::BinarySearch;
 using mozilla::IsNaN;
 using mozilla::PodZero;
 
-AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
+static uint8_t *
+ReturnAddressForExitCall(uint8_t **psp)
 {
-    if (!activation || activation->isInterruptedSP()) {
-        PodZero(this);
-        JS_ASSERT(done());
-        return;
-    }
-
-    module_ = &activation->module();
-    sp_ = activation->exitSP();
-
+    uint8_t *sp = *psp;
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     // For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
     // to C++. Since the call instruction pushes the return address, we know
     // that the return address is 1 word below exitSP.
-    returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
+    return *(uint8_t**)(sp - sizeof(void*));
 #elif defined(JS_CODEGEN_ARM)
     // For calls to Ion/C++ on ARM, the *caller* pushes the return address on
     // the stack. For Ion, this is just part of the ABI. For C++, the return
     // address is explicitly pushed before the call since we cannot expect the
     // callee to immediately push lr. This means that exitSP points to the
     // return address.
-    returnAddress_ = *(uint8_t**)sp_;
+    return *(uint8_t**)sp;
 #elif defined(JS_CODEGEN_MIPS)
-    // On MIPS we have two cases. Exit to C++ will store return addres at
-    // sp + 16, While on exits to Ion, the return address will be stored at
-    // sp + 0. We indicate exits to ion by setting the lowest bit of stored sp.
-
-    // Check if this is the exit to Ion.
-    if (uint32_t(sp_) & 0x1) {
-        // Clear the low bit.
-        sp_ -= 0x1;
-        returnAddress_ = *(uint8_t**)sp_;
-    } else {
-        // This is exit to C++
-        returnAddress_ = *(uint8_t**)(sp_ + ShadowStackSpace);
+    // On MIPS we have two cases: an exit to C++ will store the return address
+    // at ShadowStackSpace above sp; an exit to Ion will store the return
+    // address at sp. To distinguish the two cases, the low bit of sp (which is
+    // aligned and therefore zero) is set for Ion exits.
+    if (uintptr_t(sp) & 0x1) {
+        sp = *psp -= 0x1;  // Clear the low bit
+        return *(uint8_t**)sp;
     }
+    return *(uint8_t**)(sp + ShadowStackSpace);
 #else
 # error "Unknown architecture!"
 #endif
+}
 
-    settle();
+static uint8_t *
+ReturnAddressForJitCall(uint8_t *sp)
+{
+    // Once inside JIT code, sp always points to the word before the return
+    // address.
+    return *(uint8_t**)(sp - sizeof(void*));
+}
+
+AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
+  : module_(nullptr)
+{
+    if (!activation || activation->isInterruptedSP())
+        return;
+
+    module_ = &activation->module();
+    sp_ = activation->exitSP();
+
+    settle(ReturnAddressForExitCall(&sp_));
+}
+
+void
+AsmJSFrameIterator::operator++()
+{
+    settle(ReturnAddressForJitCall(sp_));
 }
 
 struct GetCallSite
 {
     const AsmJSModule &module;
     explicit GetCallSite(const AsmJSModule &module) : module(module) {}
     uint32_t operator[](size_t index) const {
         return module.callSite(index).returnAddressOffset();
     }
 };
 
 void
-AsmJSFrameIterator::popFrame()
+AsmJSFrameIterator::settle(uint8_t *returnAddress)
 {
-    // After adding stackDepth, sp points to the word before the return address,
-    // on both ARM and x86/x64.
-    sp_ += callsite_->stackDepth();
-    returnAddress_ = *(uint8_t**)(sp_ - sizeof(void*));
-}
-
-void
-AsmJSFrameIterator::settle()
-{
-    while (true) {
-        uint32_t target = returnAddress_ - module_->codeBase();
-        size_t lowerBound = 0;
-        size_t upperBound = module_->numCallSites();
+    uint32_t target = returnAddress - module_->codeBase();
+    size_t lowerBound = 0;
+    size_t upperBound = module_->numCallSites();
 
-        size_t match;
-        if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
-            callsite_ = nullptr;
-            return;
-        }
-
-        callsite_ = &module_->callSite(match);
-
-        if (callsite_->isExit()) {
-            popFrame();
-            continue;
-        }
-
-        if (callsite_->isEntry()) {
-            callsite_ = nullptr;
-            return;
-        }
-
-        JS_ASSERT(callsite_->isNormal());
+    size_t match;
+    if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) {
+        module_ = nullptr;
         return;
     }
+
+    callsite_ = &module_->callSite(match);
+
+    if (callsite_->isEntry()) {
+        module_ = nullptr;
+        return;
+    }
+
+    sp_ += callsite_->stackDepth();
+
+    if (callsite_->isExit())
+        return settle(ReturnAddressForJitCall(sp_));
+
+    JS_ASSERT(callsite_->isNormal());
 }
 
 JSAtom *
 AsmJSFrameIterator::functionDisplayAtom() const
 {
     JS_ASSERT(!done());
     return module_->functionName(callsite_->functionNameIndex());
 }
--- a/js/src/jit/AsmJSLink.h
+++ b/js/src/jit/AsmJSLink.h
@@ -18,25 +18,23 @@ class AsmJSModule;
 namespace jit { class CallSite; }
 
 // Iterates over the frames of a single AsmJSActivation.
 class AsmJSFrameIterator
 {
     const AsmJSModule *module_;
     const jit::CallSite *callsite_;
     uint8_t *sp_;
-    uint8_t *returnAddress_;
 
-    void popFrame();
-    void settle();
+    void settle(uint8_t *returnAddress);
 
   public:
     explicit AsmJSFrameIterator(const AsmJSActivation *activation);
-    void operator++() { popFrame(); settle(); }
-    bool done() const { return !callsite_; }
+    void operator++();
+    bool done() const { return !module_; }
     JSAtom *functionDisplayAtom() const;
     unsigned computeLine(uint32_t *column) const;
 };
 
 #ifdef JS_ION
 
 // Create a new JSFunction to replace originalFun as the representation of the
 // function defining the succesfully-validated module 'moduleObj'.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8477,80 +8477,46 @@ CodeGenerator::visitSetDOMProperty(LSetD
 typedef bool(*SPSFn)(JSContext *, HandleScript);
 static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter);
 static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit);
 
 bool
 CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
 {
     Register temp = ToRegister(lir->temp()->output());
-    bool inlinedFunction = lir->inlineLevel() > 0;
 
     switch (lir->type()) {
-        case MProfilerStackOp::InlineEnter:
-            // Multiple scripts can be inlined at one depth, but there is only
-            // one InlineExit node to signify this. To deal with this, if we
-            // reach the entry of another inline script on the same level, then
-            // just reset the sps metadata about the frame. We must balance
-            // calls to leave()/reenter(), so perform the balance without
-            // emitting any instrumentation. Technically the previous inline
-            // call at this same depth has reentered, but the instrumentation
-            // will be emitted at the common join point for all inlines at the
-            // same depth.
-            if (sps_.inliningDepth() == lir->inlineLevel()) {
-                sps_.leaveInlineFrame();
-                sps_.skipNextReenter();
-                sps_.reenter(masm, temp);
-            }
-
-            sps_.leave(masm, temp, /* inlinedFunction = */ true);
-            if (!sps_.enterInlineFrame())
-                return false;
-            // fallthrough
-
         case MProfilerStackOp::Enter:
             if (gen->options.spsSlowAssertionsEnabled()) {
-                if (!inlinedFunction) {
-                    saveLive(lir);
-                    pushArg(ImmGCPtr(lir->script()));
-                    if (!callVM(SPSEnterInfo, lir))
-                        return false;
-                    restoreLive(lir);
-                }
-                sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
+                saveLive(lir);
+                pushArg(ImmGCPtr(lir->script()));
+                if (!callVM(SPSEnterInfo, lir))
+                    return false;
+                restoreLive(lir);
+                sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ false);
                 return true;
             }
 
-            return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
-
-        case MProfilerStackOp::InlineExit:
-            // all inline returns were covered with ::Exit, so we just need to
-            // maintain the state of inline frames currently active and then
-            // reenter the caller
-            sps_.leaveInlineFrame();
-            sps_.reenter(masm, temp, /* inlinedFunction = */ true);
-            return true;
+            return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ false);
 
         case MProfilerStackOp::Exit:
             if (gen->options.spsSlowAssertionsEnabled()) {
-                if (!inlinedFunction) {
-                    saveLive(lir);
-                    pushArg(ImmGCPtr(lir->script()));
-                    // Once we've exited, then we shouldn't emit instrumentation for
-                    // the corresponding reenter() because we no longer have a
-                    // frame.
-                    sps_.skipNextReenter();
-                    if (!callVM(SPSExitInfo, lir))
-                        return false;
-                    restoreLive(lir);
-                }
+                saveLive(lir);
+                pushArg(ImmGCPtr(lir->script()));
+                // Once we've exited, then we shouldn't emit instrumentation for
+                // the corresponding reenter() because we no longer have a
+                // frame.
+                sps_.skipNextReenter();
+                if (!callVM(SPSExitInfo, lir))
+                    return false;
+                restoreLive(lir);
                 return true;
             }
 
-            sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction);
+            sps_.pop(masm, temp, /* inlinedFunction = */ false);
             return true;
 
         default:
             MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type");
     }
 }
 
 bool
--- a/js/src/jit/InlineList.h
+++ b/js/src/jit/InlineList.h
@@ -167,20 +167,18 @@ private:
 public:
     InlineForwardListIterator<T> & operator ++() {
         JS_ASSERT(modifyCount_ == owner_->modifyCount_);
         prev = iter;
         iter = iter->next;
         return *this;
     }
     InlineForwardListIterator<T> operator ++(int) {
-        JS_ASSERT(modifyCount_ == owner_->modifyCount_);
         InlineForwardListIterator<T> old(*this);
-        prev = iter;
-        iter = iter->next;
+        operator++();
         return old;
     }
     T * operator *() const {
         JS_ASSERT(modifyCount_ == owner_->modifyCount_);
         return static_cast<T *>(iter);
     }
     T * operator ->() const {
         JS_ASSERT(modifyCount_ == owner_->modifyCount_);
@@ -334,22 +332,26 @@ class InlineListIterator
 
   public:
     InlineListIterator<T> & operator ++() {
         iter = static_cast<Node *>(iter->next);
         return *this;
     }
     InlineListIterator<T> operator ++(int) {
         InlineListIterator<T> old(*this);
-        iter = static_cast<Node *>(iter->next);
+        operator++();
         return old;
     }
+    InlineListIterator<T> & operator --() {
+        iter = iter->prev;
+        return *this;
+    }
     InlineListIterator<T> operator --(int) {
         InlineListIterator<T> old(*this);
-        iter = iter->prev;
+        operator--();
         return old;
     }
     T * operator *() const {
         return static_cast<T *>(iter);
     }
     T * operator ->() const {
         return static_cast<T *>(iter);
     }
@@ -378,17 +380,26 @@ class InlineListReverseIterator
 
   public:
     InlineListReverseIterator<T> & operator ++() {
         iter = iter->prev;
         return *this;
     }
     InlineListReverseIterator<T> operator ++(int) {
         InlineListReverseIterator<T> old(*this);
-        iter = iter->prev;
+        operator++();
+        return old;
+    }
+    InlineListReverseIterator<T> & operator --() {
+        iter = static_cast<Node *>(iter->next);
+        return *this;
+    }
+    InlineListReverseIterator<T> operator --(int) {
+        InlineListReverseIterator<T> old(*this);
+        operator--();
         return old;
     }
     T * operator *() {
         return static_cast<T *>(iter);
     }
     T * operator ->() {
         return static_cast<T *>(iter);
     }
@@ -459,22 +470,22 @@ class InlineConcatListIterator
     typedef InlineConcatList<T> Node;
 
     explicit InlineConcatListIterator(const Node *iter)
       : iter(const_cast<Node *>(iter))
     { }
 
   public:
     InlineConcatListIterator<T> & operator ++() {
-        iter = iter->next;
-        return *iter;
+        iter = static_cast<Node *>(iter->next);
+        return *this;
     }
     InlineConcatListIterator<T> operator ++(int) {
         InlineConcatListIterator<T> old(*this);
-        iter = static_cast<Node *>(iter->next);
+        operator++();
         return old;
     }
     T * operator *() const {
         return static_cast<T *>(iter);
     }
     T * operator ->() const {
         return static_cast<T *>(iter);
     }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1408,18 +1408,17 @@ OptimizeMIR(MIRGenerator *mir)
 
     if (mir->optimizationInfo().licmEnabled()) {
         AutoTraceLog log(logger, TraceLogger::LICM);
         // LICM can hoist instructions from conditional branches and trigger
         // repeated bailouts. Disable it if this script is known to bailout
         // frequently.
         JSScript *script = mir->info().script();
         if (!script || !script->hadFrequentBailouts()) {
-            LICM licm(mir, graph);
-            if (!licm.analyze())
+            if (!LICM(mir, graph))
                 return false;
             IonSpewPass("LICM");
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
         }
     }
@@ -1503,16 +1502,31 @@ OptimizeMIR(MIRGenerator *mir)
             return false;
         IonSpewPass("DCE");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("DCE"))
             return false;
     }
 
+    // Make loops contiguious. We do this after GVN/UCE and range analysis,
+    // which can remove CFG edges, exposing more blocks that can be moved.
+    // We also disable this when profiling, since reordering blocks appears
+    // to make the profiler unhappy.
+    {
+        AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous);
+        if (!MakeLoopsContiguous(graph))
+            return false;
+        IonSpewPass("Make loops contiguous");
+        AssertExtendedGraphCoherency(graph);
+
+        if (mir->shouldCancel("Make loops contiguous"))
+            return false;
+    }
+
     // Passes after this point must not move instructions; these analyses
     // depend on knowing the final order in which instructions will execute.
 
     if (mir->optimizationInfo().edgeCaseAnalysisEnabled()) {
         AutoTraceLog log(logger, TraceLogger::EdgeCaseAnalysis);
         EdgeCaseAnalysis edgeCaseAnalysis(mir, graph);
         if (!edgeCaseAnalysis.analyzeLate())
             return false;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1402,25 +1402,28 @@ jit::AssertBasicGraphCoherency(MIRGraph 
     }
 
     JS_ASSERT(graph.numBlocks() == count);
 #endif
 }
 
 #ifdef DEBUG
 static void
-AssertReversePostOrder(MIRGraph &graph)
+AssertReversePostorder(MIRGraph &graph)
 {
     // Check that every block is visited after all its predecessors (except backedges).
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         JS_ASSERT(!block->isMarked());
 
         for (size_t i = 0; i < block->numPredecessors(); i++) {
             MBasicBlock *pred = block->getPredecessor(i);
-            JS_ASSERT_IF(!pred->isLoopBackedge(), pred->isMarked());
+            if (!pred->isMarked()) {
+                JS_ASSERT(pred->isLoopBackedge());
+                JS_ASSERT(block->backedge() == pred);
+            }
         }
 
         block->mark();
     }
 
     graph.unmarkBlocks();
 }
 #endif
@@ -1473,17 +1476,17 @@ AssertDominatorTree(MIRGraph &graph)
 
 void
 jit::AssertGraphCoherency(MIRGraph &graph)
 {
 #ifdef DEBUG
     if (!js_JitOptions.checkGraphConsistency)
         return;
     AssertBasicGraphCoherency(graph);
-    AssertReversePostOrder(graph);
+    AssertReversePostorder(graph);
 #endif
 }
 
 void
 jit::AssertExtendedGraphCoherency(MIRGraph &graph)
 {
     // Checks the basic GraphCoherency but also other conditions that
     // do not hold immediately (such as the fact that critical edges
@@ -2477,8 +2480,174 @@ jit::AnalyzeArgumentsUsage(JSContext *cx
     // arguments. The compiler can then assume that accesses through
     // arguments[i] will be on unaliased variables.
     if (script->funHasAnyAliasedFormal() && argumentsContentsObserved)
         return true;
 
     script->setNeedsArgsObj(false);
     return true;
 }
+
+// Mark all the blocks that are in the loop with the given header.
+// Returns the number of blocks marked. Set *canOsr to true if the loop is
+// reachable from both the normal entry and the OSR entry.
+size_t
+jit::MarkLoopBlocks(MIRGraph &graph, MBasicBlock *header, bool *canOsr)
+{
+#ifdef DEBUG
+    for (ReversePostorderIterator i = graph.rpoBegin(), e = graph.rpoEnd(); i != e; ++i)
+        MOZ_ASSERT(!i->isMarked(), "Some blocks already marked");
+#endif
+
+    MBasicBlock *osrBlock = graph.osrBlock();
+    *canOsr = false;
+
+    // The blocks are in RPO; start at the loop backedge, which is marks the
+    // bottom of the loop, and walk up until we get to the header. Loops may be
+    // discontiguous, so we trace predecessors to determine which blocks are
+    // actually part of the loop. The backedge is always part of the loop, and
+    // so are its predecessors, transitively, up to the loop header or an OSR
+    // entry.
+    MBasicBlock *backedge = header->backedge();
+    backedge->mark();
+    size_t numMarked = 1;
+    for (PostorderIterator i = graph.poBegin(backedge); ; ++i) {
+        MOZ_ASSERT(i != graph.poEnd(),
+                   "Reached the end of the graph while searching for the loop header");
+        MBasicBlock *block = *i;
+        // A block not marked by the time we reach it is not in the loop.
+        if (!block->isMarked())
+            continue;
+        // If we've reached the loop header, we're done.
+        if (block == header)
+            break;
+        // This block is in the loop; trace to its predecessors.
+        for (size_t p = 0, e = block->numPredecessors(); p != e; ++p) {
+            MBasicBlock *pred = block->getPredecessor(p);
+            if (pred->isMarked())
+                continue;
+
+            // Blocks dominated by the OSR entry are not part of the loop
+            // (unless they aren't reachable from the normal entry).
+            if (osrBlock && pred != header && osrBlock->dominates(pred)) {
+                *canOsr = true;
+                continue;
+            }
+
+            MOZ_ASSERT(pred->id() >= header->id() && pred->id() <= backedge->id(),
+                       "Loop block not between loop header and loop backedge");
+
+            pred->mark();
+            ++numMarked;
+
+            // A nested loop may not exit back to the enclosing loop at its
+            // bottom. If we just marked its header, then the whole nested loop
+            // is part of the enclosing loop.
+            if (pred->isLoopHeader()) {
+                MBasicBlock *innerBackedge = pred->backedge();
+                if (!innerBackedge->isMarked()) {
+                    // Mark its backedge so that we add all of its blocks to the
+                    // outer loop as we walk upwards.
+                    innerBackedge->mark();
+                    ++numMarked;
+
+                    // If the nested loop is not contiguous, we may have already
+                    // passed its backedge. If this happens, back up.
+                    if (backedge->id() > block->id()) {
+                        i = graph.poBegin(innerBackedge);
+                        --i;
+                    }
+                }
+            }
+        }
+    }
+    MOZ_ASSERT(header->isMarked(), "Loop header should be part of the loop");
+    return numMarked;
+}
+
+// Unmark all the blocks that are in the loop with the given header.
+void
+jit::UnmarkLoopBlocks(MIRGraph &graph, MBasicBlock *header)
+{
+    MBasicBlock *backedge = header->backedge();
+    for (ReversePostorderIterator i = graph.rpoBegin(header); ; ++i) {
+        MOZ_ASSERT(i != graph.rpoEnd(),
+                   "Reached the end of the graph while searching for the backedge");
+        MBasicBlock *block = *i;
+        if (block->isMarked()) {
+            block->unmark();
+            if (block == backedge)
+                break;
+        }
+    }
+
+#ifdef DEBUG
+    for (ReversePostorderIterator i = graph.rpoBegin(), e = graph.rpoEnd(); i != e; ++i)
+        MOZ_ASSERT(!i->isMarked(), "Not all blocks got unmarked");
+#endif
+}
+
+// Reorder the blocks in the loop starting at the given header to be contiguous.
+static void
+MakeLoopContiguous(MIRGraph &graph, MBasicBlock *header, size_t numMarked)
+{
+    MBasicBlock *backedge = header->backedge();
+
+    MOZ_ASSERT(header->isMarked(), "Loop header is not part of loop");
+    MOZ_ASSERT(backedge->isMarked(), "Loop backedge is not part of loop");
+
+    // If there are any blocks between the loop header and the loop backedge
+    // that are not part of the loop, prepare to move them to the end. We keep
+    // them in order, which preserves RPO.
+    ReversePostorderIterator insertIter = graph.rpoBegin(backedge);
+    insertIter++;
+    MBasicBlock *insertPt = *insertIter;
+
+    // Visit all the blocks from the loop header to the loop backedge.
+    size_t headerId = header->id();
+    size_t inLoopId = headerId;
+    size_t notInLoopId = inLoopId + numMarked;
+    ReversePostorderIterator i = graph.rpoBegin(header);
+    for (;;) {
+        MBasicBlock *block = *i++;
+        MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(),
+                   "Loop backedge should be last block in loop");
+
+        if (block->isMarked()) {
+            // This block is in the loop.
+            block->unmark();
+            block->setId(inLoopId++);
+            // If we've reached the loop backedge, we're done!
+            if (block == backedge)
+                break;
+        } else {
+            // This block is not in the loop. Move it to the end.
+            graph.moveBlockBefore(insertPt, block);
+            block->setId(notInLoopId++);
+        }
+    }
+    MOZ_ASSERT(header->id() == headerId, "Loop header id changed");
+    MOZ_ASSERT(inLoopId == headerId + numMarked, "Wrong number of blocks kept in loop");
+    MOZ_ASSERT(notInLoopId == (insertIter != graph.rpoEnd() ? insertPt->id() : graph.numBlocks()),
+               "Wrong number of blocks moved out of loop");
+}
+
+// Reorder the blocks in the graph so that loops are contiguous.
+bool
+jit::MakeLoopsContiguous(MIRGraph &graph)
+{
+    // Visit all loop headers (in any order).
+    for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) {
+        MBasicBlock *header = *i;
+        if (!header->isLoopHeader())
+            continue;
+
+        // Mark all blocks that are actually part of the loop.
+        bool canOsr;
+        size_t numMarked = MarkLoopBlocks(graph, header, &canOsr);
+
+        // Move all blocks between header and backedge that aren't marked to
+        // the end of the loop, making the loop itself contiguous.
+        MakeLoopContiguous(graph, header, numMarked);
+    }
+
+    return true;
+}
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -26,16 +26,25 @@ SplitCriticalEdges(MIRGraph &graph);
 enum Observability {
     ConservativeObservability,
     AggressiveObservability
 };
 
 bool
 EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe);
 
+size_t
+MarkLoopBlocks(MIRGraph &graph, MBasicBlock *header, bool *canOsr);
+
+void
+UnmarkLoopBlocks(MIRGraph &graph, MBasicBlock *header);
+
+bool
+MakeLoopsContiguous(MIRGraph &graph);
+
 bool
 EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph);
 
 bool
 EliminateDeadCode(MIRGenerator *mir, MIRGraph &graph);
 
 bool
 ApplyTypeInformation(MIRGenerator *mir, MIRGraph &graph);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -826,26 +826,16 @@ IonBuilder::buildInline(IonBuilder *call
         return false;
 
     current->setCallerResumePoint(callerResumePoint);
 
     // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock *predecessor = callerBuilder->current;
     JS_ASSERT(predecessor == callerResumePoint->block());
 
-    // All further instructions generated in from this scope should be
-    // considered as part of the function that we're inlining. We also need to
-    // keep track of the inlining depth because all scripts inlined on the same
-    // level contiguously have only one InlineExit node.
-    if (instrumentedProfiling()) {
-        predecessor->add(MProfilerStackOp::New(alloc(), script(),
-                                               MProfilerStackOp::InlineEnter,
-                                               inliningDepth_));
-    }
-
     predecessor->end(MGoto::New(alloc(), current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Initialize scope chain slot to Undefined.  It's set later by |initScopeChain|.
     MInstruction *scope = MConstant::New(alloc(), UndefinedValue());
     current->add(scope);
     current->initSlot(info().scopeChainSlot(), scope);
@@ -3696,19 +3686,18 @@ IonBuilder::processReturn(JSOp op)
         def = current->getSlot(info().returnValueSlot());
         break;
 
       default:
         def = nullptr;
         MOZ_ASSUME_UNREACHABLE("unknown return op");
     }
 
-    if (instrumentedProfiling()) {
-        current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit,
-                                           inliningDepth_));
+    if (instrumentedProfiling() && inliningDepth_ == 0) {
+        current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit));
     }
     MReturn *ret = MReturn::New(alloc(), def);
     current->end(ret);
 
     if (!graph().addReturn(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
@@ -4043,20 +4032,16 @@ IonBuilder::inlineScriptedCall(CallInfo 
 
     // Create return block.
     jsbytecode *postCall = GetNextPc(pc);
     MBasicBlock *returnBlock = newBlock(nullptr, postCall);
     if (!returnBlock)
         return false;
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
-    // When profiling add InlineExit instruction to indicate end of inlined function.
-    if (instrumentedProfiling())
-        returnBlock->add(MProfilerStackOp::New(alloc(), nullptr, MProfilerStackOp::InlineExit));
-
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     if (returns.empty()) {
         // Inlining of functions that have no exit is not supported.
         calleeScript->setUninlineable();
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -1956,19 +1956,20 @@ MacroAssembler::spsMarkJit(SPSProfiler *
     push(temp); // +4: Did we push an sps frame.
     branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled);
 
     Label stackFull;
     // We always need the "safe" versions, because these are used in trampolines
     // and won't be regenerated when SPS state changes.
     spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
+    // Push a C++ frame with non-copy label
     storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfLabel()));
     storePtr(framePtr,              Address(temp, ProfileEntry::offsetOfSpOrScript()));
-    store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
+    store32(Imm32(0),               Address(temp, ProfileEntry::offsetOfLineOrPc()));
     store32(Imm32(ProfileEntry::IS_CPP_ENTRY), Address(temp, ProfileEntry::offsetOfFlags()));
 
     /* Always increment the stack size, whether or not we actually pushed. */
     bind(&stackFull);
     loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp);
     add32(Imm32(1), Address(temp, 0));
 
     bind(&spsNotEnabled);
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -1081,45 +1081,47 @@ class MacroAssembler : public MacroAssem
         bind(&stackFull);
     }
 
     // spsPushFrame variant for Ion-optimized scripts.
     void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, 0, temp, &stackFull);
 
+        // Push a JS frame with a copy label
         storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfLabel()));
         storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfSpOrScript()));
         store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
-        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
+        store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->sizePointer()), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
     // spsPushFrame variant for Baseline-optimized scripts.
     void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script,
                       Register temp, Register temp2)
     {
         Label stackFull;
         spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
+        // Push a JS frame with a copy label
         loadPtr(str, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfLabel()));
 
         loadPtr(script, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfSpOrScript()));
 
         // Store 0 for PCIdx because that's what interpreter does.
         // (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
         //  with 0 pcIdx).
         store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc()));
-        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
+        store32(Imm32(ProfileEntry::FRAME_LABEL_COPY), Address(temp, ProfileEntry::offsetOfFlags()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
         movePtr(ImmPtr(p->addressOfSizePointer()), temp);
         loadPtr(Address(temp, 0), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
--- a/js/src/jit/LICM.cpp
+++ b/js/src/jit/LICM.cpp
@@ -1,382 +1,257 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/LICM.h"
 
-#include <stdio.h>
-
+#include "jit/IonAnalysis.h"
 #include "jit/IonSpewer.h"
-#include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 
 using namespace js;
 using namespace js::jit;
 
-namespace {
+// Test whether any instruction in the loop possiblyCalls().
+static bool
+LoopContainsPossibleCall(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge)
+{
+    for (auto i(graph.rpoBegin(header)); ; ++i) {
+        MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
+        MBasicBlock *block = *i;
+        if (!block->isMarked())
+            continue;
 
-typedef Vector<MInstruction*, 1, IonAllocPolicy> InstructionQueue;
+        for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) {
+            MInstruction *ins = *insIter;
+            if (ins->possiblyCalls()) {
+                IonSpew(IonSpew_LICM, "    Possile call found at %s%u", ins->opName(), ins->id());
+                return true;
+            }
+        }
 
-class Loop
+        if (block == backedge)
+            break;
+    }
+    return false;
+}
+
+// When a nested loop has no exits back into what would be its parent loop,
+// MarkLoopBlocks on the parent loop doesn't mark the blocks of the nested
+// loop, since they technically aren't part of the loop. However, AliasAnalysis
+// currently does consider such nested loops to be part of their parent
+// loops. Consequently, we can't use IsInLoop on dependency() values; we must
+// test whether a dependency() is *before* the loop, even if it is not
+// technically in the loop.
+static bool
+IsBeforeLoop(MDefinition *ins, MBasicBlock *header)
 {
-    MIRGenerator *mir;
+    return ins->block()->id() < header->id();
+}
+
+// Test whether the given instruction is inside the loop (and thus not
+// loop-invariant).
+static bool
+IsInLoop(MDefinition *ins)
+{
+    return ins->block()->isMarked();
+}
 
-  public:
-    // Loop code may return three values:
-    enum LoopReturn {
-        LoopReturn_Success,
-        LoopReturn_Error, // Compilation failure.
-        LoopReturn_Skip   // The loop is not suitable for LICM, but there is no error.
-    };
+// Test whether the given instruction is cheap and not worth hoisting unless
+// one of its users will be hoisted as well.
+static bool
+RequiresHoistedUse(const MDefinition *ins, bool hasCalls)
+{
+    if (ins->isConstantElements())
+        return true;
+
+    if (ins->isBox()) {
+        MOZ_ASSERT(!ins->toBox()->input()->isBox(),
+                "Box of a box could lead to unbounded recursion");
+        return true;
+    }
+
+    // Integer constants are usually cheap and aren't worth hoisting on their
+    // own, in general. Floating-point constants typically are worth hoisting,
+    // unless they'll end up being spilled (eg. due to a call).
+    if (ins->isConstant() && (!IsFloatingPointType(ins->type()) || hasCalls))
+        return true;
+
+    return false;
+}
+
+// Test whether the given instruction has any operands defined within the loop.
+static bool
+HasOperandInLoop(MInstruction *ins, bool hasCalls)
+{
+    // An instruction is only loop invariant if it and all of its operands can
+    // be safely hoisted into the loop preheader.
+    for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
+        MDefinition *op = ins->getOperand(i);
+
+        if (!IsInLoop(op))
+            continue;
+
+        if (RequiresHoistedUse(op, hasCalls)) {
+            // Recursively test for loop invariance. Note that the recursion is
+            // bounded because we require RequiresHoistedUse to be set at each
+            // level.
+            if (!HasOperandInLoop(op->toInstruction(), hasCalls))
+                continue;
+        }
+
+        return true;
+    }
+    return false;
+}
 
-  public:
-    // A loop is constructed on a backedge found in the control flow graph.
-    Loop(MIRGenerator *mir, MBasicBlock *footer);
+// Test whether the given instruction is hoistable, ignoring memory
+// dependencies.
+static bool
+IsHoistableIgnoringDependency(MInstruction *ins, bool hasCalls)
+{
+    return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist() &&
+           !HasOperandInLoop(ins, hasCalls);
+}
+
+// Test whether the given instruction has a memory dependency inside the loop.
+static bool
+HasDependencyInLoop(MInstruction *ins, MBasicBlock *header)
+{
+    // Don't hoist if this instruction depends on a store inside the loop.
+    if (MDefinition *dep = ins->dependency())
+        return !IsBeforeLoop(dep, header);
+    return false;
+}
 
-    // Initializes the loop, finds all blocks and instructions contained in the loop.
-    LoopReturn init();
+// Test whether the given instruction is hoistable.
+static bool
+IsHoistable(MInstruction *ins, MBasicBlock *header, bool hasCalls)
+{
+    return IsHoistableIgnoringDependency(ins, hasCalls) && !HasDependencyInLoop(ins, header);
+}
+
+// In preparation for hoisting an instruction, hoist any of its operands which
+// were too cheap to hoist on their own.
+static void
+MoveDeferredOperands(MInstruction *ins, MInstruction *hoistPoint, bool hasCalls)
+{
+    // If any of our operands were waiting for a user to be hoisted, make a note
+    // to hoist them.
+    for (size_t i = 0, e = ins->numOperands(); i != e; ++i) {
+        MDefinition *op = ins->getOperand(i);
+        if (!IsInLoop(op))
+            continue;
+        MOZ_ASSERT(RequiresHoistedUse(op, hasCalls),
+                   "Deferred loop-invariant operand is not cheap");
+        MInstruction *opIns = op->toInstruction();
+
+        // Recursively move the operands. Note that the recursion is bounded
+        // because we require RequiresHoistedUse to be set at each level.
+        MoveDeferredOperands(opIns, hoistPoint, hasCalls);
+
+        IonSpew(IonSpew_LICM, "    Hoisting %s%u (now that a user will be hoisted)",
+                opIns->opName(), opIns->id());
 
-    // Identifies hoistable loop invariant instructions and moves them out of the loop.
-    bool optimize();
+        opIns->block()->moveBefore(hoistPoint, opIns);
+    }
+}
+
+static void
+VisitLoopBlock(MBasicBlock *block, MBasicBlock *header, MInstruction *hoistPoint, bool hasCalls)
+{
+    for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ) {
+        MInstruction *ins = *insIter++;
+
+        if (!IsHoistable(ins, header, hasCalls)) {
+#ifdef DEBUG
+            if (IsHoistableIgnoringDependency(ins, hasCalls)) {
+                IonSpew(IonSpew_LICM, "    %s%u isn't hoistable due to dependency on %s%u",
+                        ins->opName(), ins->id(),
+                        ins->dependency()->opName(), ins->dependency()->id());
+            }
+#endif
+            continue;
+        }
 
-  private:
-    // These blocks define the loop.  header_ points to the loop header
-    MBasicBlock *header_;
+        // Don't hoist a cheap constant if it doesn't enable us to hoist one of
+        // its uses. We want those instructions as close as possible to their
+        // use, to minimize register pressure.
+        if (RequiresHoistedUse(ins, hasCalls)) {
+            IonSpew(IonSpew_LICM, "    %s%u will be hoisted only if its users are",
+                    ins->opName(), ins->id());
+            continue;
+        }
+
+        // Hoist operands which were too cheap to hoist on their own.
+        MoveDeferredOperands(ins, hoistPoint, hasCalls);
+
+        IonSpew(IonSpew_LICM, "    Hoisting %s%u", ins->opName(), ins->id());
 
-    // The pre-loop block is the first predecessor of the loop header.  It is where
-    // the loop is first entered and where hoisted instructions will be placed.
-    MBasicBlock* preLoop_;
+        // Move the instruction to the hoistPoint.
+        block->moveBefore(hoistPoint, ins);
+    }
+}
+
+static void
+VisitLoop(MIRGraph &graph, MBasicBlock *header)
+{
+    MInstruction *hoistPoint = header->loopPredecessor()->lastIns();
+
+    IonSpew(IonSpew_LICM, "  Visiting loop with header block%u, hoisting to %s%u",
+            header->id(), hoistPoint->opName(), hoistPoint->id());
+
+    MBasicBlock *backedge = header->backedge();
 
     // This indicates whether the loop contains calls or other things which
     // clobber most or all floating-point registers. In such loops,
     // floating-point constants should not be hoisted unless it enables further
     // hoisting.
-    bool containsPossibleCall_;
-
-    TempAllocator &alloc() const {
-        return mir->alloc();
-    }
-
-    bool hoistInstructions(InstructionQueue &toHoist);
-
-    // Utility methods for invariance testing and instruction hoisting.
-    bool isInLoop(MDefinition *ins);
-    bool isBeforeLoop(MDefinition *ins);
-    bool isLoopInvariant(MInstruction *ins);
-
-    // This method determines if this block hot within a loop.  That is, if it's
-    // always or usually run when the loop executes
-    bool checkHotness(MBasicBlock *block);
+    bool hasCalls = LoopContainsPossibleCall(graph, header, backedge);
 
-    // Worklist and worklist usage methods
-    InstructionQueue worklist_;
-    bool insertInWorklist(MInstruction *ins);
-    MInstruction* popFromWorklist();
-
-    inline bool isHoistable(const MDefinition *ins) const {
-        return ins->isMovable() && !ins->isEffectful() && !ins->neverHoist();
-    }
+    for (auto i(graph.rpoBegin(header)); ; ++i) {
+        MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop");
+        MBasicBlock *block = *i;
+        if (!block->isMarked())
+            continue;
 
-    bool requiresHoistedUse(const MDefinition *ins) const;
-};
+        VisitLoopBlock(block, header, hoistPoint, hasCalls);
 
-} /* namespace anonymous */
-
-LICM::LICM(MIRGenerator *mir, MIRGraph &graph)
-  : mir(mir), graph(graph)
-{
+        if (block == backedge)
+            break;
+    }
 }
 
 bool
-LICM::analyze()
+jit::LICM(MIRGenerator *mir, MIRGraph &graph)
 {
-    IonSpew(IonSpew_LICM, "Beginning LICM pass.");
+    IonSpew(IonSpew_LICM, "Beginning LICM pass");
 
-    // Iterate in RPO to visit outer loops before inner loops.
-    for (ReversePostorderIterator i(graph.rpoBegin()); i != graph.rpoEnd(); i++) {
+    // Iterate in RPO to visit outer loops before inner loops. We'd hoist the
+    // same things either way, but outer first means we do a little less work.
+    for (auto i(graph.rpoBegin()), e(graph.rpoEnd()); i != e; ++i) {
         MBasicBlock *header = *i;
-
-        // Skip non-headers and self-loops.
-        if (!header->isLoopHeader() || header->numPredecessors() < 2)
+        if (!header->isLoopHeader())
             continue;
 
-        // Attempt to optimize loop.
-        Loop loop(mir, header);
+        bool canOsr;
+        MarkLoopBlocks(graph, header, &canOsr);
 
-        Loop::LoopReturn lr = loop.init();
-        if (lr == Loop::LoopReturn_Error)
+        // Hoisting out of a loop that has an entry from the OSR block in
+        // addition to its normal entry is tricky. In theory we could clone
+        // the instruction and insert phis.
+        if (!canOsr)
+            VisitLoop(graph, header);