Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Jun 2014 17:34:13 -0400
changeset 206561 9aa487e0bce42de277a91d3a9f19e9933058efe4
parent 206560 cfd4978a9a371de139922243077ccb6ec365e454 (current diff)
parent 206476 62d33e3ba5148906105567837faf78491802ed1d (diff)
child 206562 0e387a55464687d5a8224c90bb046af46294bceb
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [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 inbound. a=merge
browser/app/profile/firefox.js
browser/base/content/fonts/ClearSans-Regular.woff
browser/base/content/fonts/FiraSans-Light.woff
browser/base/content/fonts/FiraSans-Regular.woff
browser/installer/package-manifest.in
layout/base/tests/marionette/manifest.ini
layout/base/tests/marionette/test_touchcaret.py
modules/libpref/src/init/all.js
testing/marionette/client/marionette/www/test_touchcaret.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/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9100fa82fc355f5201e23e400fc6b40e875304ed"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a819a94a572c7b32556435491ed8eaab841a95ff"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="6e2a3b589d1e8cc1d9df25f5e630ce30a0aa39f3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -121,16 +121,16 @@
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
-  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="c3ee0c875393607430086f942950d1b3f496ab0e"/>
+  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0e31f35a2a77301e91baa8a237aa9e9fa4076084"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="0a2b7e94dce4989a3740fea6f6e3152978216c88"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="cba8ebe395652e62a911d885cd6ac1bb4ad3ed57"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
-  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="dd94b2e17a146cb782d71933d25dcaa9c060e6ce"/>
+  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d0aa65b140a45016975ed0ecf35f280dd336e1d3"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9100fa82fc355f5201e23e400fc6b40e875304ed"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="a819a94a572c7b32556435491ed8eaab841a95ff"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="6e2a3b589d1e8cc1d9df25f5e630ce30a0aa39f3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
@@ -114,17 +114,17 @@
   <project name="platform/system/netd" path="system/netd" revision="ea8103eae5642621ca8202e00620f4ca954ed413"/>
   <project name="platform/system/security" path="system/security" revision="360f51f7af191316cd739f229db1c5f7233be063"/>
   <project name="platform/system/vold" path="system/vold" revision="153df4d067a4149c7d78f1c92fed2ce2bd6a272e"/>
   <default remote="caf" revision="jb_3.2" sync-j="4"/>
   <!-- Flame specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
   <project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
   <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="95423152b66d4a41032414fc3fb8c3d9a0636c7d"/>
-  <project name="kernel/msm" path="kernel" revision="e3895712aa9ddb955f0fdac880aa553556e64c41"/>
+  <project name="kernel/msm" path="kernel" revision="ccfff630163ca9a0530701fa93b501c34042d06c"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="81c4a859d75d413ad688067829d21b7ba9205f81"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="f0689ac1914cdbc59e53bdc9edd9013dc157c299"/>
   <project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="dd925f76e4f149c3d5571b80e12f7e24bbe89c59"/>
   <project name="platform/external/dbus" path="external/dbus" revision="ea87119c843116340f5df1d94eaf8275e1055ae8"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="494c177966fdc31183a5f7af82dc9130f523da4b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="320b05a5761eb2a4816f7529c91ea49422979b55"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="5b934dc57dae25f286b0e7210dc6ff47f3244927"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "e32dee285e41ff8be7779ab4adb3db81a5b36570", 
+    "revision": "99abcedfe288d6d29352b2add96b5ace90543985", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="6e2a3b589d1e8cc1d9df25f5e630ce30a0aa39f3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="857129928b6e56a809cee9d5445effb8fa9f1c2c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3f3963fe6e165c0a7afc6222e1137d0862c70b30"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="89812422efe8df364ddf364b4740030496181277"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="6a68dde81fcbe3c6bbd45bafdeb94c3f269bdc20"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- 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
@@ -633,21 +633,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/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/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -3431,36 +3431,41 @@ BluetoothDBusService::ToggleCalls(Blueto
 
   DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
 }
 #endif // MOZ_B2G_RIL
 
 class OnUpdateSdpRecordsRunnable : public nsRunnable
 {
 public:
-  OnUpdateSdpRecordsRunnable(const nsAString& aObjectPath,
+  OnUpdateSdpRecordsRunnable(const nsAString& aDeviceAddress,
                              BluetoothProfileManagerBase* aManager)
-    : mManager(aManager)
+    : mDeviceAddress(aDeviceAddress)
+    , mManager(aManager)
   {
-    MOZ_ASSERT(!aObjectPath.IsEmpty());
+    MOZ_ASSERT(!aDeviceAddress.IsEmpty());
     MOZ_ASSERT(aManager);
-
-    mDeviceAddress = GetAddressFromObjectPath(aObjectPath);
   }
 
   nsresult
   Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mManager->OnUpdateSdpRecords(mDeviceAddress);
 
     return NS_OK;
   }
 
+  void
+  GetDeviceAddress(nsAString& aRetDeviceAddress)
+  {
+    aRetDeviceAddress = mDeviceAddress;
+  }
+
 private:
   nsString mDeviceAddress;
   BluetoothProfileManagerBase* mManager;
 };
 
 class OnGetServiceChannelRunnable : public nsRunnable
 {
 public:
@@ -3636,57 +3641,99 @@ public:
   }
 
   void Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
     MOZ_ASSERT(sDBusConnection);
     MOZ_ASSERT(!sAdapterPath.IsEmpty());
 
-    const nsString objectPath =
-      GetObjectPathFromAddress(sAdapterPath, mDeviceAddress);
+    // We first guess that the device doesn't exist at all. So we use BlueZ
+    // API "CreateDevice" to create an object path for the BluetoothDevice
+    // object. "CreateDevice" will connect to the remote device and retrieve
+    // SDP records of the target.
+    NS_ConvertUTF16toUTF8 address(mDeviceAddress);
+    const char* cAddress = address.get();
 
     // I choose to use raw pointer here because this is going to be passed as an
     // argument into SendWithReply() at once.
     OnUpdateSdpRecordsRunnable* callbackRunnable =
-      new OnUpdateSdpRecordsRunnable(objectPath, mBluetoothProfileManager);
-
-    sDBusConnection->SendWithReply(DiscoverServicesCallback,
-                                   (void*)callbackRunnable, -1,
-                                   BLUEZ_DBUS_BASE_IFC,
-                                   NS_ConvertUTF16toUTF8(objectPath).get(),
-                                   DBUS_DEVICE_IFACE,
-                                   "DiscoverServices",
-                                   DBUS_TYPE_STRING, &EmptyCString(),
-                                   DBUS_TYPE_INVALID);
+      new OnUpdateSdpRecordsRunnable(mDeviceAddress, mBluetoothProfileManager);
+
+    sDBusConnection->SendWithReply(
+      CreateDeviceCallback, callbackRunnable, -1,
+      BLUEZ_DBUS_BASE_IFC,
+      NS_ConvertUTF16toUTF8(sAdapterPath).get(),
+      DBUS_ADAPTER_IFACE,
+      "CreateDevice",
+      DBUS_TYPE_STRING, &cAddress,
+      DBUS_TYPE_INVALID);
   }
 
 protected:
+  static void CreateDeviceCallback(DBusMessage* aMsg, void* aData)
+  {
+    MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
+
+    nsAutoString errorString;
+    OnUpdateSdpRecordsRunnable* r =
+      static_cast<OnUpdateSdpRecordsRunnable*>(aData);
+
+    if (IsDBusMessageError(aMsg, nullptr, errorString)) {
+      // If the device already exists it comes here. If we want to refresh its
+      // SDP records then we have to do "DiscoverServices"
+      BT_LOGR("%s", NS_ConvertUTF16toUTF8(errorString).get());
+
+      nsString deviceAddress;
+      r->GetDeviceAddress(deviceAddress);
+
+      const nsString objectPath =
+        GetObjectPathFromAddress(sAdapterPath, deviceAddress);
+
+      sDBusConnection->SendWithReply(DiscoverServicesCallback,
+                                     aData, -1,
+                                     BLUEZ_DBUS_BASE_IFC,
+                                     NS_ConvertUTF16toUTF8(objectPath).get(),
+                                     DBUS_DEVICE_IFACE,
+                                     "DiscoverServices",
+                                     DBUS_TYPE_STRING, &EmptyCString(),
+                                     DBUS_TYPE_INVALID);
+      return;
+    }
+
+    NS_DispatchToMainThread(r);
+  }
+
   static void DiscoverServicesCallback(DBusMessage* aMsg, void* aData)
   {
     MOZ_ASSERT(!NS_IsMainThread()); // I/O thread
 
-    nsRefPtr<OnUpdateSdpRecordsRunnable> r(
-      static_cast<OnUpdateSdpRecordsRunnable*>(aData));
+    nsAutoString errorStr;
+
+    if (IsDBusMessageError(aMsg, nullptr, errorStr)) {
+      BT_LOGR("%s", NS_ConvertUTF16toUTF8(errorStr).get());
+    }
+
+    OnUpdateSdpRecordsRunnable* r =
+      static_cast<OnUpdateSdpRecordsRunnable*>(aData);
     NS_DispatchToMainThread(r);
   }
 
 private:
   const nsString mDeviceAddress;
   BluetoothProfileManagerBase* mBluetoothProfileManager;
 };
 
 bool
 BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress,
                                        BluetoothProfileManagerBase* aManager)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  Task* task = new UpdateSdpRecordsTask(aDeviceAddress, aManager);
-  DispatchToDBusThread(task);
+  DispatchToDBusThread(new UpdateSdpRecordsTask(aDeviceAddress, aManager));
 
   return true;
 }
 
 void
 BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
                                BlobParent* aBlobParent,
                                BlobChild* aBlobChild,
--- a/dom/bluetooth/tests/marionette/head.js
+++ b/dom/bluetooth/tests/marionette/head.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
 
 // https://github.com/mozilla-b2g/platform_external_qemu/blob/master/vl-android.c#L765
 // static int bt_hci_parse(const char *str) {
 //   ...
 //   bdaddr.b[0] = 0x52;
 //   bdaddr.b[1] = 0x54;
@@ -31,17 +29,24 @@ const EMULATOR_CLASS = 0x58020c;
 
 // Use same definition in QEMU for special bluetooth address,
 // which were defined at external/qemu/hw/bt.h:
 const BDADDR_ANY   = "00:00:00:00:00:00";
 const BDADDR_ALL   = "ff:ff:ff:ff:ff:ff";
 const BDADDR_LOCAL = "ff:ff:ff:00:00:00";
 
 // A user friendly name for remote BT device.
-const REMOTE_DEVICE_NAME = "Remote BT Device";
+const REMOTE_DEVICE_NAME = "Remote_BT_Device";
+
+// A system message signature of pairing request event
+const BT_PAIRING_REQ = "bluetooth-pairing-request";
+
+// Passkey and pincode used to reply pairing requst
+const BT_PAIRING_PASSKEY = 123456;
+const BT_PAIRING_PINCODE = "ABCDEFG";
 
 let Promise =
   SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
 
 let bluetoothManager;
 
 let pendingEmulatorCmdCount = 0;
 
@@ -76,16 +81,43 @@ function runEmulatorCmdSafe(aCommand) {
       deferred.reject(aResult);
     }
   });
 
   return deferred.promise;
 }
 
 /**
+ * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
+ *
+ * Fulfill params: A DOMEvent.
+ * Reject params: A DOMEvent.
+ *
+ * @param aRequest
+ *        A DOMRequest instance.
+ *
+ * @return A deferred promise.
+ */
+function wrapDomRequestAsPromise(aRequest) {
+  let deferred = Promise.defer();
+
+  ok(aRequest instanceof DOMRequest,
+     "aRequest is instanceof " + aRequest.constructor);
+
+  aRequest.onsuccess = function(aEvent) {
+    deferred.resolve(aEvent);
+  };
+  aRequest.onerror = function(aEvent) {
+    deferred.reject(aEvent);
+  };
+
+  return deferred.promise;
+}
+
+/**
  * Add a Bluetooth remote device to scatternet and set its properties.
  *
  * Use QEMU command 'bt remote add' to add a virtual Bluetooth remote
  * and set its properties by setEmulatorDeviceProperty().
  *
  * Fulfill params:
  *   result -- bluetooth address of the remote device.
  * Reject params: (none)
@@ -183,144 +215,291 @@ function setEmulatorDeviceProperty(aAddr
  * @param aPropertyName
  *        The property name of Bluetooth device.
  *
  * @return A deferred promise.
  */
 function getEmulatorDeviceProperty(aAddress, aPropertyName) {
   let cmd = "bt property " + aAddress + " " + aPropertyName;
   return runEmulatorCmdSafe(cmd)
-    .then(function(aResults) {
-      return aResults[0];
-    });
+         .then(aResults => aResults[0]);
 }
 
 /**
- * Start dicovering Bluetooth devices.
+ * Start discovering Bluetooth devices.
  *
  * Allows the device's adapter to start seeking for remote devices.
  *
  * Fulfill params: (none)
  * Reject params: a DOMError
  *
  * @param aAdapter
- *        A BluetoothAdapter which is used to interact with local BT dev
+ *        A BluetoothAdapter which is used to interact with local BT device.
  *
  * @return A deferred promise.
  */
 function startDiscovery(aAdapter) {
-  let deferred = Promise.defer();
+  let request = aAdapter.startDiscovery();
 
-  let request = aAdapter.startDiscovery();
-  request.onsuccess = function () {
-    log("  Start discovery - Success");
-    // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
-    //     Currently, discovering state wouldn't change immediately here.
-    //     We would turn on this check when the redesigned API are landed.
-    // is(aAdapter.discovering, true, "BluetoothAdapter.discovering");
-    deferred.resolve();
-  }
-  request.onerror = function (aEvent) {
-    ok(false, "Start discovery - Fail");
-    deferred.reject(aEvent.target.error);
-  }
-
-  return deferred.promise;
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve() {
+      // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
+      //     Currently, discovering state wouldn't change immediately here.
+      //     We would turn on this check when the redesigned API are landed.
+      // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
+      log("  Start discovery - Success");
+    }, function reject(aEvent) {
+      ok(false, "Start discovery - Fail");
+      throw aEvent.target.error;
+    });
 }
 
 /**
- * Stop dicovering Bluetooth devices.
+ * Stop discovering Bluetooth devices.
  *
  * Allows the device's adapter to stop seeking for remote devices.
  *
  * Fulfill params: (none)
  * Reject params: a DOMError
  *
  * @param aAdapter
  *        A BluetoothAdapter which is used to interact with local BT device.
  *
  * @return A deferred promise.
  */
 function stopDiscovery(aAdapter) {
+  let request = aAdapter.stopDiscovery();
+
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve() {
+      // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
+      //     Currently, discovering state wouldn't change immediately here.
+      //     We would turn on this check when the redesigned API are landed.
+      // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
+      log("  Stop discovery - Success");
+    }, function reject(aEvent) {
+      ok(false, "Stop discovery - Fail");
+      throw aEvent.target.error;
+    });
+}
+
+/**
+ * Wait for 'devicefound' event of specified devices.
+ *
+ * Resolve if that every specified devices has been found.  Never reject.
+ *
+ * Fulfill params: an array which contains addresses of remote devices.
+ *
+ * @param aAdapter
+ *        A BluetoothAdapter which is used to interact with local BT device.
+ * @param aRemoteAddresses
+ *        An array which contains addresses of remote devices.
+ *
+ * @return A deferred promise.
+ */
+function waitForDevicesFound(aAdapter, aRemoteAddresses) {
   let deferred = Promise.defer();
 
-  let request = aAdapter.stopDiscovery();
-  request.onsuccess = function () {
-    log("  Stop discovery - Success");
-    // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
-    //     Currently, discovering state wouldn't change immediately here.
-    //     We would turn on this check when the redesigned API are landed.
-    // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
-    deferred.resolve();
-  }
-  request.onerror = function (aEvent) {
-    ok(false, "Stop discovery - Fail");
-    deferred.reject(aEvent.target.error);
-  }
+  var addrArray = [];
+  aAdapter.addEventListener("devicefound", function onevent(aEvent) {
+    if(aRemoteAddresses.indexOf(aEvent.device.address) != -1) {
+      addrArray.push(aEvent.device.address);
+    }
+    if(addrArray.length == aRemoteAddresses.length) {
+      aAdapter.removeEventListener("devicefound", onevent);
+      ok(true, "BluetoothAdapter has found all remote devices.");
+
+      deferred.resolve(addrArray);
+    }
+  });
+
   return deferred.promise;
 }
 
 /**
+ * Start discovering Bluetooth devices and wait for 'devicefound' events.
+ *
+ * Allows the device's adapter to start seeking for remote devices and wait for
+ * the 'devicefound' events of specified devices.
+ *
+ * Fulfill params: an array of addresses of found devices.
+ *
+ * @param aAdapter
+ *        A BluetoothAdapter which is used to interact with local BT device.
+ * @param aRemoteAddresses
+ *        An array which contains addresses of remote devices.
+ *
+ * @return A deferred promise.
+ */
+function startDiscoveryAndWaitDevicesFound(aAdapter, aRemoteAddresses) {
+  let promises = [];
+
+  promises.push(waitForDevicesFound(aAdapter, aRemoteAddresses));
+  promises.push(startDiscovery(aAdapter));
+  return Promise.all(promises)
+         .then(aResults => aResults[0]);
+}
+
+/**
+ * Start pairing a remote device.
+ *
+ * Start pairing a remote device with the device's adapter.
+ *
+ * Fulfill params: (none)
+ * Reject params: a DOMError
+ *
+ * @param aAdapter
+ *        A BluetoothAdapter which is used to interact with local BT device.
+ * @param aDeviceAddress
+ *        The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
+ *
+ * @return A deferred promise.
+ */
+function pair(aAdapter, aDeviceAddress) {
+  let request = aAdapter.pair(aDeviceAddress);
+
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve() {
+      log("  Pair - Success");
+    }, function reject(aEvent) {
+      ok(false, "Pair - Fail");
+      throw aEvent.target.error;
+    });
+}
+
+/**
+ * Remove the paired device from the paired device list.
+ *
+ * Remove the paired device from the paired device list of the device's adapter.
+ *
+ * Fulfill params: (none)
+ * Reject params: a DOMError
+ *
+ * @param aAdapter
+ *        A BluetoothAdapter which is used to interact with local BT device.
+ * @param aDeviceAddress
+ *        The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
+ *
+ * @return A deferred promise.
+ */
+function unpair(aAdapter, aDeviceAddress) {
+  let request = aAdapter.unpair(aDeviceAddress);
+
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve() {
+      log("  Unpair - Success");
+    }, function reject(aEvent) {
+      ok(false, "Unpair - Fail");
+      throw aEvent.target.error;
+    });
+}
+
+/**
+ * Start pairing a remote device and wait for "pairedstatuschanged" event.
+ *
+ * Start pairing a remote device with the device's adapter and wait for
+ * "pairedstatuschanged" event.
+ *
+ * Fulfill params:  an array of promise results contains the fulfilled params of
+ *                  'waitForAdapterEvent' and 'pair'.
+ *
+ * @param aAdapter
+ *        A BluetoothAdapter which is used to interact with local BT device.
+ * @param aDeviceAddress
+ *        The string of remote Bluetooth address with format xx:xx:xx:xx:xx:xx.
+ *
+ * @return A deferred promise.
+ */
+function pairDeviceAndWait(aAdapter, aDeviceAddress) {
+  let promises = [];
+  promises.push(waitForAdapterEvent(aAdapter, "pairedstatuschanged"));
+  promises.push(pair(aAdapter, aDeviceAddress));
+  return Promise.all(promises);
+}
+
+/**
+ * Get paried Bluetooth devices.
+ *
+ * The getPairedDevices method is used to retrieve the full list of all devices
+ * paired with the device's adapter.
+ *
+ * Fulfill params: a shallow copy of the Array of paired BluetoothDevice
+ *                 objects.
+ * Reject params: a DOMError
+ *
+ * @param aAdapter
+ *        A BluetoothAdapter which is used to interact with local BT device.
+ *
+ * @return A deferred promise.
+ */
+function getPairedDevices(aAdapter) {
+  let request = aAdapter.getPairedDevices();
+
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve() {
+      log("  getPairedDevices - Success");
+      let pairedDevices = request.result.slice();
+      return pairedDevices;
+    }, function reject(aEvent) {
+      ok(false, "getPairedDevices - Fail");
+      throw aEvent.target.error;
+    });
+}
+
+/**
  * Get mozSettings value specified by @aKey.
  *
  * Resolve if that mozSettings value is retrieved successfully, reject
  * otherwise.
  *
  * Fulfill params:
  *   The corresponding mozSettings value of the key.
  * Reject params: (none)
  *
  * @param aKey
  *        A string.
  *
  * @return A deferred promise.
  */
 function getSettings(aKey) {
-  let deferred = Promise.defer();
+  let request = navigator.mozSettings.createLock().get(aKey);
 
-  let request = navigator.mozSettings.createLock().get(aKey);
-  request.addEventListener("success", function(aEvent) {
-    ok(true, "getSettings(" + aKey + ")");
-    deferred.resolve(aEvent.target.result[aKey]);
-  });
-  request.addEventListener("error", function() {
-    ok(false, "getSettings(" + aKey + ")");
-    deferred.reject();
-  });
-
-  return deferred.promise;
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve(aEvent) {
+      ok(true, "getSettings(" + aKey + ")");
+      return aEvent.target.result[aKey];
+    }, function reject(aEvent) {
+      ok(false, "getSettings(" + aKey + ")");
+      throw aEvent.target.error;
+    });
 }
 
 /**
  * Set mozSettings values.
  *
  * Resolve if that mozSettings value is set successfully, reject otherwise.
  *
  * Fulfill params: (none)
  * Reject params: (none)
  *
  * @param aSettings
  *        An object of format |{key1: value1, key2: value2, ...}|.
  *
  * @return A deferred promise.
  */
 function setSettings(aSettings) {
-  let deferred = Promise.defer();
+  let request = navigator.mozSettings.createLock().set(aSettings);
 
-  let request = navigator.mozSettings.createLock().set(aSettings);
-  request.addEventListener("success", function() {
-    ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
-    deferred.resolve();
-  });
-  request.addEventListener("error", function() {
-    ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
-    deferred.reject();
-  });
-
-  return deferred.promise;
+  return wrapDomRequestAsPromise(request)
+    .then(function resolve() {
+      ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
+    }, function reject(aEvent) {
+      ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
+      throw aEvent.target.error;
+    });
 }
 
 /**
  * Get mozSettings value of 'bluetooth.enabled'.
  *
  * Resolve if that mozSettings value is retrieved successfully, reject
  * otherwise.
  *
@@ -433,17 +612,17 @@ function waitForManagerEvent(aEventName)
 /**
  * Wait for one named BluetoothAdapter event.
  *
  * Resolve if that named event occurs.  Never reject.
  *
  * Fulfill params: the DOMEvent passed.
  *
  * @param aAdapter
- *        The BluetoothAdapter you want to use.
+ *        A BluetoothAdapter which is used to interact with local BT device.
  * @param aEventName
  *        The name of the EventHandler.
  *
  * @return A deferred promise.
  */
 function waitForAdapterEvent(aAdapter, aEventName) {
   let deferred = Promise.defer();
 
@@ -458,17 +637,18 @@ function waitForAdapterEvent(aAdapter, a
 }
 
 /**
  * Convenient function for setBluetoothEnabled and waitForManagerEvent
  * combined.
  *
  * Resolve if that named event occurs.  Reject if we can't set settings.
  *
- * Fulfill params: the DOMEvent passed.
+ * Fulfill params: an array of promise results contains the fulfill params of
+ *                 'waitForManagerEvent' and 'setBluetoothEnabled'.
  * Reject params: (none)
  *
  * @return A deferred promise.
  */
 function setBluetoothEnabledAndWait(aEnabled) {
   let promises = [];
 
   // Bug 969109 -  Intermittent test_dom_BluetoothManager_adapteradded.js
--- a/dom/bluetooth/tests/marionette/manifest.ini
+++ b/dom/bluetooth/tests/marionette/manifest.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 b2g = true
 browser = false
 qemu = true
 
+[test_navigate_to_default_url.py]
 [test_dom_BluetoothManager_enabled.js]
 [test_dom_BluetoothManager_adapteradded.js]
 [test_dom_BluetoothAdapter_setters.js]
 [test_dom_BluetoothAdapter_getters.js]
 [test_dom_BluetoothAdapter_discovery.js]
+[test_dom_BluetoothAdapter_pair.js]
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_discovery.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_discovery.js
@@ -1,43 +1,35 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
 
 ///////////////////////////////////////////////////////////////////////////////
 // Test Purpose:
 //   To verify that discovery process of BluetoothAdapter is correct.
-//   Use B2G emulator commands to add/remote remote devices to simulate
+//   Use B2G emulator commands to add/remove remote devices to simulate
 //   discovering behavior.
 //
 // Test Coverage:
 //   - BluetoothAdapter.startDiscovery()
 //   - BluetoothAdapter.stopDiscovery()
 //   - BluetoothAdapter.ondevicefound()
 //   - BluetoothAdapter.discovering [Temporarily turned off until BT API update]
 //
 ///////////////////////////////////////////////////////////////////////////////
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 startBluetoothTest(true, function testCaseMain(aAdapter) {
   log("Testing the discovery process of BluetoothAdapter ...");
 
-  // The properties of remote device.
-  let theProperties = {
-    "name": REMOTE_DEVICE_NAME,
-    "discoverable": true
-  };
-
   return Promise.resolve()
     .then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
-    .then(() => addEmulatorRemoteDevice(/*theProperties*/ null))
+    .then(() => addEmulatorRemoteDevice(null))
     .then(function(aRemoteAddress) {
       let promises = [];
       promises.push(waitForAdapterEvent(aAdapter, "devicefound"));
       promises.push(startDiscovery(aAdapter));
       return Promise.all(promises)
         .then(function(aResults) {
           is(aResults[0].device.address, aRemoteAddress, "BluetoothDevice.address");
         });
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_getters.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_getters.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
 
 ///////////////////////////////////////////////////////////////////////////////
 // Test Purpose:
 //   To verify that the properties of BluetoothAdapter can be updated and
 //   retrieved correctly. Use B2G emulator commands to set properties for this
 //   test case.
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_pair.js
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Purpose:
+//   To verify that pairing process of BluetoothAdapter is correct.
+//   Use B2G emulator commands to add/remove remote devices to simulate
+//   discovering behavior. With current emulator implemation, the pair method
+//   between adapter and remote device would be 'confirmation'.
+//
+// Test Coverage:
+//   - BluetoothAdapter.startDiscovery()
+//   - BluetoothAdapter.stopDiscovery()
+//   - BluetoothAdapter.pair()
+//   - BluetoothAdapter.unpair()
+//   - BluetoothAdapter.onpairedstatuschanged()
+//   - BluetoothAdapter.setPairingConfirmation()
+//
+///////////////////////////////////////////////////////////////////////////////
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+function replyPairingReq(aAdapter, aPairingEvent) {
+  switch (aPairingEvent.method) {
+    case 'confirmation':
+      log("The pairing passkey is " + aPairingEvent.passkey);
+      aAdapter.setPairingConfirmation(aPairingEvent.address, true);
+      break;
+    case 'pincode':
+      let pincode = BT_PAIRING_PINCODE;
+      aAdapter.setPinCode(aPairingEvent.address, pincode);
+      break;
+    case 'passkey':
+      let passkey = BT_PAIRING_PASSKEY;
+      aAdapter.setPasskey(aPairingEvent.address, passkey);
+      break;
+    default:
+      ok(false, "Unsupported pairing method. [" + aPairingEvent.method + "]");
+  }
+}
+
+startBluetoothTest(true, function testCaseMain(aAdapter) {
+  log("Testing the pairing process of BluetoothAdapter ...");
+
+  // listens to the system message BT_PAIRING_REQ
+  navigator.mozSetMessageHandler(BT_PAIRING_REQ,
+    (evt) => replyPairingReq(aAdapter, evt));
+
+  return Promise.resolve()
+    .then(() => removeEmulatorRemoteDevice(BDADDR_ALL))
+    .then(() => addEmulatorRemoteDevice())
+    .then((aRemoteAddress) =>
+          startDiscoveryAndWaitDevicesFound(aAdapter, [aRemoteAddress]))
+    .then((aRemoteAddresses) =>
+          stopDiscovery(aAdapter).then(() => aRemoteAddresses))
+    // 'aRemoteAddresses' is an arrary which contains addresses of discovered
+    // remote devices.
+    // Pairs with the first device in 'aRemoteAddresses' to test the functionality
+    // of BluetoothAdapter.pair and BluetoothAdapter.onpairedstatuschanged.
+    .then((aRemoteAddresses) => pairDeviceAndWait(aAdapter, aRemoteAddresses.pop()))
+    .then(() => getPairedDevices(aAdapter))
+    .then((aPairedDevices) => unpair(aAdapter, aPairedDevices.pop().address))
+    .then(() => removeEmulatorRemoteDevice(BDADDR_ALL));
+});
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_setters.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothAdapter_setters.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
 
 ///////////////////////////////////////////////////////////////////////////////
 // Test Purpose:
 //   To verify that all setters of BluetoothAdapter (except for pairing related
 //   APIs) can change properties correctly.
 //
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_adapteradded.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_adapteradded.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 startBluetoothTest(true, function testCaseMain(aAdapter) {
   log("Checking adapter attributes ...");
--- a/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_enabled.js
+++ b/dom/bluetooth/tests/marionette/test_dom_BluetoothManager_enabled.js
@@ -1,11 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2 et filetype=javascript
- * 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/. */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 function waitEitherEnabledOrDisabled() {
   let deferred = Promise.defer();
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/tests/marionette/test_navigate_to_default_url.py
@@ -0,0 +1,12 @@
+# 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/.
+
+from marionette_test import MarionetteTestCase
+
+class testNavigateToDefault(MarionetteTestCase):
+    def test_navigate_to_default_url(self):
+        try:
+            self.marionette.navigate("app://system.gaiamobile.org/index.html")
+        except:
+            self.assertTrue(Flase, "Can not navigate to system app.")
deleted file mode 100644
--- a/layout/base/tests/marionette/manifest.ini
+++ /dev/null
@@ -1,15 +0,0 @@
-[DEFAULT]
-; true if the test requires an emulator, otherwise false
-qemu = false
-
-; true if the test is compatible with the browser, otherwise false
-browser = true
-
-; true if the test is compatible with b2g, otherwise false
-b2g = true
-
-; true if the test should be skipped
-skip = false
-
-[test_touchcaret.py]
-b2g = false ; Bug 1020261
deleted file mode 100644
--- a/layout/base/tests/marionette/test_touchcaret.py
+++ /dev/null
@@ -1,339 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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 string
-
-from by import By
-from marionette import Actions
-from marionette_test import MarionetteTestCase
-
-
-class TouchCaretTest(MarionetteTestCase):
-    _input_selector = (By.ID, 'input')
-    _textarea_selector = (By.ID, 'textarea')
-    _contenteditable_selector = (By.ID, 'contenteditable')
-
-    def setUp(self):
-        # Code to execute before a tests are run.
-        MarionetteTestCase.setUp(self)
-        self.actions = Actions(self.marionette)
-
-    def openTestHtml(self, enabled=True):
-        '''Open html for testing and locate elements, and enable/disable touch
-        caret.'''
-        self.marionette.execute_script(
-            'SpecialPowers.setBoolPref("touchcaret.enabled", %s);' %
-            ('true' if enabled else 'false'))
-
-        test_html = self.marionette.absolute_url('test_touchcaret.html')
-        self.marionette.navigate(test_html)
-
-        self._input = self.marionette.find_element(*self._input_selector)
-        self._textarea = self.marionette.find_element(*self._textarea_selector)
-        self._contenteditable = self.marionette.find_element(*self._contenteditable_selector)
-
-    def is_input_or_textarea(self, element):
-        '''Return True if element is either <input> or <textarea>'''
-        return element.tag_name in ('input', 'textarea')
-
-    def get_js_selection_cmd(self, element):
-        '''Return a command snippet to get selection object.
-
-        If the element is <input> or <textarea>, return the selection object
-        associated with it. Otherwise, return the current selection object.
-
-        Note: "element" must be provided as the first argument to
-        execute_script().
-
-        '''
-        if self.is_input_or_textarea(element):
-            # We must unwrap sel so that DOMRect could be returned to Python
-            # side.
-            return '''var sel = SpecialPowers.wrap(arguments[0]).editor.selection;
-                   sel = SpecialPowers.unwrap(sel);'''
-        else:
-            return '''var sel = window.getSelection();'''
-
-    def caret_rect(self, element):
-        '''Return the caret's DOMRect object.
-
-        If the element is either <input> or <textarea>, return the caret's
-        DOMRect within the element. Otherwise, return the DOMRect of the
-        current selected caret.
-
-        '''
-        cmd = self.get_js_selection_cmd(element) +\
-            '''return sel.getRangeAt(0).getClientRects()[0];'''
-        return self.marionette.execute_script(cmd, script_args=[element])
-
-    def caret_location(self, element):
-        '''Return caret's center location by the number of characters offset
-        within the given element.
-
-        Return (x, y) coordinates of the caret's center by the number of
-        characters offset relative to the top left-hand corner of the given
-        element.
-
-        '''
-        rect = self.caret_rect(element)
-        x = rect['left'] + rect['width'] / 2.0 - element.location['x']
-        y = rect['top'] + rect['height'] / 2.0 - element.location['y']
-        return x, y
-
-    def touch_caret_location(self, element):
-        '''Return touch caret's location (based on current caret location).
-
-        Return (x, y) coordinates of the touch caret's tip relative to the top
-        left-hand corner of the given element.
-
-        '''
-        rect = self.caret_rect(element)
-        x = rect['left'] - element.location['x']
-
-        # Touch caret's tip is below the bottom of the caret. Add 5px to y
-        # should be sufficient to locate it.
-        y = rect['bottom'] + 5 - element.location['y']
-
-        return x, y
-
-    def move_caret_by_offset(self, element, offset, backward=False):
-        '''Move caret in the element by offset.'''
-        cmd = self.get_js_selection_cmd(element) +\
-            '''sel.modify("move", arguments[1], "character");'''
-        direction = 'backward' if backward else 'forward'
-
-        for i in range(offset):
-            self.marionette.execute_script(
-                cmd, script_args=[element, direction])
-
-    def move_caret_to_front(self, element):
-        if self.is_input_or_textarea(element):
-            cmd = '''arguments[0].setSelectionRange(0, 0);'''
-        else:
-            cmd = '''var sel = window.getSelection();
-                  sel.collapse(arguments[0].firstChild, 0);'''
-
-        self.marionette.execute_script(cmd, script_args=[element])
-
-    def move_caret_to_end(self, element):
-        if self.is_input_or_textarea(element):
-            cmd = '''var len = arguments[0].value.length;
-                  arguments[0].setSelectionRange(len, len);'''
-        else:
-            cmd = '''var sel = window.getSelection();
-                  sel.collapse(arguments[0].lastChild, arguments[0].lastChild.length);'''
-
-        self.marionette.execute_script(cmd, script_args=[element])
-
-    def get_content(self, element):
-        '''Return the content of the element.'''
-        if self.is_input_or_textarea(element):
-            return element.get_attribute('value')
-        else:
-            return element.text
-
-    def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc):
-        content_to_add = '!'
-        target_content = self.get_content(el)
-        target_content = target_content[:1] + content_to_add + target_content[1:]
-
-        # Get touch caret (x, y) at position 1 and 2.
-        self.move_caret_to_front(el)
-        caret0_x, caret0_y = self.caret_location(el)
-        touch_caret0_x, touch_caret0_y = self.touch_caret_location(el)
-        self.move_caret_by_offset(el, 1)
-        touch_caret1_x, touch_caret1_y = self.touch_caret_location(el)
-
-        # Tap the front of the input to make touch caret appear.
-        el.tap(caret0_x, caret0_y)
-
-        # Move touch caret
-        self.actions.flick(el, touch_caret0_x, touch_caret0_y,
-                           touch_caret1_x, touch_caret1_y).perform()
-
-        el.send_keys(content_to_add)
-        assertFunc(target_content, self.get_content(el))
-
-    def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc):
-        content_to_add = '!'
-        target_content = self.get_content(el) + content_to_add
-
-        # Tap the front of the input to make touch caret appear.
-        self.move_caret_to_front(el)
-        el.tap(*self.caret_location(el))
-
-        # Move touch caret to the bottom-right corner of the element.
-        src_x, src_y = self.touch_caret_location(el)
-        dest_x, dest_y = el.size['width'], el.size['height']
-        self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
-
-        el.send_keys(content_to_add)
-        assertFunc(target_content, self.get_content(el))
-
-    def _test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self, el, assertFunc):
-        content_to_add = '!'
-        target_content = content_to_add + self.get_content(el)
-
-        # Tap to make touch caret appear. Note: it's strange that when the caret
-        # is at the end, the rect of the caret in <textarea> cannot be obtained.
-        # A bug perhaps.
-        self.move_caret_to_end(el)
-        self.move_caret_by_offset(el, 1, backward=True)
-        el.tap(*self.caret_location(el))
-
-        # Move touch caret to the top-left corner of the input box.
-        src_x, src_y = self.touch_caret_location(el)
-        dest_x, dest_y = 0, 0
-        self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
-
-        el.send_keys(content_to_add)
-        assertFunc(target_content, self.get_content(el))
-
-    def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self, el, assertFunc):
-        content_to_add = '!'
-        non_target_content = content_to_add + self.get_content(el)
-
-        # Get touch caret timeout in millisecond, and convert it to second.
-        timeout = self.marionette.execute_script(
-            'return SpecialPowers.getIntPref("touchcaret.expiration.time");')
-        timeout /= 1000.0
-
-        # Tap to make touch caret appear. Note: it's strange that when the caret
-        # is at the end, the rect of the caret in <textarea> cannot be obtained.
-        # A bug perhaps.
-        self.move_caret_to_end(el)
-        self.move_caret_by_offset(el, 1, backward=True)
-        el.tap(*self.caret_location(el))
-
-        # Wait until touch caret disappears, then pretend to move it to the
-        # top-left corner of the input box.
-        src_x, src_y = self.touch_caret_location(el)
-        dest_x, dest_y = 0, 0
-        self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform()
-
-        el.send_keys(content_to_add)
-        assertFunc(non_target_content, self.get_content(el))
-
-    def _test_scroll_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc):
-        content_to_add = '!'
-        target_content = string.ascii_letters + content_to_add
-
-        # Insert a long string to test horizontal scrolling.
-        el.clear()
-        el.send_keys(string.ascii_letters)
-
-        # Tap to make touch caret appear.
-        el.tap()
-
-        # Move touch caret to 100px right to the bottom-right corner of the input
-        # box so that it could scroll faster.
-        src_x, src_y = self.touch_caret_location(el)
-        dest_x, dest_y = el.size['width'] + 100, el.size['height']
-        self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
-
-        el.send_keys(content_to_add)
-        assertFunc(target_content, self.get_content(el))
-
-    ########################################################################
-    # <input> test cases with touch caret enabled
-    ########################################################################
-    def test_input_move_caret_to_the_right_by_one_character(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_the_right_by_one_character(self._input, self.assertEqual)
-
-    def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual)
-
-    def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._input, self.assertEqual)
-
-    def test_input_touch_caret_timeout(self):
-        self.openTestHtml(enabled=True)
-        self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._input, self.assertNotEqual)
-
-    def test_input_scroll_by_dragging_touch_caret_to_bottom_right_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_scroll_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual)
-
-    ########################################################################
-    # <input> test cases with touch caret disabled
-    ########################################################################
-    def test_input_move_caret_to_the_right_by_one_character_disabled(self):
-        self.openTestHtml(enabled=False)
-        self._test_move_caret_to_the_right_by_one_character(self._input, self.assertNotEqual)
-
-    def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self):
-        self.openTestHtml(enabled=False)
-        self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._input, self.assertNotEqual)
-
-    ########################################################################
-    # <textarea> test cases with touch caret enabled
-    ########################################################################
-    def test_textarea_move_caret_to_the_right_by_one_character(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertEqual)
-
-    def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual)
-
-    def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._textarea, self.assertEqual)
-
-    def test_textarea_touch_caret_timeout(self):
-        self.openTestHtml(enabled=True)
-        self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._textarea, self.assertNotEqual)
-
-    def test_textarea_scroll_by_dragging_touch_caret_to_bottom_right_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_scroll_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual)
-
-    ########################################################################
-    # <textarea> test cases with touch caret disabled
-    ########################################################################
-    def test_textarea_move_caret_to_the_right_by_one_character_disabled(self):
-        self.openTestHtml(enabled=False)
-        self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertNotEqual)
-
-    def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self):
-        self.openTestHtml(enabled=False)
-        self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._textarea, self.assertNotEqual)
-
-    ########################################################################
-    # <div> contenteditable test cases with touch caret enabled
-    ########################################################################
-    def test_contenteditable_move_caret_to_the_right_by_one_character(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertEqual)
-
-    def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._contenteditable, self.assertEqual)
-
-    def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._contenteditable, self.assertEqual)
-
-    def test_contenteditable_touch_caret_timeout(self):
-        self.openTestHtml(enabled=True)
-        self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._contenteditable, self.assertNotEqual)
-
-    def test_contenteditable_scroll_by_dragging_touch_caret_to_bottom_right_corner(self):
-        self.openTestHtml(enabled=True)
-        self._test_scroll_by_dragging_touch_caret_to_bottom_right_corner(self._contenteditable, self.assertEqual)
-
-    ########################################################################
-    # <div> contenteditable test cases with touch caret disabled
-    ########################################################################
-    def test_contenteditable_move_caret_to_the_right_by_one_character_disabled(self):
-        self.openTestHtml(enabled=False)
-        self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertNotEqual)
-
-    def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self):
-        self.openTestHtml(enabled=False)
-        self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._contenteditable, self.assertNotEqual)
--- a/mobile/android/base/health/BrowserHealthRecorder.java
+++ b/mobile/android/base/health/BrowserHealthRecorder.java
@@ -520,26 +520,27 @@ public class BrowserHealthRecorder imple
         // Otherwise, let's initialize it from scratch.
         this.profileCache.beginInitialization();
         this.profileCache.setProfileCreationTime(getAndPersistProfileInitTime(context, profilePath));
         this.profileCache.setOSLocale(osLocale);
         this.profileCache.setAppLocale(appLocale);
 
         // Because the distribution lookup can take some time, do it at the end of
         // our background startup work, along with the Gecko snapshot fetch.
-        final GeckoEventListener self = this;
-        ThreadUtils.postToBackgroundThread(new Runnable() {
+        final Distribution distribution = Distribution.getInstance(context);
+        distribution.addOnDistributionReadyCallback(new Runnable() {
             @Override
             public void run() {
-                final DistributionDescriptor desc = new Distribution(context).getDescriptor();
+                Log.d(LOG_TAG, "Running post-distribution task: health recorder.");
+                final DistributionDescriptor desc = distribution.getDescriptor();
                 if (desc != null && desc.valid) {
                     profileCache.setDistributionString(desc.id, desc.version);
                 }
                 Log.d(LOG_TAG, "Requesting all add-ons and FHR prefs from Gecko.");
-                dispatcher.registerGeckoThreadListener(self, EVENT_SNAPSHOT);
+                dispatcher.registerGeckoThreadListener(BrowserHealthRecorder.this, EVENT_SNAPSHOT);
                 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HealthReport:RequestSnapshot", null));
             }
         });
     }
 
     /**
      * Invoked in the background whenever the environment transitions between
      * two valid values.
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -88,20 +88,25 @@
   <color name="url_bar_domaintext_private">#FFF</color>
   <color name="url_bar_blockedtext">#b14646</color>
   <color name="url_bar_shadow">#12000000</color>
 
   <color name="home_button_bar_bg">#FFF5F7F9</color>
 
   <color name="panel_image_item_background">#D1D9E1</color>
 
-  <!-- Swipe to refresh colors -->
+  <!-- Swipe to refresh colors for dynamic panel -->
   <color name="swipe_refresh_orange">#FFFFC26C</color>
   <color name="swipe_refresh_white">#FFFFFFFF</color>
-  <color name="swipe_refresh_orange_dark">#FF9500</color>
+
+  <!-- Swipe to refresh colors for remote tabs -->
+  <color name="swipe_refresh_orange1">#EE6700</color>
+  <color name="swipe_refresh_orange2">#FF9400</color>
+  <color name="swipe_refresh_orange3">#F57900</color>
+  <color name="swipe_refresh_orange4">#FFB44C</color>
 
   <!-- Remote tabs setup -->
   <color name="remote_tabs_setup_button_background">#E66000</color>
   <color name="remote_tabs_setup_button_background_hit">#D95300</color>
 
   <!-- Button toast colors. -->
   <color name="toast_button_background">#FF2A2A2A</color>
   <color name="toast_button_pressed">#FF3E6784</color>
--- a/mobile/android/base/tabspanel/RemoteTabsContainerPanel.java
+++ b/mobile/android/base/tabspanel/RemoteTabsContainerPanel.java
@@ -55,18 +55,18 @@ public class RemoteTabsContainerPanel ex
 
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
         super.addView(child, index, params);
 
         list = (RemoteTabsList) child;
 
         // Must be called after the child view has been added.
-        setColorScheme(R.color.swipe_refresh_orange_dark, R.color.background_tabs,
-                       R.color.swipe_refresh_orange_dark, R.color.background_tabs);
+        setColorScheme(R.color.swipe_refresh_orange1, R.color.swipe_refresh_orange2,
+                       R.color.swipe_refresh_orange3, R.color.swipe_refresh_orange4);
     }
 
 
     @Override
     public boolean canChildScrollUp() {
         // We are not supporting swipe-to-refresh for old sync. This disables
         // the swipe gesture if no FxA are detected.
         if (FirefoxAccounts.firefoxAccountsExist(getContext())) {
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -986,20 +986,25 @@ var BrowserApp = {
     aTab.destroy();
     this._tabs.splice(tabIndex, 1);
 
     if (aShowUndoToast) {
       // Get a title for the undo close toast. Fall back to the URL if there is no title.
       let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
       let closedTabData = ss.getClosedTabs(window)[0];
 
-      let historyEntry = closedTabData.entries[closedTabData.index - 1];
-      let title = historyEntry.title || historyEntry.url;
-
-      let message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
+      let message;
+      let title = closedTabData.entries[closedTabData.index - 1].title;
+
+      if (title) {
+        message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
+      } else {
+        message = Strings.browser.GetStringFromName("undoCloseToast.messageDefault");
+      }
+
       NativeWindow.toast.show(message, "short", {
         button: {
           icon: "drawable://undo_button_icon",
           label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
           callback: function() {
             UITelemetry.addEvent("undo.1", "toast", null, "closetab");
             ss.undoCloseTab(window, 0);
           }
--- a/mobile/android/locales/en-US/chrome/browser.properties
+++ b/mobile/android/locales/en-US/chrome/browser.properties
@@ -127,16 +127,20 @@ newprivatetabpopup.opened=New private ta
 # button label) and upper-case, to match Google and Android's convention.
 newtabpopup.switch=SWITCH
 
 # Undo close tab toast
 # LOCALIZATION NOTE (undoCloseToast.message): This message appears in a toast
 # when the user closes a tab. %S is the title of the tab that was closed.
 undoCloseToast.message=Closed %S
 
+# LOCALIZATION NOTE (undoCloseToast.messageDefault): This message appears in a
+# toast when the user closes a tab if there is no title to display.
+undoCloseToast.messageDefault=Closed tab
+
 # LOCALIZATION NOTE (undoCloseToast.action2): Ideally, this string is short (it's a
 # button label) and upper-case, to match Google and Android's convention.
 undoCloseToast.action2=UNDO
 
 # Offline web applications
 offlineApps.ask=Allow %S to store data on your device for offline use?
 offlineApps.dontAskAgain=Don't ask again for this site
 offlineApps.allow=Allow
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3944,18 +3944,22 @@ pref("layout.css.touch_action.enabled", 
 // enable JS dump() function.
 pref("browser.dom.window.dump.enabled", false);
 
 // SPS Profiler
 pref("profiler.enabled", false);
 pref("profiler.interval", 10);
 pref("profiler.entries", 100000);
 
+#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
 // Network Information API
 pref("dom.netinfo.enabled", true);
+#else
+pref("dom.netinfo.enabled", false);
+#endif
 
 #ifdef XP_WIN
 // On 32-bit Windows, fire a low-memory notification if we have less than this
 // many mb of virtual address space available.
 pref("memory.low_virtual_memory_threshold_mb", 128);
 
 // On Windows 32- or 64-bit, fire a low-memory notification if we have less
 // than this many mb of commit space (physical memory plus page file) left.
--- a/testing/marionette/client/marionette/tests/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit-tests.ini
@@ -22,11 +22,8 @@ skip = false
 [include:../../../../../dom/mobilemessage/tests/marionette/manifest.ini]
 [include:../../../../../dom/mobileconnection/tests/marionette/manifest.ini]
 [include:../../../../../dom/system/gonk/tests/marionette/manifest.ini]
 [include:../../../../../dom/icc/tests/marionette/manifest.ini]
 [include:../../../../../dom/system/tests/marionette/manifest.ini]
 [include:../../../../../dom/nfc/tests/marionette/manifest.ini]
 [include:../../../../../dom/events/test/marionette/manifest.ini]
 [include:../../../../../dom/wifi/test/marionette/manifest.ini]
-
-; layout tests
-[include:../../../../../layout/base/tests/marionette/manifest.ini]
deleted file mode 100644
--- a/testing/marionette/client/marionette/www/test_touchcaret.html
+++ /dev/null
@@ -1,17 +0,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/. -->
-
-<!DOCTYPE html>
-<html id="html">
-  <head>
-    <title>Bug 960897: Marionette tests for touch caret</title>
-  </head>
-  <body>
-    <div><input id="input" value="ABCDEFGHI"></input></div>
-    <br />
-    <div><textarea name="textarea" id="textarea" rows="4" cols="6">ABCDEFGHI</textarea></div>
-    <br />
-    <div style="width: 10em; height: 2em; word-wrap: break-word; overflow: auto;" contenteditable="true" id="contenteditable">ABCDEFGHI</div>
-  </body>
-</html>
--- a/toolkit/devtools/styleinspector/css-logic.js
+++ b/toolkit/devtools/styleinspector/css-logic.js
@@ -912,20 +912,23 @@ CssLogic.findCssSelector = function CssL
       selector = selector + ':nth-child(' + index + ')';
       matches = document.querySelectorAll(selector);
       if (matches.length === 1) {
         return selector;
       }
     }
   }
 
-  // So we can be unique w.r.t. our parent, and use recursion
-  index = positionInNodeList(ele, ele.parentNode.children) + 1;
-  selector = CssLogic_findCssSelector(ele.parentNode) + ' > ' +
-          tagName + ':nth-child(' + index + ')';
+  // Not unique enough yet.  As long as it's not a child of the document,
+  // continue recursing up until it is unique enough.
+  if (ele.parentNode !== document) {
+    index = positionInNodeList(ele, ele.parentNode.children) + 1;
+    selector = CssLogic_findCssSelector(ele.parentNode) + ' > ' +
+            tagName + ':nth-child(' + index + ')';
+  }
 
   return selector;
 };
 
 /**
  * A safe way to access cached bits of information about a stylesheet.
  *
  * @constructor
--- a/toolkit/devtools/transport/packets.js
+++ b/toolkit/devtools/transport/packets.js
@@ -20,17 +20,16 @@
  *     Called when the output stream is ready to write
  *   * get done()
  *     Returns true once the packet is done being read / written
  *   * destroy()
  *     Called to clean up at the end of use
  */
 
 const { Cc, Ci, Cu } = require("chrome");
-const Heritage = require("sdk/core/heritage");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const { dumpn, dumpv } = DevToolsUtils;
 const StreamUtils = require("devtools/toolkit/transport/stream-utils");
 
 DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
   const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                            .createInstance(Ci.nsIScriptableUnicodeConverter);
   unicodeConverter.charset = "UTF-8";
@@ -123,93 +122,95 @@ JSONPacket.fromHeader = function(header,
   dumpv("Header matches JSON packet");
   let packet = new JSONPacket(transport);
   packet.length = +match[1];
   return packet;
 };
 
 JSONPacket.HEADER_PATTERN = /^(\d+):$/;
 
-JSONPacket.prototype = Heritage.extend(Packet.prototype, {
+JSONPacket.prototype = Object.create(Packet.prototype);
 
+Object.defineProperty(JSONPacket.prototype, "object", {
   /**
    * Gets the object (not the serialized string) being read or written.
    */
-  get object() { return this._object; },
+  get: function() { return this._object; },
 
   /**
    * Sets the object to be sent when write() is called.
    */
-  set object(object) {
+  set: function(object) {
     this._object = object;
     let data = JSON.stringify(object);
     this._data = unicodeConverter.ConvertFromUnicode(data);
     this.length = this._data.length;
-  },
-
-  read: function(stream, scriptableStream) {
-    dumpv("Reading JSON packet");
-
-    // Read in more packet data.
-    this._readData(stream, scriptableStream);
-
-    if (!this.done) {
-      // Don't have a complete packet yet.
-      return;
-    }
+  }
+});
 
-    let json = this._data;
-    try {
-      json = unicodeConverter.ConvertToUnicode(json);
-      this._object = JSON.parse(json);
-    } catch(e) {
-      let msg = "Error parsing incoming packet: " + json + " (" + e +
-                " - " + e.stack + ")";
-      if (Cu.reportError) {
-        Cu.reportError(msg);
-      }
-      dumpn(msg);
-      return;
-    }
+JSONPacket.prototype.read = function(stream, scriptableStream) {
+  dumpv("Reading JSON packet");
+
+  // Read in more packet data.
+  this._readData(stream, scriptableStream);
 
-    this._transport._onJSONObjectReady(this._object);
-  },
-
-  _readData: function(stream, scriptableStream) {
-    if (dumpv.wantVerbose) {
-      dumpv("Reading JSON data: _l: " + this.length + " dL: " +
-            this._data.length + " sA: " + stream.available());
-    }
-    let bytesToRead = Math.min(this.length - this._data.length,
-                               stream.available());
-    this._data += scriptableStream.readBytes(bytesToRead);
-    this._done = this._data.length === this.length;
-  },
+  if (!this.done) {
+    // Don't have a complete packet yet.
+    return;
+  }
 
-  write: function(stream) {
-    dumpv("Writing JSON packet");
-
-    if (this._outgoing === undefined) {
-      // Format the serialized packet to a buffer
-      this._outgoing = this.length + ":" + this._data;
+  let json = this._data;
+  try {
+    json = unicodeConverter.ConvertToUnicode(json);
+    this._object = JSON.parse(json);
+  } catch(e) {
+    let msg = "Error parsing incoming packet: " + json + " (" + e +
+              " - " + e.stack + ")";
+    if (Cu.reportError) {
+      Cu.reportError(msg);
     }
-
-    let written = stream.write(this._outgoing, this._outgoing.length);
-    this._outgoing = this._outgoing.slice(written);
-    this._done = !this._outgoing.length;
-  },
-
-  get done() { return this._done; },
-
-  toString: function() {
-    return JSON.stringify(this._object, null, 2);
+    dumpn(msg);
+    return;
   }
 
+  this._transport._onJSONObjectReady(this._object);
+}
+
+JSONPacket.prototype._readData = function(stream, scriptableStream) {
+  if (dumpv.wantVerbose) {
+    dumpv("Reading JSON data: _l: " + this.length + " dL: " +
+          this._data.length + " sA: " + stream.available());
+  }
+  let bytesToRead = Math.min(this.length - this._data.length,
+                             stream.available());
+  this._data += scriptableStream.readBytes(bytesToRead);
+  this._done = this._data.length === this.length;
+}
+
+JSONPacket.prototype.write = function(stream) {
+  dumpv("Writing JSON packet");
+
+  if (this._outgoing === undefined) {
+    // Format the serialized packet to a buffer
+    this._outgoing = this.length + ":" + this._data;
+  }
+
+  let written = stream.write(this._outgoing, this._outgoing.length);
+  this._outgoing = this._outgoing.slice(written);
+  this._done = !this._outgoing.length;
+}
+
+Object.defineProperty(JSONPacket.prototype, "done", {
+  get: function() { return this._done; }
 });
 
+JSONPacket.prototype.toString = function() {
+  return JSON.stringify(this._object, null, 2);
+}
+
 exports.JSONPacket = JSONPacket;
 
 /**
  * With a bulk packet, data is transferred by temporarily handing over the
  * transport's input or output stream to the application layer for writing data
  * directly.  This can be much faster for large data sets, and avoids various
  * stages of copies and data duplication inherent in the JSON packet type.  The
  * bulk packet looks like:
@@ -252,126 +253,131 @@ BulkPacket.fromHeader = function(header,
     type: match[2],
     length: +match[3]
   };
   return packet;
 };
 
 BulkPacket.HEADER_PATTERN = /^bulk ([^: ]+) ([^: ]+) (\d+):$/;
 
-BulkPacket.prototype = Heritage.extend(Packet.prototype, {
+BulkPacket.prototype = Object.create(Packet.prototype);
+
+BulkPacket.prototype.read = function(stream) {
+  dumpv("Reading bulk packet, handing off input stream");
 
-  read: function(stream) {
-    dumpv("Reading bulk packet, handing off input stream");
+  // Temporarily pause monitoring of the input stream
+  this._transport.pauseIncoming();
+
+  let deferred = promise.defer();
 
-    // Temporarily pause monitoring of the input stream
-    this._transport.pauseIncoming();
-
-    let deferred = promise.defer();
+  this._transport._onBulkReadReady({
+    actor: this.actor,
+    type: this.type,
+    length: this.length,
+    copyTo: (output) => {
+      dumpv("CT length: " + this.length);
+      deferred.resolve(StreamUtils.copyStream(stream, output, this.length));
+      return deferred.promise;
+    },
+    stream: stream,
+    done: deferred
+  });
 
-    this._transport._onBulkReadReady({
-      actor: this.actor,
-      type: this.type,
-      length: this.length,
-      copyTo: (output) => {
-        dumpv("CT length: " + this.length);
-        deferred.resolve(StreamUtils.copyStream(stream, output, this.length));
-        return deferred.promise;
-      },
-      stream: stream,
-      done: deferred
-    });
+  // Await the result of reading from the stream
+  deferred.promise.then(() => {
+    dumpv("onReadDone called, ending bulk mode");
+    this._done = true;
+    this._transport.resumeIncoming();
+  }, this._transport.close);
 
-    // Await the result of reading from the stream
-    deferred.promise.then(() => {
-      dumpv("onReadDone called, ending bulk mode");
-      this._done = true;
-      this._transport.resumeIncoming();
-    }, this._transport.close);
+  // Ensure this is only done once
+  this.read = () => {
+    throw new Error("Tried to read() a BulkPacket's stream multiple times.");
+  };
+}
 
-    // Ensure this is only done once
-    this.read = () => {
-      throw new Error("Tried to read() a BulkPacket's stream multiple times.");
-    };
-  },
+BulkPacket.prototype.write = function(stream) {
+  dumpv("Writing bulk packet");
 
-  write: function(stream) {
-    dumpv("Writing bulk packet");
+  if (this._outgoingHeader === undefined) {
+    dumpv("Serializing bulk packet header");
+    // Format the serialized packet header to a buffer
+    this._outgoingHeader = "bulk " + this.actor + " " + this.type + " " +
+                           this.length + ":";
+  }
 
-    if (this._outgoingHeader === undefined) {
-      dumpv("Serializing bulk packet header");
-      // Format the serialized packet header to a buffer
-      this._outgoingHeader = "bulk " + this.actor + " " + this.type + " " +
-                             this.length + ":";
-    }
+  // Write the header, or whatever's left of it to write.
+  if (this._outgoingHeader.length) {
+    dumpv("Writing bulk packet header");
+    let written = stream.write(this._outgoingHeader,
+                               this._outgoingHeader.length);
+    this._outgoingHeader = this._outgoingHeader.slice(written);
+    return;
+  }
 
-    // Write the header, or whatever's left of it to write.
-    if (this._outgoingHeader.length) {
-      dumpv("Writing bulk packet header");
-      let written = stream.write(this._outgoingHeader,
-                                 this._outgoingHeader.length);
-      this._outgoingHeader = this._outgoingHeader.slice(written);
-      return;
-    }
+  dumpv("Handing off output stream");
 
-    dumpv("Handing off output stream");
+  // Temporarily pause the monitoring of the output stream
+  this._transport.pauseOutgoing();
 
-    // Temporarily pause the monitoring of the output stream
-    this._transport.pauseOutgoing();
-
-    let deferred = promise.defer();
+  let deferred = promise.defer();
 
-    this._readyForWriting.resolve({
-      copyFrom: (input) => {
-        dumpv("CF length: " + this.length);
-        deferred.resolve(StreamUtils.copyStream(input, stream, this.length));
-        return deferred.promise;
-      },
-      stream: stream,
-      done: deferred
-    });
+  this._readyForWriting.resolve({
+    copyFrom: (input) => {
+      dumpv("CF length: " + this.length);
+      deferred.resolve(StreamUtils.copyStream(input, stream, this.length));
+      return deferred.promise;
+    },
+    stream: stream,
+    done: deferred
+  });
 
-    // Await the result of writing to the stream
-    deferred.promise.then(() => {
-      dumpv("onWriteDone called, ending bulk mode");
-      this._done = true;
-      this._transport.resumeOutgoing();
-    }, this._transport.close);
+  // Await the result of writing to the stream
+  deferred.promise.then(() => {
+    dumpv("onWriteDone called, ending bulk mode");
+    this._done = true;
+    this._transport.resumeOutgoing();
+  }, this._transport.close);
 
-    // Ensure this is only done once
-    this.write = () => {
-      throw new Error("Tried to write() a BulkPacket's stream multiple times.");
-    };
-  },
+  // Ensure this is only done once
+  this.write = () => {
+    throw new Error("Tried to write() a BulkPacket's stream multiple times.");
+  };
+}
 
-  get streamReadyForWriting() {
+Object.defineProperty(BulkPacket.prototype, "streamReadyForWriting", {
+  get: function() {
     return this._readyForWriting.promise;
-  },
+  }
+});
 
-  get header() {
+Object.defineProperty(BulkPacket.prototype, "header", {
+  get: function() {
     return {
       actor: this.actor,
       type: this.type,
       length: this.length
     };
   },
 
-  set header(header) {
+  set: function(header) {
     this.actor = header.actor;
     this.type = header.type;
     this.length = header.length;
   },
-
-  get done() { return this._done; },
+});
 
-  toString: function() {
-    return "Bulk: " + JSON.stringify(this.header, null, 2);
-  }
+Object.defineProperty(BulkPacket.prototype, "done", {
+  get: function() { return this._done; },
+});
 
-});
+
+BulkPacket.prototype.toString = function() {
+  return "Bulk: " + JSON.stringify(this.header, null, 2);
+}
 
 exports.BulkPacket = BulkPacket;
 
 /**
  * RawPacket is used to test the transport's error handling of malformed
  * packets, by writing data directly onto the stream.
  * @param transport DebuggerTransport
  *        The transport instance that will own the packet.
@@ -380,26 +386,26 @@ exports.BulkPacket = BulkPacket;
  */
 function RawPacket(transport, data) {
   Packet.call(this, transport);
   this._data = data;
   this.length = data.length;
   this._done = false;
 }
 
-RawPacket.prototype = Heritage.extend(Packet.prototype, {
+RawPacket.prototype = Object.create(Packet.prototype);
 
-  read: function(stream) {
-    // This hasn't yet been needed for testing.
-    throw Error("Not implmented.");
-  },
+RawPacket.prototype.read = function(stream) {
+  // This hasn't yet been needed for testing.
+  throw Error("Not implmented.");
+}
 
-  write: function(stream) {
-    let written = stream.write(this._data, this._data.length);
-    this._data = this._data.slice(written);
-    this._done = !this._data.length;
-  },
+RawPacket.prototype.write = function(stream) {
+  let written = stream.write(this._data, this._data.length);
+  this._data = this._data.slice(written);
+  this._done = !this._data.length;
+}
 
-  get done() { return this._done; }
-
+Object.defineProperty(RawPacket.prototype, "done", {
+  get: function() { return this._done; }
 });
 
 exports.RawPacket = RawPacket;
--- a/toolkit/modules/tests/xpcshell/test_DirectoryLinksProvider.js
+++ b/toolkit/modules/tests/xpcshell/test_DirectoryLinksProvider.js
@@ -200,17 +200,17 @@ add_task(function test_fetchAndCacheLink
   // File should be empty.
   let data = yield readJsonFile();
   isIdentical(data, "");
 });
 
 add_task(function test_fetchAndCacheLinks_unknownHost() {
   yield DirectoryLinksProvider.init();
   yield cleanJsonFile();
-  let nonExistentServer = "http://nosuchhost";
+  let nonExistentServer = "http://nosuchhost.localhost";
   try {
     yield DirectoryLinksProvider._fetchAndCacheLinks(nonExistentServer);
     do_throw("BAD URIs should fail");
   } catch (e) {
     do_check_true(e.startsWith("Fetching " + nonExistentServer + " results in error code: "))
   }
 
   // File should be empty.
@@ -281,17 +281,17 @@ add_task(function test_DirectoryLinksPro
   let links = yield fetchData();
   do_check_eq(links.length, 1);
   let expectedData =  [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 0}];
   isIdentical(links, expectedData);
 
   // tests these 2 things:
   // 1. _linksURL is properly set after the pref change
   // 2. invalid source url is correctly handled
-  let exampleUrl = 'http://nosuchhost/bad';
+  let exampleUrl = 'http://nosuchhost.localhost/bad';
   yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, exampleUrl);
   do_check_eq(DirectoryLinksProvider._linksURL, exampleUrl);
 
   // since the download fail, the directory file must remain the same
   let newLinks = yield fetchData();
   isIdentical(newLinks, expectedData);
 
   // now remove the file, and re-download