Merge backout.
authorMs2ger <ms2ger@gmail.com>
Mon, 09 Sep 2013 16:56:18 +0200
changeset 146197 3931b9652326cf3ac5ad520d9503444c69643c3a
parent 146196 a5e8a4e466bc970d850f978bc1cdb47087e9fe9a (current diff)
parent 146195 07ff0d3d9c403a3e804c14da528af113b5416f5b (diff)
child 146198 7cadc7e21f5628c31722107a11958d641a578d24
push id25244
push userryanvm@gmail.com
push dateMon, 09 Sep 2013 20:03:14 +0000
treeherdermozilla-central@f320b8c034bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.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 backout.
b2g/chrome/content/shell.xul
browser/base/content/test/social/browser_social_markButton.js
browser/base/content/test/social/social_mark_image.png
browser/devtools/shared/LayoutHelpers.jsm
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLMemoryMultiReporterWrapper.h
content/html/content/src/HTMLCanvasElement.cpp
content/media/omx/MP3FrameParser.cpp
content/media/omx/MP3FrameParser.h
dom/base/nsDOMException.cpp
dom/base/nsDOMException.h
dom/mobilemessage/interfaces/nsIRilSmsService.idl
dom/mobilemessage/src/gonk/SmsService.js
dom/mobilemessage/src/gonk/SmsService.manifest
dom/system/gonk/RilMessageManager.jsm
dom/workers/Exceptions.cpp
dom/workers/Exceptions.h
gfx/angle/angle-cross-compilation.patch
gfx/angle/angle-line-loop-overflow.patch
gfx/angle/angle-long-ident-spooky-hash.patch
gfx/angle/codereview.settings
gfx/angle/src/ANGLE.sln
gfx/angle/src/compiler/DetectRecursion.cpp
gfx/angle/src/compiler/DetectRecursion.h
gfx/angle/src/compiler/spooky.cpp
gfx/angle/src/compiler/spooky.h
gfx/angle/src/compiler/translator_common.vcxproj
gfx/angle/src/compiler/translator_common.vcxproj.filters
gfx/angle/src/compiler/translator_hlsl.vcxproj
gfx/angle/src/compiler/translator_hlsl.vcxproj.filters
gfx/angle/src/libEGL/ShaderCache.h
gfx/angle/src/libEGL/libEGL.vcxproj
gfx/angle/src/libEGL/libEGL.vcxproj.filters
gfx/angle/src/libGLESv2/Blit.cpp
gfx/angle/src/libGLESv2/Blit.h
gfx/angle/src/libGLESv2/D3DConstantTable.cpp
gfx/angle/src/libGLESv2/D3DConstantTable.h
gfx/angle/src/libGLESv2/IndexDataManager.cpp
gfx/angle/src/libGLESv2/IndexDataManager.h
gfx/angle/src/libGLESv2/TextureSSE2.cpp
gfx/angle/src/libGLESv2/VertexDataManager.cpp
gfx/angle/src/libGLESv2/VertexDataManager.h
gfx/angle/src/libGLESv2/libGLESv2.vcxproj
gfx/angle/src/libGLESv2/libGLESv2.vcxproj.filters
gfx/angle/src/libGLESv2/shaders/Blit.ps
gfx/angle/src/libGLESv2/shaders/Blit.vs
gfx/angle/src/libGLESv2/shaders/componentmaskps.h
gfx/angle/src/libGLESv2/shaders/flipyvs.h
gfx/angle/src/libGLESv2/shaders/generate_shaders.bat
gfx/angle/src/libGLESv2/shaders/luminanceps.h
gfx/angle/src/libGLESv2/shaders/passthroughps.h
gfx/angle/src/libGLESv2/shaders/standardvs.h
gfx/angle/src/libGLESv2/vertexconversion.h
js/src/builtin/BinaryData.cpp
js/src/builtin/BinaryData.h
js/src/jit-test/tests/binary-data/fuzz1.js
js/src/jit-test/tests/binary-data/fuzz10.js
js/src/jit-test/tests/binary-data/fuzz11.js
js/src/jit-test/tests/binary-data/fuzz2.js
js/src/jit-test/tests/binary-data/fuzz3.js
js/src/jit-test/tests/binary-data/fuzz4.js
js/src/jit-test/tests/binary-data/fuzz5.js
js/src/jit-test/tests/binary-data/fuzz6.js
js/src/jit-test/tests/binary-data/fuzz7.js
js/src/jit-test/tests/binary-data/fuzz8.js
js/src/jit-test/tests/binary-data/fuzz9.js
js/src/jit-test/tests/binary-data/jit-prefix.js
js/src/jit-test/tests/binary-data/jit-read-float64.js
js/src/jit-test/tests/binary-data/jit-read-int.js
js/src/jsapi-tests/testGetPropertyDefault.cpp
js/src/tests/ecma_6/BinaryData/architecture.js
js/src/tests/ecma_6/BinaryData/arrayofstructs.js
js/src/tests/ecma_6/BinaryData/arraytype.js
js/src/tests/ecma_6/BinaryData/memory.js
js/src/tests/ecma_6/BinaryData/numerictypes.js
js/src/tests/ecma_6/BinaryData/shell.js
js/src/tests/ecma_6/BinaryData/size_and_alignment.js
js/src/tests/ecma_6/BinaryData/structequiv.js
js/src/tests/ecma_6/BinaryData/structtypeenumerate.js
js/src/tests/ecma_6/BinaryData/structtypeindexedfields.js
js/src/tests/ecma_6/BinaryData/structtypeprototype.js
js/src/tests/ecma_6/BinaryData/structtypereflection.js
js/src/tests/ecma_6/BinaryData/structtypestructuralassign.js
js/src/vm/GlobalObject-inl.h
js/xpconnect/src/XPCStack.cpp
widget/gonk/libui/BitSet.h
widget/gonk/libui/DisplayInfo.h
widget/gonk/libui/PixelFormat.cpp
widget/gonk/libui/PixelFormat.h
widget/gonk/libui/PropertyMap.cpp
widget/gonk/libui/PropertyMap.h
widget/gonk/libui/Static.cpp
widget/gonk/libui/Static.h
widget/gonk/libui/String16.cpp
widget/gonk/libui/String16.h
widget/gonk/libui/String8.cpp
widget/gonk/libui/String8.h
widget/gonk/libui/Timers.cpp
widget/gonk/libui/Timers.h
widget/gonk/libui/Unicode.cpp
widget/gonk/libui/Unicode.h
widget/gonk/libui/utils_Log.h
widget/windows/winrt/FrameworkViewGfx.cpp
xpcom/base/nsExceptionService.cpp
xpcom/base/nsExceptionService.h
xpcom/base/nsIExceptionService.idl
--- a/accessible/src/atk/nsMaiInterfaceImage.cpp
+++ b/accessible/src/atk/nsMaiInterfaceImage.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InterfaceInitFuncs.h"
 
 #include "AccessibleWrap.h"
 #include "ImageAccessible.h"
 #include "mozilla/Likely.h"
 #include "nsMai.h"
+#include "nsIURI.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 extern "C" {
 const gchar* getDescriptionCB(AtkObject* aAtkObj);
 
 static void
--- a/accessible/src/base/Logging.cpp
+++ b/accessible/src/base/Logging.cpp
@@ -16,16 +16,17 @@
 #include "nsDocShellLoadTypes.h"
 #include "nsIChannel.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsISelectionPrivate.h"
 #include "nsTraceRefcntImpl.h"
 #include "nsIWebProgress.h"
 #include "prenv.h"
 #include "nsIDocShellTreeItem.h"
+#include "nsIURI.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Logging helpers
 
 static uint32_t sModules = 0;
--- a/accessible/src/base/RoleMap.h
+++ b/accessible/src/base/RoleMap.h
@@ -614,17 +614,17 @@ ROLE(FONT_CHOOSER,
      NSAccessibilityUnknownRole,
      USE_ROLE_STRING,
      IA2_ROLE_FONT_CHOOSER,
      eNoNameRule)
 
 ROLE(CHROME_WINDOW,
      "chrome window",
      ATK_ROLE_FRAME,
-     NSAccessibilityUnknownRole,  //Unused on OS X
+     NSAccessibilityGroupRole,  //Contains the main Firefox UI
      ROLE_SYSTEM_APPLICATION,
      IA2_ROLE_FRAME,
      eNoNameRule)
 
 ROLE(GLASS_PANE,
      "glass pane",
      ATK_ROLE_GLASS_PANE,
      NSAccessibilityGroupRole,
--- a/accessible/src/generic/ImageAccessible.cpp
+++ b/accessible/src/generic/ImageAccessible.cpp
@@ -14,16 +14,17 @@
 #include "imgIRequest.h"
 #include "nsGenericHTMLElement.h"
 #include "nsIDocument.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIPresShell.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsPIDOMWindow.h"
+#include "nsIURI.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // ImageAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 ImageAccessible::
--- a/accessible/src/html/HTMLImageMapAccessible.cpp
+++ b/accessible/src/html/HTMLImageMapAccessible.cpp
@@ -12,16 +12,17 @@
 
 #include "nsIDOMHTMLCollection.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLAreaElement.h"
 #include "nsIFrame.h"
 #include "nsImageFrame.h"
 #include "nsImageMap.h"
+#include "nsIURI.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLImageMapAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLImageMapAccessible::
--- a/accessible/src/mac/AccessibleWrap.h
+++ b/accessible/src/mac/AccessibleWrap.h
@@ -11,17 +11,16 @@
 #define _AccessibleWrap_H_
 
 #include <objc/objc.h>
 
 #include "Accessible.h"
 #include "States.h"
 
 #include "nsCOMPtr.h"
-#include "nsRect.h"
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 #if defined(__OBJC__)
 @class mozAccessible;
 #endif
 
--- a/accessible/src/windows/ia2/ia2AccessibleImage.cpp
+++ b/accessible/src/windows/ia2/ia2AccessibleImage.cpp
@@ -9,16 +9,17 @@
 
 #include "AccessibleImage_i.c"
 
 #include "ImageAccessibleWrap.h"
 #include "IUnknownImpl.h"
 #include "nsIAccessibleTypes.h"
 
 #include "nsString.h"
+#include "nsIURI.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 // IUnknown
 
 STDMETHODIMP
 ia2AccessibleImage::QueryInterface(REFIID iid, void** ppv)
--- a/accessible/src/windows/msaa/ImageAccessibleWrap.cpp
+++ b/accessible/src/windows/msaa/ImageAccessibleWrap.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:expandtab:shiftwidth=2:tabstop=2:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageAccessibleWrap.h"
+#include "nsIURI.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 NS_IMPL_ISUPPORTS_INHERITED0(ImageAccessibleWrap,
                              ImageAccessible)
 
 IMPL_IUNKNOWN_INHERITED1(ImageAccessibleWrap,
--- a/addon-sdk/source/lib/sdk/addon/installer.js
+++ b/addon-sdk/source/lib/sdk/addon/installer.js
@@ -82,25 +82,29 @@ exports.uninstall = function uninstall(a
         return;
       AddonManager.removeAddonListener(listener);
       resolve();
     }
   };
   AddonManager.addAddonListener(listener);
 
   // Order Addonmanager to uninstall the addon
-  AddonManager.getAddonByID(addonId, function (addon) {
-    addon.uninstall();
-  });
+  getAddon(addonId).then(addon => addon.uninstall(), reject);
 
   return promise;
 };
 
 exports.disable = function disable(addonId) {
-  let { promise, resolve, reject } = defer();
-
-  AddonManager.getAddonByID(addonId, function (addon) {
+  return getAddon(addonId).then(addon => {
     addon.userDisabled = true;
-    resolve();
+    return addonId;
   });
+};
 
+exports.isActive = function isActive(addonId) {
+  return getAddon(addonId).then(addon => addon.isActive && !addon.appDisabled);
+};
+
+function getAddon (id) {
+  let { promise, resolve, reject } = defer();
+  AddonManager.getAddonByID(id, addon => addon ? resolve(addon) : reject());
   return promise;
-};
+}
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -125,36 +125,36 @@ const WorkerSandbox = EventEmitter.compo
     // 1/ It is useless to use multiple domains as the worker is only used
     // to communicate with the addon,
     // 2/ By using it it would prevent the document to have access to any JS
     // value of the worker. As JS values coming from multiple domain principals
     // can't be accessed by "mono-principals" (principal with only one domain).
     // Even if this principal is for a domain that is specified in the multiple
     // domain principal.
     let principals  = window;
-    let wantXHRConstructor = false;
+    let wantDOMConstructors = []
     if (EXPANDED_PRINCIPALS.length > 0 && !worker._injectInDocument) {
       principals = EXPANDED_PRINCIPALS.concat(window);
       // We have to replace XHR constructor of the content document
       // with a custom cross origin one, automagically added by platform code:
       delete proto.XMLHttpRequest;
-      wantXHRConstructor = true;
+      wantDOMConstructors.push("XMLHttpRequest");
     }
 
     // Instantiate trusted code in another Sandbox in order to prevent content
     // script from messing with standard classes used by proxy and API code.
     let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window });
     apiSandbox.console = console;
 
     // Create the sandbox and bind it to window in order for content scripts to
     // have access to all standard globals (window, document, ...)
     let content = this._sandbox = sandbox(principals, {
       sandboxPrototype: proto,
       wantXrays: true,
-      wantXHRConstructor: wantXHRConstructor,
+      wantDOMConstructors: wantDOMConstructors,
       sameZoneAs: window
     });
     // We have to ensure that window.top and window.parent are the exact same
     // object than window object, i.e. the sandbox global object. But not
     // always, in case of iframes, top and parent are another window object.
     let top = window.top === window ? content : content.top;
     let parent = window.parent === window ? content : content.parent;
     merge(content, {
--- a/addon-sdk/source/lib/sdk/hotkeys.js
+++ b/addon-sdk/source/lib/sdk/hotkeys.js
@@ -19,17 +19,17 @@ const Hotkey = exports.Hotkey = function
     return new Hotkey(options);
 
   // Parsing key combination string.
   let hotkey = jsonify(options.combo);
   if (!isFunctionKey(hotkey.key) && !hotkey.modifiers.length) {
     throw new TypeError(INVALID_HOTKEY);
   }
 
-  this.onPress = options.onPress;
+  this.onPress = options.onPress && options.onPress.bind(this);
   this.toString = stringify.bind(null, hotkey);
   // Registering listener on keyboard combination enclosed by this hotkey.
   // Please note that `this.toString()` is a normalized version of
   // `options.combination` where order of modifiers is sorted and `accel` is
   // replaced with platform specific key.
   register(this.toString(), this.onPress);
   // We freeze instance before returning it in order to make it's properties
   // read-only.
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -143,16 +143,19 @@ function dictDiff(last, curr) {
 }
 
 function reportMemoryUsage() {
   memory.gc();
 
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
 
+  // XXX: this code is *so* bogus -- nsIMemoryReporter changed its |memoryUsed|
+  // field to |amount| *years* ago, and even bigger changes have happened
+  // since -- that it must just never be run.
   var reporters = mgr.enumerateReporters();
   if (reporters.hasMoreElements())
     print("\n");
 
   while (reporters.hasMoreElements()) {
     var reporter = reporters.getNext();
     reporter.QueryInterface(Ci.nsIMemoryReporter);
     print(reporter.description + ": " + reporter.memoryUsed + "\n");
@@ -371,24 +374,17 @@ function getPotentialLeaks() {
     }
   }
 
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
   let enm = mgr.enumerateReporters();
   while (enm.hasMoreElements()) {
-    let reporter = enm.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    logReporter(reporter.process, reporter.path, reporter.kind, reporter.units,
-                reporter.amount, reporter.description);
-  }
-
-  let enm = mgr.enumerateMultiReporters();
-  while (enm.hasMoreElements()) {
-    let mr = enm.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+    let mr = enm.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mr.collectReports(logReporter, null);
   }
 
   return { compartments: compartments, windows: windows };
 }
 
 function nextIteration(tests) {
   if (tests) {
--- a/addon-sdk/source/lib/sdk/util/deprecate.js
+++ b/addon-sdk/source/lib/sdk/util/deprecate.js
@@ -4,22 +4,26 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { get, format } = require("../console/traceback");
+const { get: getPref } = require("../preferences/service");
+const PREFERENCE = "devtools.errorconsole.deprecation_warnings";
 
 function deprecateUsage(msg) {
   // Print caller stacktrace in order to help figuring out which code
   // does use deprecated thing
   let stack = get().slice(2);
-  console.error("DEPRECATED: " + msg + "\n" + format(stack));
+
+  if (getPref(PREFERENCE)) 
+    console.error("DEPRECATED: " + msg + "\n" + format(stack));
 }
 exports.deprecateUsage = deprecateUsage;
 
 function deprecateFunction(fun, msg) {
   return function deprecated() {
     deprecateUsage(msg);
     return fun.apply(this, arguments);
   };
--- a/addon-sdk/source/test/private-browsing/global.js
+++ b/addon-sdk/source/test/private-browsing/global.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const timer = require("sdk/timers");
 const { LoaderWithHookedConsole, deactivate, pb, pbUtils } = require("./helper");
 const tabs = require("sdk/tabs");
 const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils');
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 exports["test activate private mode via handler"] = function(test) {
   test.waitUntilDone();
 
   function onReady(tab) {
     if (tab.url == "about:robots")
       tab.close(function() pb.activate());
   }
@@ -169,16 +171,17 @@ exports.testBothListeners = function(tes
 
   pb.on("start", onStart);
   pb.on("start", onStart2);
   pb.activate();
 };
 
 exports.testAutomaticUnload = function(test) {
   test.waitUntilDone();
+  setPref(DEPRECATE_PREF, true);
 
   // Create another private browsing instance and unload it
   let { loader, errors } = LoaderWithHookedConsole(module);
   let pb2 = loader.require("sdk/private-browsing");
   let called = false;
   pb2.on("start", function onStart() {
     called = true;
     test.fail("should not be called:x");
--- a/addon-sdk/source/test/tabs/test-fennec-tabs.js
+++ b/addon-sdk/source/test/tabs/test-fennec-tabs.js
@@ -3,16 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { Cc, Ci } = require('chrome');
 const { Loader, LoaderWithHookedConsole } = require('sdk/test/loader');
 const timer = require('sdk/timers');
 const tabs = require('sdk/tabs');
 const windows = require('sdk/windows');
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 const tabsLen = tabs.length;
 const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>';
 
 // Fennec error message dispatched on all currently unimplement tab features,
 // that match LoaderWithHookedConsole messages object pattern
 const ERR_FENNEC_MSG = {
   type: "error",
@@ -91,16 +93,17 @@ exports.testAutomaticDestroy = function(
     });
   });
 
   tabs.open('data:text/html;charset=utf-8,foo');
 };
 
 // TEST: tab properties
 exports.testTabProperties = function(assert, done) {
+  setPref(DEPRECATE_PREF, true);
   let { loader, messages } = LoaderWithHookedConsole();
   let tabs = loader.require('sdk/tabs');
 
   let url = "data:text/html;charset=utf-8,<html><head><title>foo</title></head><body>foo</body></html>";
   let tabsLen = tabs.length;
   tabs.open({
     url: url,
     onReady: function(tab) {
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -7,16 +7,18 @@ const { Cc, Ci } = require('chrome');
 const { Loader } = require('sdk/test/loader');
 const timer = require('sdk/timers');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
 const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { open, focus, close } = require('sdk/window/helpers');
 const { StringBundle } = require('sdk/deprecated/app-strings');
 const tabs = require('sdk/tabs');
 const { browserWindows } = require('sdk/windows');
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAA";
 
 // Bug 682681 - tab.title should never be empty
 exports.testBug682681_aboutURI = function(assert, done) {
   let tabStrings = StringBundle('chrome://browser/locale/tabbrowser.properties');
 
   tabs.on('ready', function onReady(tab) {
@@ -899,16 +901,17 @@ exports.testOnLoadEventWithImage = funct
         assert.pass('first onLoad event occured');
         tab.reload();
       }
     }
   });
 };
 
 exports.testFaviconGetterDeprecation = function (assert, done) {
+  setPref(DEPRECATE_PREF, true);
   const { LoaderWithHookedConsole } = require("sdk/test/loader");
   let { loader, messages } = LoaderWithHookedConsole(module);
   let tabs = loader.require('sdk/tabs');
 
   tabs.open({
     url: 'data:text/html;charset=utf-8,',
     onOpen: function (tab) {
       let favicon = tab.favicon;
--- a/addon-sdk/source/test/test-addon-installer.js
+++ b/addon-sdk/source/test/test-addon-installer.js
@@ -43,31 +43,31 @@ exports["test Install"] = function (asse
       });
     },
     function onFailure(code) {
       assert.fail("Install failed: "+code);
       observers.remove("addon-install-unit-test", eventsObserver);
       done();
     }
   );
-}
+};
 
 exports["test Failing Install With Invalid Path"] = function (assert, done) {
   AddonInstaller.install("invalid-path").then(
     function onInstalled(id) {
       assert.fail("Unexpected success");
       done();
     },
     function onFailure(code) {
       assert.equal(code, AddonInstaller.ERROR_FILE_ACCESS,
                        "Got expected error code");
       done();
     }
   );
-}
+};
 
 exports["test Failing Install With Invalid File"] = function (assert, done) {
   let directory = system.pathFor("ProfD");
   AddonInstaller.install(directory).then(
     function onInstalled(id) {
       assert.fail("Unexpected success");
       done();
     },
@@ -126,11 +126,43 @@ exports["test Update"] = function (asser
   }
 
   function next() {
     events = [];
     AddonInstaller.install(ADDON_PATH).then(onInstalled, onFailure);
   }
 
   next();
-}
+};
+
+exports['test Uninstall failure'] = function (assert, done) {
+  AddonInstaller.uninstall('invalid-addon-path').then(
+    () => assert.fail('Addon uninstall should not resolve successfully'),
+    () => assert.pass('Addon correctly rejected invalid uninstall')
+  ).then(done, assert.fail);
+};
+
+exports['test Addon Disable'] = function (assert, done) {
+  let ensureActive = (addonId) => AddonInstaller.isActive(addonId).then(state => {
+    assert.equal(state, true, 'Addon should be enabled by default');
+    return addonId;
+  });
+  let ensureInactive = (addonId) => AddonInstaller.isActive(addonId).then(state => {
+    assert.equal(state, false, 'Addon should be disabled after disabling');
+    return addonId;
+  });
+
+  AddonInstaller.install(ADDON_PATH)
+    .then(ensureActive)
+    .then(AddonInstaller.disable)
+    .then(ensureInactive)
+    .then(AddonInstaller.uninstall)
+    .then(done, assert.fail);
+};
+
+exports['test Disable failure'] = function (assert, done) {
+  AddonInstaller.disable('not-an-id').then(
+    () => assert.fail('Addon disable should not resolve successfully'),
+    () => assert.pass('Addon correctly rejected invalid disable')
+  ).then(done, assert.fail);
+};
 
 require("test").run(exports);
--- a/addon-sdk/source/test/test-addon-page.js
+++ b/addon-sdk/source/test/test-addon-page.js
@@ -13,27 +13,30 @@ module.metadata = {
 const { isTabOpen, activateTab, openTab,
         closeTab, getURI } = require('sdk/tabs/utils');
 const windows = require('sdk/deprecated/window-utils');
 const { LoaderWithHookedConsole } = require('sdk/test/loader');
 const { setTimeout } = require('sdk/timers');
 const { is } = require('sdk/system/xul-app');
 const tabs = require('sdk/tabs');
 const isAustralis = "gCustomizeMode" in windows.activeBrowserWindow;
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 let uri = require('sdk/self').data.url('index.html');
 
 function isChromeVisible(window) {
   let x = window.document.documentElement.getAttribute('disablechrome')
   return x !== 'true';
 }
 
 exports['test add-on page deprecation message'] = function(assert) {
   let { loader, messages } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
+  setPref(DEPRECATE_PREF, true);
 
   assert.equal(messages.length, 1, "only one error is dispatched");
   assert.equal(messages[0].type, "error", "the console message is an error");
 
   let msg = messages[0].msg;
 
   assert.ok(msg.indexOf("DEPRECATED") === 0,
             "The message is deprecation message");
--- a/addon-sdk/source/test/test-content-events.js
+++ b/addon-sdk/source/test/test-content-events.js
@@ -36,28 +36,30 @@ let close = function(tab) {
   return promise;
 }
 
 exports["test multiple tabs"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/content/events");
   let { on, off } = loader.require("sdk/event/core");
   let actual = [];
-  on(events, "data", function({type, target, timeStamp}) {
+
+  on(events, "data", handler);
+  function handler ({type, target, timeStamp}) {
     // ignore about:blank pages and *-document-global-created
     // events that are not very consistent.
     // ignore http:// requests, as Fennec's `about:home` page
     // displays add-ons a user could install
     if (target.URL !== "about:blank" &&
         target.URL !== "about:home" &&
         !target.URL.match(/^https?:\/\//i) &&
         type !== "chrome-document-global-created" &&
         type !== "content-document-global-created")
       actual.push(type + " -> " + target.URL)
-  });
+  }
 
   let window = getMostRecentBrowserWindow();
   let firstTab = open("data:text/html,first-tab", window);
 
   when("pageshow", firstTab).
     then(close).
     then(use(window)).
     then(open("data:text/html,second-tab")).
@@ -73,33 +75,35 @@ exports["test multiple tabs"] = function
         "DOMContentLoaded -> data:text/html,second-tab",
         "load -> data:text/html,second-tab",
         "pageshow -> data:text/html,second-tab"
       ], "all events dispatche as expeced")
     }, function(reason) {
       assert.fail(Error(reason));
     }).then(function() {
       loader.unload();
+      off(events, "data", handler);
       done();
     });
 };
 
 exports["test nested frames"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/content/events");
   let { on, off } = loader.require("sdk/event/core");
   let actual = [];
-  on(events, "data", function({type, target, timeStamp}) {
+  on(events, "data", handler);
+  function handler ({type, target, timeStamp}) {
     // ignore about:blank pages and *-global-created
     // events that are not very consistent.
     if (target.URL !== "about:blank" &&
        type !== "chrome-document-global-created" &&
        type !== "content-document-global-created")
       actual.push(type + " -> " + target.URL)
-  });
+  }
 
   let window =  getMostRecentBrowserWindow();
   let uri = encodeURI("data:text/html,<iframe src='data:text/html,iframe'>");
   let tab = open(uri, window);
 
   when("pageshow", tab).
     then(close).
     then(function() {
@@ -112,13 +116,14 @@ exports["test nested frames"] = function
         "pageshow -> data:text/html,iframe",
         "load -> " + uri,
         "pageshow -> " + uri
       ], "events where dispatched")
     }, function(reason) {
       assert.fail(Error(reason))
     }).then(function() {
       loader.unload();
+      off(events, "data", handler);
       done();
     });
 };
 
 require("test").run(exports);
--- a/addon-sdk/source/test/test-content-worker.js
+++ b/addon-sdk/source/test/test-content-worker.js
@@ -11,16 +11,18 @@ module.metadata = {
   }
 };
 
 const { Cc, Ci } = require("chrome");
 const { setTimeout } = require("sdk/timers");
 const { LoaderWithHookedConsole } = require("sdk/test/loader");
 const { Worker } = require("sdk/content/worker");
 const { close } = require("sdk/window/helpers");
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
 
 function makeWindow(contentURL) {
   let content =
     "<?xml version=\"1.0\"?>" +
     "<window " +
     "xmlns=\"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul\">" +
@@ -653,16 +655,17 @@ exports["test:check worker API with page
 
   }
 );
 
 exports["test:global postMessage"] = WorkerTest(
   DEFAULT_CONTENT_URL,
   function(assert, browser, done) {
     let { loader } = LoaderWithHookedConsole(module, onMessage);
+    setPref(DEPRECATE_PREF, true);
 
     // Intercept all console method calls
     let seenMessages = 0;
     function onMessage(type, message) {
       seenMessages++;
       assert.equal(type, "error", "Should be an error");
       assert.equal(message, "DEPRECATED: The global `postMessage()` function in " +
                             "content scripts is deprecated in favor of the " +
--- a/addon-sdk/source/test/test-deprecate.js
+++ b/addon-sdk/source/test/test-deprecate.js
@@ -1,16 +1,21 @@
 /* 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 deprecate = require("sdk/util/deprecate");
 const { LoaderWithHookedConsole } = require("sdk/test/loader");
+const { get, set } = require("sdk/preferences/service");
+const PREFERENCE = "devtools.errorconsole.deprecation_warnings";
 
 exports["test Deprecate Usage"] = function testDeprecateUsage(assert) {
+  set(PREFERENCE, true);
   let { loader, messages } = LoaderWithHookedConsole(module);
   let deprecate = loader.require("sdk/util/deprecate");
 
   function functionIsDeprecated() {
     deprecate.deprecateUsage("foo");
   }
 
   functionIsDeprecated();
@@ -26,16 +31,17 @@ exports["test Deprecate Usage"] = functi
             "message contains name of the caller function");
   assert.ok(msg.indexOf(module.uri) !== -1,
              "message contains URI of the caller module");
 
   loader.unload();
 }
 
 exports["test Deprecate Function"] = function testDeprecateFunction(assert) {
+  set(PREFERENCE, true);
   let { loader, messages } = LoaderWithHookedConsole(module);
   let deprecate = loader.require("sdk/util/deprecate");
 
   let self = {};
   let arg1 = "foo";
   let arg2 = {};
 
   function originalFunction(a1, a2) {
@@ -58,16 +64,17 @@ exports["test Deprecate Function"] = fun
             "message contains name of the caller function");
   assert.ok(msg.indexOf(module.uri) !== -1,
             "message contains URI of the caller module");
 
   loader.unload();
 }
 
 exports.testDeprecateEvent = function(assert, done) {
+  set(PREFERENCE, true);
   let { loader, messages } = LoaderWithHookedConsole(module);
   let deprecate = loader.require("sdk/util/deprecate");
 
   let { on, emit } = loader.require('sdk/event/core');
   let testObj = {};
   testObj.on = deprecate.deprecateEvent(on.bind(null, testObj), 'BAD', ['fire']);
 
   testObj.on('fire', function() {
@@ -86,9 +93,68 @@ exports.testDeprecateEvent = function(as
   assert.ok(msg.indexOf("deprecateEvent") !== -1,
             "message contains name of the caller function");
   assert.ok(msg.indexOf(module.uri) !== -1,
             "message contains URI of the caller module");
 
   emit(testObj, 'fire');
 }
 
+exports.testDeprecateSettingToggle = function (assert, done) {
+  let { loader, messages } = LoaderWithHookedConsole(module);
+  let deprecate = loader.require("sdk/util/deprecate");
+  
+  function fn () { deprecate.deprecateUsage("foo"); }
+
+  set(PREFERENCE, false);
+  fn();
+  assert.equal(messages.length, 0, 'no deprecation warnings');
+  
+  set(PREFERENCE, true);
+  fn();
+  assert.equal(messages.length, 1, 'deprecation warnings when toggled');
+
+  set(PREFERENCE, false);
+  fn();
+  assert.equal(messages.length, 1, 'no new deprecation warnings');
+  done();
+};
+
+exports.testDeprecateSetting = function (assert, done) {
+  // Set devtools.errorconsole.deprecation_warnings to false
+  set(PREFERENCE, false);
+
+  let { loader, messages } = LoaderWithHookedConsole(module);
+  let deprecate = loader.require("sdk/util/deprecate");
+
+  // deprecateUsage test
+  function functionIsDeprecated() {
+    deprecate.deprecateUsage("foo");
+  }
+  functionIsDeprecated();
+
+  assert.equal(messages.length, 0,
+    "no errors dispatched on deprecateUsage");
+
+  // deprecateFunction test
+  function originalFunction() {};
+
+  let deprecateFunction = deprecate.deprecateFunction(originalFunction,
+                                                       "bar");
+  deprecateFunction();
+
+  assert.equal(messages.length, 0,
+    "no errors dispatched on deprecateFunction");
+
+  // deprecateEvent
+  let { on, emit } = loader.require('sdk/event/core');
+  let testObj = {};
+  testObj.on = deprecate.deprecateEvent(on.bind(null, testObj), 'BAD', ['fire']);
+
+  testObj.on('fire', () => {
+    assert.equal(messages.length, 0,
+      "no errors dispatched on deprecateEvent");
+    done();
+  });
+
+  emit(testObj, 'fire');
+}
 require("test").run(exports);
--- a/addon-sdk/source/test/test-hotkeys.js
+++ b/addon-sdk/source/test/test-hotkeys.js
@@ -11,16 +11,18 @@ const timer = require("sdk/timers");
 const winUtils = require("sdk/deprecated/window-utils");
 
 exports["test hotkey: function key"] = function(assert, done) {
   var element = winUtils.activeBrowserWindow.document.documentElement;
   var showHotKey = Hotkey({
     combo: "f1",
     onPress: function() {
       assert.pass("first callback is called");
+      assert.equal(this, showHotKey,
+        'Context `this` in `onPress` should be the hotkey object');
       keyDown(element, "f2");
       showHotKey.destroy();
     }
   });
 
   var hideHotKey = Hotkey({
     combo: "f2",
     onPress: function() {
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -19,16 +19,17 @@ const { isPrivate } = require('sdk/priva
 const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const { defer, all } = require('sdk/core/promise');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { getWindow } = require('sdk/panel/window');
 const { pb } = require('./private-browsing/helper');
 const { URL } = require('sdk/url');
 
 const SVG_URL = self.data.url('mofo_logo.SVG');
+const Isolate = fn => '(' + fn + ')()';
 
 function ignorePassingDOMNodeWarning(type, message) {
   if (type !== 'warn' || !message.startsWith('Passing a DOM node'))
     console[type](message);
 }
 
 function makeEmptyPrivateBrowserWindow(options) {
   options = options || {};
@@ -161,64 +162,63 @@ exports["test Document Reload"] = functi
     }
   });
   assert.pass('Panel was created');
 };
 
 exports["test Parent Resize Hack"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
-  let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
-                      getService(Ci.nsIWindowMediator).
-                      getMostRecentWindow("navigator:browser");
-  let docShell = browserWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                  .getInterface(Ci.nsIWebNavigation)
-                  .QueryInterface(Ci.nsIDocShell);
-  if (!("allowWindowControl" in docShell)) {
-    // bug 635673 is not fixed in this firefox build
-    assert.pass("allowWindowControl attribute that allow to fix browser window " +
-              "resize is not available on this build.");
-    return;
-  }
+  let browserWindow = getMostRecentBrowserWindow();
 
-  let previousWidth = browserWindow.outerWidth, previousHeight = browserWindow.outerHeight;
+  let previousWidth = browserWindow.outerWidth;
+  let previousHeight = browserWindow.outerHeight;
 
   let content = "<script>" +
                 "function contentResize() {" +
                 "  resizeTo(200,200);" +
                 "  resizeBy(200,200);" +
+                "  window.postMessage('resize-attempt', '*');" +
                 "}" +
                 "</script>" +
                 "Try to resize browser window";
+
   let panel = Panel({
     contentURL: "data:text/html;charset=utf-8," + encodeURIComponent(content),
-    contentScript: "self.on('message', function(message){" +
-                   "  if (message=='resize') " +
-                   "    unsafeWindow.contentResize();" +
-                   "});",
     contentScriptWhen: "ready",
+    contentScript: Isolate(() => {
+        self.on('message', message => {
+          if (message === 'resize') unsafeWindow.contentResize();
+        });
+
+        window.addEventListener('message', ({ data }) => self.postMessage(data));
+      }),
     onMessage: function (message) {
+      if (message !== "resize-attempt") return;
 
+      assert.equal(browserWindow, getMostRecentBrowserWindow(),
+        "The browser window is still the same");
+      assert.equal(previousWidth, browserWindow.outerWidth,
+        "Size doesn't change by calling resizeTo/By/...");
+      assert.equal(previousHeight, browserWindow.outerHeight,
+        "Size doesn't change by calling resizeTo/By/...");
+
+      try {
+        panel.destroy();
+      }
+      catch (e) {
+        assert.fail(e);
+        throw e;
+      }
+
+      done();
     },
-    onShow: function () {
-      panel.postMessage('resize');
-      timer.setTimeout(function () {
-        assert.equal(previousWidth,browserWindow.outerWidth,"Size doesn't change by calling resizeTo/By/...");
-        assert.equal(previousHeight,browserWindow.outerHeight,"Size doesn't change by calling resizeTo/By/...");
-        try {
-          panel.destroy();
-        }
-        catch (e) {
-          console.exception(e);
-          throw e;
-        }
-        done();
-      },0);
-    }
+    onShow: () => panel.postMessage('resize')
   });
+
   panel.show();
 }
 
 exports["test Resize Panel"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   // These tests fail on Linux if the browser window in which the panel
   // is displayed is not active.  And depending on what other tests have run
--- a/addon-sdk/source/test/test-private-browsing.js
+++ b/addon-sdk/source/test/test-private-browsing.js
@@ -13,16 +13,18 @@ const { isPrivateBrowsingSupported } = r
 const { is } = require('sdk/system/xul-app');
 const { isPrivate } = require('sdk/private-browsing');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
 const { LoaderWithHookedConsole } = require("sdk/test/loader");
 const { getMode, isGlobalPBSupported,
         isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
 const { pb } = require('./private-browsing/helper');
 const prefs = require('sdk/preferences/service');
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 const kAutoStartPref = "browser.privatebrowsing.autostart";
 
 // is global pb is enabled?
 if (isGlobalPBSupported) {
   merge(module.exports, require('./private-browsing/global'));
 
   exports.testGlobalOnlyOnFirefox = function(test) {
@@ -50,16 +52,17 @@ exports.testIsPrivateDefaults = function
   test.assertEqual(isPrivate('test'), false, 'strings are not private');
   test.assertEqual(isPrivate({}), false, 'random objects are not private');
   test.assertEqual(isPrivate(4), false, 'numbers are not private');
   test.assertEqual(isPrivate(/abc/), false, 'regex are not private');
   test.assertEqual(isPrivate(function() {}), false, 'functions are not private');
 };
 
 exports.testWindowDefaults = function(test) {
+  setPref(DEPRECATE_PREF, true);
   // Ensure that browserWindow still works while being deprecated
   let { loader, messages } = LoaderWithHookedConsole(module);
   let windows = loader.require("sdk/windows").browserWindows;
   test.assertEqual(windows.activeWindow.isPrivateBrowsing, false,
                    'window is not private browsing by default');
   test.assertMatches(messages[0].msg, /DEPRECATED.+isPrivateBrowsing/,
                      'isPrivateBrowsing is deprecated');
 
--- a/addon-sdk/source/test/test-xhr.js
+++ b/addon-sdk/source/test/test-xhr.js
@@ -1,21 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  'use strict'
 
 const { XMLHttpRequest } = require('sdk/net/xhr');
 const { LoaderWithHookedConsole } = require('sdk/test/loader');
 const { data } = require('sdk/self');
-
+const { set: setPref } = require("sdk/preferences/service");
+const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 exports.testAPIExtension = function(assert) {
   let { loader, messages } = LoaderWithHookedConsole(module);
   let { XMLHttpRequest } = loader.require("sdk/net/xhr");
+  setPref(DEPRECATE_PREF, true);
 
   let xhr = new XMLHttpRequest();
   assert.equal(typeof(xhr.forceAllowThirdPartyCookie), "function",
                "forceAllowThirdPartyCookie is defined");
   assert.equal(xhr.forceAllowThirdPartyCookie(), undefined,
                "function can be called");
 
   assert.ok(messages[0].msg.indexOf("`xhr.forceAllowThirdPartyCookie()` is deprecated") >= 0,
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1,15 +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/. */
 
 #filter substitution
 
-pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
+pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.html");
 pref("browser.chromeURL", "chrome://browser/content/");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
@@ -745,8 +745,13 @@ pref("disk_space_watcher.enabled", true)
 
 // Enable promise
 pref("dom.promise.enabled", false);
 
 // Allow ADB to run for this many hours before disabling
 // (only applies when marionette is disabled)
 // 0 disables the timer.
 pref("b2g.adb.timeout-hours", 12);
+
+// enable Skia/GL (OpenGL-accelerated 2D drawing) for large enough 2d canvases,
+// falling back to Skia/software for smaller canvases
+pref("gfx.canvas.azure.backends", "skia");
+pref("gfx.canvas.azure.accelerated", true);
--- a/b2g/chrome/content/payment.js
+++ b/b2g/chrome/content/payment.js
@@ -4,24 +4,41 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This JS shim contains the callbacks to fire DOMRequest events for
 // navigator.pay API within the payment processor's scope.
 
 "use strict";
 
-let _DEBUG = false;
-function _debug(s) { dump("== Payment flow == " + s + "\n"); }
-_debug("Frame script injected");
-
 let { classes: Cc, interfaces: Ci, utils: Cu }  = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+const PREF_DEBUG = "dom.payment.debug";
+
+let _debug;
+try {
+  _debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
+           && Services.prefs.getBoolPref(PREF_DEBUG);
+} catch(e){
+  _debug = false;
+}
+
+function LOG(s) {
+  if (!_debug) {
+    return;
+  }
+  dump("== Payment flow == " + s + "\n");
+}
+
+if (_debug) {
+  LOG("Frame script injected");
+}
+
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
@@ -141,32 +158,32 @@ let PaymentProvider = {
     gBrowser.shell.sendChromeEvent(detail);
 
 #ifdef MOZ_B2G_RIL
     this._cleanUp();
 #endif
   },
 
   paymentSuccess: function paymentSuccess(aResult) {
-    if (_DEBUG) {
-      _debug("paymentSuccess " + aResult);
+    if (_debug) {
+      LOG("paymentSuccess " + aResult);
     }
 
     PaymentProvider._closePaymentFlowDialog(function notifySuccess() {
       if (!gRequestId) {
         return;
       }
       cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
                                                  requestId: gRequestId });
     });
   },
 
   paymentFailed: function paymentFailed(aErrorMsg) {
-    if (_DEBUG) {
-      _debug("paymentFailed " + aErrorMsg);
+    if (_debug) {
+      LOG("paymentFailed " + aErrorMsg);
     }
 
     PaymentProvider._closePaymentFlowDialog(function notifyError() {
       if (!gRequestId) {
         return;
       }
       cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
                                                 requestId: gRequestId });
@@ -190,28 +207,28 @@ let PaymentProvider = {
   get mnc() {
     return [iccProvider.iccInfo.mnc];
   },
 
   _silentNumbers: null,
   _silentSmsObservers: null,
 
   sendSilentSms: function sendSilentSms(aNumber, aMessage) {
-    if (_DEBUG) {
-      _debug("Sending silent message " + aNumber + " - " + aMessage);
+    if (_debug) {
+      LOG("Sending silent message " + aNumber + " - " + aMessage);
     }
 
     let request = new SilentSmsRequest();
     smsService.send(aNumber, aMessage, true, request);
     return request;
   },
 
   observeSilentSms: function observeSilentSms(aNumber, aCallback) {
-    if (_DEBUG) {
-      _debug("observeSilentSms " + aNumber);
+    if (_debug) {
+      LOG("observeSilentSms " + aNumber);
     }
 
     if (!this._silentSmsObservers) {
       this._silentSmsObservers = {};
       this._silentNumbers = [];
       Services.obs.addObserver(this._onSilentSms.bind(this),
                                kSilentSmsReceivedTopic,
                                false);
@@ -224,61 +241,61 @@ let PaymentProvider = {
     }
 
     if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
       this._silentSmsObservers[aNumber].push(aCallback);
     }
   },
 
   removeSilentSmsObserver: function removeSilentSmsObserver(aNumber, aCallback) {
-    if (_DEBUG) {
-      _debug("removeSilentSmsObserver " + aNumber);
+    if (_debug) {
+      LOG("removeSilentSmsObserver " + aNumber);
     }
 
     if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
-      if (_DEBUG) {
-        _debug("No observers for " + aNumber);
+      if (_debug) {
+        LOG("No observers for " + aNumber);
       }
       return;
     }
 
     let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
     if (index != -1) {
       this._silentSmsObservers[aNumber].splice(index, 1);
       if (this._silentSmsObservers[aNumber].length == 0) {
         this._silentSmsObservers[aNumber] = null;
         this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
         smsService.removeSilentNumber(aNumber);
       }
-    } else if (_DEBUG) {
-      _debug("No callback found for " + aNumber);
+    } else if (_debug) {
+      LOG("No callback found for " + aNumber);
     }
   },
 
   _onSilentSms: function _onSilentSms(aSubject, aTopic, aData) {
-    if (_DEBUG) {
-      _debug("Got silent message! " + aSubject.sender + " - " + aSubject.body);
+    if (_debug) {
+      LOG("Got silent message! " + aSubject.sender + " - " + aSubject.body);
     }
 
     let number = aSubject.sender;
     if (!number || this._silentNumbers.indexOf(number) == -1) {
-      if (_DEBUG) {
-        _debug("No observers for " + number);
+      if (_debug) {
+        LOG("No observers for " + number);
       }
       return;
     }
 
     this._silentSmsObservers[number].forEach(function(callback) {
       callback(aSubject);
     });
   },
 
   _cleanUp: function _cleanUp() {
-    if (_DEBUG) {
-      _debug("Cleaning up!");
+    if (_debug) {
+      LOG("Cleaning up!");
     }
 
     if (!this._silentNumbers) {
       return;
     }
 
     while (this._silentNumbers.length) {
       let number = this._silentNumbers.pop();
--- a/b2g/chrome/content/screen.js
+++ b/b2g/chrome/content/screen.js
@@ -6,17 +6,17 @@
 // 
 
 // We do this on ContentStart because querying the displayDPI fails otherwise.
 window.addEventListener('ContentStart', function() {
   // This is the toplevel <window> element
   let shell = document.getElementById('shell');
 
   // The <browser> element inside it
-  let browser = document.getElementById('homescreen');
+  let browser = document.getElementById('systemapp');
 
   // Figure out the native resolution of the screen
   let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Components.interfaces.nsIDOMWindowUtils);
   let hostDPI = windowUtils.displayDPI;
 
   let DEFAULT_SCREEN = "320x480";
 
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -309,18 +309,25 @@ let AdbController = {
       //
       // By waiting until both values are properly initialized, we avoid
       // turning adb on or off accidentally.
       if (this.DEBUG) {
         this.debug("updateState: Waiting for all vars to be initialized");
       }
       return;
     }
+
+    // Check if we have a remote debugging session going on. If so, we won't
+    // disable adb even if the screen is locked.
+    let isDebugging = Object.keys(DebuggerServer._connections).length > 0;
+    debug("isDebugging=" + isDebugging);
+
     let enableAdb = this.remoteDebuggerEnabled &&
-      !(this.lockEnabled && this.locked);
+      (!(this.lockEnabled && this.locked) || isDebugging);
+
     let useDisableAdbTimer = true;
     try {
       if (Services.prefs.getBoolPref("marionette.defaultPrefs.enabled")) {
         // Marionette is enabled. Marionette requires that adb be on (and also
         // requires that remote debugging be off). The fact that marionette
         // is enabled also implies that we're doing a non-production build, so
         // we want adb enabled all of the time.
         enableAdb = true;
@@ -361,17 +368,17 @@ let AdbController = {
       }
       try {
         libcutils.property_set("persist.sys.usb.config", newConfig);
       } catch(e) {
         dump("Error configuring adb: " + e);
       }
     }
     if (useDisableAdbTimer) {
-      if (enableAdb) {
+      if (enableAdb && !isDebugging) {
         this.startDisableAdbTimer();
       } else {
         this.stopDisableAdbTimer();
       }
     }
   }
 };
 
rename from b2g/chrome/content/shell.xul
rename to b2g/chrome/content/shell.html
--- a/b2g/chrome/content/shell.xul
+++ b/b2g/chrome/content/shell.html
@@ -1,26 +1,41 @@
-<?xml version="1.0"?>
-
+<!DOCTYPE html>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
 
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        id="shell"
-        windowtype="navigator:browser"
+<html xmlns="http://www.w3.org/1999/xhtml "
+      id="shell"
+      windowtype="navigator:browser"
 #ifdef ANDROID
-        sizemode="fullscreen"
+      sizemode="fullscreen"
 #endif
-        style="background: black; overflow: hidden; width:320px; height:480px"
-        onunload="shell.stop();">
+      style="background: black; overflow: hidden; width:100%; height:100%; padding: 0px !important"
+      onunload="shell.stop();">
 
-  <script type="application/javascript" src="chrome://browser/content/settings.js"/>
-  <script type="application/javascript" src="chrome://browser/content/shell.js"/>
+<head>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/settings.js"> </script>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/shell.js"> </script>
 
 #ifndef MOZ_WIDGET_GONK
+
   <!-- this script handles the screen argument for desktop builds -->
-  <script type="application/javascript" src="chrome://browser/content/screen.js"/>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/screen.js"> </script>
   <!-- this script handles the "runapp" argument for desktop builds -->
-  <script type="application/javascript" src="chrome://browser/content/runapp.js"/>
+  <script type="application/javascript;version=1.8"
+          src="chrome://browser/content/runapp.js"> </script>
 #endif
-  <!-- The html:iframe containing the UI is created here. -->
-</window>
+</head>
+  <body id="container" style="margin: 0px; width:100%; height:100%;">
+#ifdef MOZ_WIDGET_COCOA
+    <!--
+     If the document is empty at startup, we don't display the window
+     at all on Mac OS...
+    -->
+    <h1 id="placeholder">wtf mac os!</h1>
+#endif
+    <!-- The html:iframe containing the UI is created here. -->
+  </body>
+</html>
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -178,17 +178,17 @@ var shell = {
 
         Services.obs.removeObserver(observer, topic);
       }
     }, "network-interface-state-changed", false);
   },
 
   get contentBrowser() {
     delete this.contentBrowser;
-    return this.contentBrowser = document.getElementById('homescreen');
+    return this.contentBrowser = document.getElementById('systemapp');
   },
 
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
       if (homeSrc)
         return homeSrc;
     } catch (e) {}
@@ -261,35 +261,39 @@ var shell = {
     let homeURL = this.homeURL;
     if (!homeURL) {
       let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
       alert(msg);
       return;
     }
 
     let manifestURL = this.manifestURL;
-    // <html:iframe id="homescreen"
+    // <html:iframe id="systemapp"
     //              mozbrowser="true" allowfullscreen="true"
-    //              style="overflow: hidden; -moz-box-flex: 1; border: none;"
+    //              style="overflow: hidden; height: 100%; width: 100%; border: none;"
     //              src="data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
-    let browserFrame =
+    let systemAppFrame =
       document.createElementNS('http://www.w3.org/1999/xhtml', 'html:iframe');
-    browserFrame.setAttribute('id', 'homescreen');
-    browserFrame.setAttribute('mozbrowser', 'true');
-    browserFrame.setAttribute('mozapp', manifestURL);
-    browserFrame.setAttribute('allowfullscreen', 'true');
-    browserFrame.setAttribute('style', "overflow: hidden; -moz-box-flex: 1; border: none;");
-    browserFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
-    document.getElementById('shell').appendChild(browserFrame);
+    systemAppFrame.setAttribute('id', 'systemapp');
+    systemAppFrame.setAttribute('mozbrowser', 'true');
+    systemAppFrame.setAttribute('mozapp', manifestURL);
+    systemAppFrame.setAttribute('allowfullscreen', 'true');
+    systemAppFrame.setAttribute('style', "overflow: hidden; height: 100%; width: 100%; border: none;");
+    systemAppFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
+    let container = document.getElementById('container');
+#ifdef MOZ_WIDGET_COCOA
+    container.removeChild(document.getElementById('placeholder'));
+#endif
+    container.appendChild(systemAppFrame);
 
-    browserFrame.contentWindow
-                .QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIWebNavigation)
-                .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
-                                    .createInstance(Ci.nsISHistory);
+    systemAppFrame.contentWindow
+                  .QueryInterface(Ci.nsIInterfaceRequestor)
+                  .getInterface(Ci.nsIWebNavigation)
+                  .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
+                                      .createInstance(Ci.nsISHistory);
 
     // Capture all key events so we can filter out hardware buttons
     // And send them to Gaia via mozChromeEvents.
     // Ideally, hardware buttons wouldn't generate key events at all, or
     // if they did, they would use keycodes that conform to DOM 3 Events.
     // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
     window.addEventListener('keydown', this, true);
     window.addEventListener('keypress', this, true);
@@ -1016,16 +1020,20 @@ let RemoteDebugger = {
           DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
         }
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/styleeditor.js");
         DebuggerServer.enableWebappsContentActor = true;
       }
       DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
       DebuggerServer.registerModule("devtools/server/actors/device");
+
+      DebuggerServer.onConnectionChange = function(what) {
+        AdbController.updateState();
+      }
     }
 
     let port = Services.prefs.getIntPref('devtools.debugger.remote-port') || 6000;
     try {
       DebuggerServer.openListener(port);
     } catch (e) {
       dump('Unable to start debugger server: ' + e + '\n');
     }
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -7,17 +7,17 @@
 chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
   content/arrow.svg                     (content/arrow.svg)
 * content/dbg-browser-actors.js         (content/dbg-browser-actors.js)
   content/forms.js                      (content/forms.js)
 * content/settings.js                   (content/settings.js)
-* content/shell.xul                     (content/shell.xul)
+* content/shell.html                    (content/shell.html)
 * content/shell.js                      (content/shell.js)
 #ifndef ANDROID
   content/screen.js                     (content/screen.js)
   content/runapp.js                     (content/runapp.js)
 #endif
 * content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
 
--- a/b2g/components/Keyboard.jsm
+++ b/b2g/components/Keyboard.jsm
@@ -33,16 +33,22 @@ let Keyboard = {
 
     throw Error('no message manager set');
   },
 
   set messageManager(mm) {
     this._messageManager = mm;
   },
 
+  sendAsyncMessage: function(name, data) {
+    try {
+      this.messageManager.sendAsyncMessage(name, data);
+    } catch(e) { }
+  },
+
   init: function keyboardInit() {
     Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
     Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
     Services.obs.addObserver(this, 'oop-frameloader-crashed', false);
 
     for (let name of this._messageNames)
       ppmm.addMessageListener('Keyboard:' + name, this);
   },
@@ -181,38 +187,37 @@ let Keyboard = {
 
     browser.shell.sendChromeEvent({
       type: 'inputmethod-contextchange',
       inputType: msg.data.type
     });
   },
 
   setSelectedOption: function keyboardSetSelectedOption(msg) {
-    this.messageManager.sendAsyncMessage('Forms:Select:Choice', msg.data);
+    this.sendAsyncMessage('Forms:Select:Choice', msg.data);
   },
 
   setSelectedOptions: function keyboardSetSelectedOptions(msg) {
-    this.messageManager.sendAsyncMessage('Forms:Select:Choice', msg.data);
+    this.sendAsyncMessage('Forms:Select:Choice', msg.data);
   },
 
   setSelectionRange: function keyboardSetSelectionRange(msg) {
-    this.messageManager.sendAsyncMessage('Forms:SetSelectionRange', msg.data);
+    this.sendAsyncMessage('Forms:SetSelectionRange', msg.data);
   },
 
   setValue: function keyboardSetValue(msg) {
-    this.messageManager.sendAsyncMessage('Forms:Input:Value', msg.data);
+    this.sendAsyncMessage('Forms:Input:Value', msg.data);
   },
 
   removeFocus: function keyboardRemoveFocus() {
-    this.messageManager.sendAsyncMessage('Forms:Select:Blur', {});
+    this.sendAsyncMessage('Forms:Select:Blur', {});
   },
 
   replaceSurroundingText: function keyboardReplaceSurroundingText(msg) {
-    this.messageManager.sendAsyncMessage('Forms:ReplaceSurroundingText',
-                                         msg.data);
+    this.sendAsyncMessage('Forms:ReplaceSurroundingText', msg.data);
   },
 
   showInputMethodPicker: function keyboardShowInputMethodPicker() {
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     browser.shell.sendChromeEvent({
       type: "inputmethod-showall"
     });
   },
@@ -220,29 +225,29 @@ let Keyboard = {
   switchToNextInputMethod: function keyboardSwitchToNextInputMethod() {
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     browser.shell.sendChromeEvent({
       type: "inputmethod-next"
     });
   },
 
   getText: function keyboardGetText(msg) {
-    this.messageManager.sendAsyncMessage('Forms:GetText', msg.data);
+    this.sendAsyncMessage('Forms:GetText', msg.data);
   },
 
   sendKey: function keyboardSendKey(msg) {
-    this.messageManager.sendAsyncMessage('Forms:Input:SendKey', msg.data);
+    this.sendAsyncMessage('Forms:Input:SendKey', msg.data);
   },
 
   getContext: function keyboardGetContext(msg) {
-    this.messageManager.sendAsyncMessage('Forms:GetContext', msg.data);
+    this.sendAsyncMessage('Forms:GetContext', msg.data);
   },
 
   setComposition: function keyboardSetComposition(msg) {
-    this.messageManager.sendAsyncMessage('Forms:SetComposition', msg.data);
+    this.sendAsyncMessage('Forms:SetComposition', msg.data);
   },
 
   endComposition: function keyboardEndComposition(msg) {
-    this.messageManager.sendAsyncMessage('Forms:EndComposition', msg.data);
+    this.sendAsyncMessage('Forms:EndComposition', msg.data);
   }
 };
 
 Keyboard.init();
--- a/b2g/components/PaymentGlue.js
+++ b/b2g/components/PaymentGlue.js
@@ -12,25 +12,30 @@ Cu.import("resource://gre/modules/Servic
 // JS shim that contains the callback functions to be triggered from the
 // payment provider's code in order to fire DOMRequest events.
 const kPaymentShimFile = "chrome://browser/content/payment.js";
 
 // Type of MozChromEvents to handle payment dialogs.
 const kOpenPaymentConfirmationEvent = "open-payment-confirmation-dialog";
 const kOpenPaymentFlowEvent = "open-payment-flow-dialog";
 
+const PREF_DEBUG = "dom.payment.debug";
+
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
-function debug (s) {
-  //dump("-*- PaymentGlue: " + s + "\n");
-};
-
 function PaymentUI() {
+  try {
+    this._debug =
+      Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
+      && Services.prefs.getBoolPref(PREF_DEBUG);
+  } catch(e) {
+    this._debug = false;
+  }
 }
 
 PaymentUI.prototype = {
 
   confirmPaymentRequest: function confirmPaymentRequest(aRequestId,
                                                         aRequests,
                                                         aSuccessCb,
                                                         aErrorCb) {
@@ -124,17 +129,20 @@ PaymentUI.prototype = {
       let frame = evt.detail.frame;
       let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner)
                         .frameLoader;
       let mm = frameLoader.messageManager;
       try {
         mm.loadFrameScript(kPaymentShimFile, true);
         mm.sendAsyncMessage("Payment:LoadShim", { requestId: aRequestId });
       } catch (e) {
-        debug("Error loading " + kPaymentShimFile + " as a frame script: " + e);
+        if (this._debug) {
+          this.LOG("Error loading " + kPaymentShimFile + " as a frame script: "
+                    + e);
+        }
         _error("ERROR_LOADING_PAYMENT_SHIM");
       } finally {
         content.removeEventListener("mozContentEvent", loadPaymentShim);
       }
     }).bind(this));
 
     // We also listen for UI notifications about a closed payment flow. The UI
     // should provide the reason of the closure within the 'errorMsg' parameter
@@ -163,14 +171,21 @@ PaymentUI.prototype = {
     }
     content.removeEventListener("mozContentEvent", this._notifyPayFlowClosed);
   },
 
   getRandomId: function getRandomId() {
     return uuidgen.generateUUID().toString();
   },
 
+  LOG: function LOG(s) {
+    if (!this._debug) {
+      return;
+    }
+    dump("-*- PaymentGlue: " + s + "\n");
+  },
+
   classID: Components.ID("{8b83eabc-7929-47f4-8b48-4dea8d887e4b}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue])
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUI]);
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "3a11e24a45501f1fcad106ef588fcc7262722644", 
+    "revision": "627f494cb7b6eb49057434711d1070341430b367", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -455,18 +455,16 @@
 @BINPATH@/components/SettingsService.manifest
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/NetworkManager.manifest
 @BINPATH@/components/NetworkManager.js
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/MmsService.manifest
 @BINPATH@/components/MmsService.js
-@BINPATH@/components/SmsService.manifest
-@BINPATH@/components/SmsService.js
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 @BINPATH@/components/NetworkStatsManager.js
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1377553788000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1378508419000">
   <emItems>
       <emItem  blockID="i350" id="sqlmoz@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
@@ -298,16 +298,20 @@
       <emItem  blockID="i258" id="helperbar@helperbar.com">
                         <versionRange  minVersion="0" maxVersion="1.0" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i398" id="{377e5d4d-77e5-476a-8716-7e70a9272da0}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i447" id="{B18B1E5C-4D81-11E1-9C00-AFEB4824019B}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i256" id="/^[0-9a-f]+@[0-9a-f]+\.info/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i370" id="happylyrics@hpyproductions.net">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
@@ -346,16 +350,20 @@
       <emItem  blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
                         <versionRange  minVersion="2.2" maxVersion="2.2">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i115" id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i446" id="{E90FA778-C2B7-41D0-9FA9-3FEC1CA54D66}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
                         </emItem>
       <emItem  blockID="i52" id="ff-ext@youtube">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i224" id="{336D0C35-8A85-403a-B9D2-65C292C39087}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -652,16 +660,20 @@
       <emItem  blockID="i90" id="videoplugin@player.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i68" id="flashupdate@adobe.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i445" id="firefoxaddon@youtubeenhancer.com">
+                        <versionRange  minVersion="208.7.0" maxVersion="208.7.0" severity="3">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i441" id="{49c53dce-afa0-49a1-a08b-2eb8e8444128}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i282" id="{33e0daa6-3af3-d8b5-6752-10e949c61516}">
                         <versionRange  minVersion="0" maxVersion="1.1.999" severity="1">
                     </versionRange>
                   </emItem>
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -32,27 +32,28 @@
                 label="&openLinkInPrivateWindowCmd.label;"
                 accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                 oncommand="gContextMenu.openLinkInPrivateWindow();"/>
       <menuseparator id="context-sep-open"/>
       <menuitem id="context-bookmarklink"
                 label="&bookmarkThisLinkCmd.label;"
                 accesskey="&bookmarkThisLinkCmd.accesskey;"
                 oncommand="gContextMenu.bookmarkLink();"/>
-      <menuitem id="context-marklink"
-                accesskey="&social.marklink.accesskey;"
-                oncommand="gContextMenu.markLink();"/>
       <menuitem id="context-sharelink"
                 label="&shareLinkCmd.label;"
                 accesskey="&shareLinkCmd.accesskey;"
                 oncommand="gContextMenu.shareLink();"/>
       <menuitem id="context-savelink"
                 label="&saveLinkCmd.label;"
                 accesskey="&saveLinkCmd.accesskey;"
                 oncommand="gContextMenu.saveLink();"/>
+      <menu id="context-marklinkMenu" label="&social.marklinkMenu.label;"
+            accesskey="&social.marklink.accesskey;">
+        <menupopup/>
+      </menu>
       <menuitem id="context-copyemail"
                 label="&copyEmailCmd.label;"
                 accesskey="&copyEmailCmd.accesskey;"
                 oncommand="gContextMenu.copyEmail();"/>
       <menuitem id="context-copylink"
                 label="&copyLinkCmd.label;"
                 accesskey="&copyLinkCmd.accesskey;"
                 oncommand="goDoCommand('cmd_copyLink');"/>
@@ -237,27 +238,28 @@
                 label="&stopCmd.label;"
                 accesskey="&stopCmd.accesskey;"
                 command="Browser:Stop"/>
       <menuseparator id="context-sep-stop"/>
       <menuitem id="context-bookmarkpage"
                 label="&bookmarkPageCmd2.label;"
                 accesskey="&bookmarkPageCmd2.accesskey;"
                 oncommand="gContextMenu.bookmarkThisPage();"/>
-      <menuitem id="context-markpage"
-                accesskey="&social.markpage.accesskey;"
-                command="Social:TogglePageMark"/>
       <menuitem id="context-sharepage"
                 label="&sharePageCmd.label;"
                 accesskey="&sharePageCmd.accesskey;"
                 oncommand="SocialShare.sharePage();"/>
       <menuitem id="context-savepage"
                 label="&savePageCmd.label;"
                 accesskey="&savePageCmd.accesskey2;"
                 oncommand="gContextMenu.savePageAs();"/>
+      <menu id="context-markpageMenu" label="&social.markpageMenu.label;"
+            accesskey="&social.markpage.accesskey;">
+        <menupopup/>
+      </menu>
       <menuseparator id="context-sep-viewbgimage"/>
       <menuitem id="context-viewbgimage"
                 label="&viewBGImageCmd.label;"
                 accesskey="&viewBGImageCmd.accesskey;"
                 oncommand="gContextMenu.viewBGImage(event);"
                 onclick="checkForMiddleClick(this, event);"/>
       <menuitem id="context-undo"
                 label="&undoCmd.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -105,17 +105,16 @@
     <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
       oncommand="OpenBrowserWindow({private: true});"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
-    <command id="Social:TogglePageMark" oncommand="SocialMark.togglePageMark();" disabled="true"/>
     <command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
     <command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();" hidden="true"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
     <command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
     <command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
     <command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
   </commandset>
 
@@ -362,17 +361,17 @@
 #endif
     <key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #ifdef XP_WIN
 # Cmd+I is conventially mapped to Info on MacOS X, thus it should not be
 # overridden for other purposes there.
     <key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #endif
 
-    <key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>
+    <!--<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>-->
     <key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
 
     <key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
 
 #ifdef XP_MACOSX
     <key id="key_stop_mac" modifiers="accel" key="&stopCmd.macCommandKey;" command="Browser:Stop"/>
 #endif
 
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1,17 +1,17 @@
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 // the "exported" symbols
 let SocialUI,
     SocialChatBar,
     SocialFlyout,
-    SocialMark,
+    SocialMarks,
     SocialShare,
     SocialMenu,
     SocialToolbar,
     SocialSidebar;
 
 (function() {
 
 // The minimum sizes for the auto-resize panel code.
@@ -22,51 +22,65 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource:///modules/SharedFrame.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "OpenGraphBuilder", function() {
   let tmp = {};
   Cu.import("resource:///modules/Social.jsm", tmp);
   return tmp.OpenGraphBuilder;
 });
 
+XPCOMUtils.defineLazyGetter(this, "DynamicResizeWatcher", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/Social.jsm", tmp);
+  return tmp.DynamicResizeWatcher;
+});
+
+XPCOMUtils.defineLazyGetter(this, "sizeSocialPanelToContent", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/Social.jsm", tmp);
+  return tmp.sizeSocialPanelToContent;
+});
+
 SocialUI = {
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
-    Services.obs.addObserver(this, "social:page-mark-config", false);
     Services.obs.addObserver(this, "social:frameworker-error", false);
     Services.obs.addObserver(this, "social:provider-set", false);
     Services.obs.addObserver(this, "social:providers-changed", false);
     Services.obs.addObserver(this, "social:provider-reload", false);
     Services.obs.addObserver(this, "social:provider-installed", false);
     Services.obs.addObserver(this, "social:provider-uninstalled", false);
     Services.obs.addObserver(this, "social:provider-enabled", false);
     Services.obs.addObserver(this, "social:provider-disabled", false);
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
+    window.addEventListener("aftercustomization", function() {
+      if (SocialUI.enabled)
+        SocialMarks.populateContextMenu(SocialMarks);
+    }, false);
 
     if (!Social.initialized) {
       Social.init();
     } else if (Social.providers.length > 0) {
       // Social was initialized during startup in a previous window. If we have
       // providers enabled initialize the UI for this window.
       this.observe(null, "social:providers-changed", null);
       this.observe(null, "social:provider-set", Social.provider ? Social.provider.origin : null);
     }
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
-    Services.obs.removeObserver(this, "social:page-mark-config");
     Services.obs.removeObserver(this, "social:frameworker-error");
     Services.obs.removeObserver(this, "social:provider-set");
     Services.obs.removeObserver(this, "social:providers-changed");
     Services.obs.removeObserver(this, "social:provider-reload");
     Services.obs.removeObserver(this, "social:provider-installed");
     Services.obs.removeObserver(this, "social:provider-uninstalled");
     Services.obs.removeObserver(this, "social:provider-enabled");
     Services.obs.removeObserver(this, "social:provider-disabled");
@@ -80,25 +94,29 @@ SocialUI = {
   },
 
   observe: function SocialUI_observe(subject, topic, data) {
     // Exceptions here sometimes don't get reported properly, report them
     // manually :(
     try {
       switch (topic) {
         case "social:provider-installed":
+          SocialMarks.setPosition(data);
           SocialStatus.setPosition(data);
           break;
         case "social:provider-uninstalled":
+          SocialMarks.removePosition(data);
           SocialStatus.removePosition(data);
           break;
         case "social:provider-enabled":
+          SocialMarks.populateToolbarPalette();
           SocialStatus.populateToolbarPalette();
           break;
         case "social:provider-disabled":
+          SocialMarks.removeProvider(data);
           SocialStatus.removeProvider(data);
           break;
         case "social:provider-reload":
           // if the reloaded provider is our current provider, fall through
           // to social:provider-set so the ui will be reset
           if (!Social.provider || Social.provider.origin != data)
             return;
           // be sure to unload the sidebar as it will not reload if the origin
@@ -111,50 +129,46 @@ SocialUI = {
           // which depends on it.
           this._updateActiveUI();
           this._updateMenuItems();
 
           SocialFlyout.unload();
           SocialChatBar.update();
           SocialShare.update();
           SocialSidebar.update();
-          SocialMark.update();
           SocialToolbar.update();
           SocialStatus.populateToolbarPalette();
+          SocialMarks.populateToolbarPalette();
           SocialMenu.populate();
           break;
         case "social:providers-changed":
           // the list of providers changed - this may impact the "active" UI.
           this._updateActiveUI();
           // and the multi-provider menu
           SocialToolbar.populateProviderMenus();
           SocialShare.populateProviderMenu();
           SocialStatus.populateToolbarPalette();
+          SocialMarks.populateToolbarPalette();
           break;
 
         // Provider-specific notifications
         case "social:ambient-notification-changed":
           SocialStatus.updateNotification(data);
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateButton();
             SocialMenu.populate();
           }
           break;
         case "social:profile-changed":
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateProvider();
-            SocialMark.update();
+            SocialMarks.update();
             SocialChatBar.update();
           }
           break;
-        case "social:page-mark-config":
-          if (this._matchesCurrentProvider(data)) {
-            SocialMark.updateMarkState();
-          }
-          break;
         case "social:frameworker-error":
           if (this.enabled && Social.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage();
           }
           break;
 
         case "nsPref:changed":
           if (data == "social.sidebar.open") {
@@ -368,16 +382,24 @@ SocialUI = {
 
   get enabled() {
     // Returns whether social is enabled *for this window*.
     if (this._chromeless || PrivateBrowsingUtils.isWindowPrivate(window))
       return false;
     return !!Social.provider;
   },
 
+  // called on tab/urlbar/location changes and after customization. Update
+  // anything that is tab specific.
+  updateState: function() {
+    if (!this.enabled)
+      return;
+    SocialMarks.update();
+    SocialShare.update();
+  }
 }
 
 SocialChatBar = {
   get chatbar() {
     return document.getElementById("pinnedchats");
   },
   // Whether the chatbar is available for this window.  Note that in full-screen
   // mode chats are available, but not shown.
@@ -408,73 +430,16 @@ SocialChatBar = {
     }
     command.setAttribute("disabled", command.hidden ? "true" : "false");
   },
   focus: function SocialChatBar_focus() {
     this.chatbar.focus();
   }
 }
 
-function sizeSocialPanelToContent(panel, iframe) {
-  // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
-  let doc = iframe.contentDocument;
-  if (!doc || !doc.body) {
-    return;
-  }
-  // We need an element to use for sizing our panel.  See if the body defines
-  // an id for that element, otherwise use the body itself.
-  let body = doc.body;
-  let bodyId = body.getAttribute("contentid");
-  if (bodyId) {
-    body = doc.getElementById(bodyId) || doc.body;
-  }
-  // offsetHeight/Width don't include margins, so account for that.
-  let cs = doc.defaultView.getComputedStyle(body);
-  let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
-  let height = Math.max(computedHeight, PANEL_MIN_HEIGHT);
-  let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
-  let width = Math.max(computedWidth, PANEL_MIN_WIDTH);
-  iframe.style.width = width + "px";
-  iframe.style.height = height + "px";
-  // since we do not use panel.sizeTo, we need to adjust the arrow ourselves
-  if (panel.state == "open")
-    panel.adjustArrowPosition();
-}
-
-function DynamicResizeWatcher() {
-  this._mutationObserver = null;
-}
-
-DynamicResizeWatcher.prototype = {
-  start: function DynamicResizeWatcher_start(panel, iframe) {
-    this.stop(); // just in case...
-    let doc = iframe.contentDocument;
-    this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
-      sizeSocialPanelToContent(panel, iframe);
-    });
-    // Observe anything that causes the size to change.
-    let config = {attributes: true, characterData: true, childList: true, subtree: true};
-    this._mutationObserver.observe(doc, config);
-    // and since this may be setup after the load event has fired we do an
-    // initial resize now.
-    sizeSocialPanelToContent(panel, iframe);
-  },
-  stop: function DynamicResizeWatcher_stop() {
-    if (this._mutationObserver) {
-      try {
-        this._mutationObserver.disconnect();
-      } catch (ex) {
-        // may get "TypeError: can't access dead object" which seems strange,
-        // but doesn't seem to indicate a real problem, so ignore it...
-      }
-      this._mutationObserver = null;
-    }
-  }
-}
-
 SocialFlyout = {
   get panel() {
     return document.getElementById("social-flyout-panel");
   },
 
   get iframe() {
     if (!this.panel.firstChild)
       this._createFrame();
@@ -770,17 +735,17 @@ SocialShare = {
         // overwrite data retreived from page with data given to us as a param
         for (let p in graphData) {
           pageData[p] = graphData[p];
         }
       }
     }
     this.currentShare = pageData;
 
-    let shareEndpoint = this._generateShareEndpointURL(provider.shareURL, pageData);
+    let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
 
     this._dynamicResizer = new DynamicResizeWatcher();
     // if we've already loaded this provider/page share endpoint, we don't want
     // to add another load event listener.
     let reload = true;
     let endpointMatch = shareEndpoint == iframe.getAttribute("src");
     let docLoaded = iframe.contentDocument && iframe.contentDocument.readyState == "complete";
     if (endpointMatch && docLoaded) {
@@ -815,129 +780,16 @@ SocialShare = {
     iframe.setAttribute("src", shareEndpoint);
 
     let navBar = document.getElementById("nav-bar");
     let anchor = navBar.getAttribute("mode") == "text" ?
                    document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-text") :
                    document.getAnonymousElementByAttribute(this.shareButton, "class", "toolbarbutton-icon");
     this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
     Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
-  },
-
-  _generateShareEndpointURL: function(shareURL, pageData) {
-    // support for existing share endpoints by supporting their querystring
-    // arguments. parse the query string template and do replacements where
-    // necessary the query names may be different than ours, so we could see
-    // u=%{url} or url=%{url}
-    let [shareEndpoint, queryString] = shareURL.split("?");
-    let query = {};
-    if (queryString) {
-      queryString.split('&').forEach(function (val) {
-        let [name, value] = val.split('=');
-        let p = /%\{(.+)\}/.exec(value);
-        if (!p) {
-          // preserve non-template query vars
-          query[name] = value;
-        } else if (pageData[p[1]]) {
-          query[name] = pageData[p[1]];
-        } else if (p[1] == "body") {
-          // build a body for emailers
-          let body = "";
-          if (pageData.title)
-            body += pageData.title + "\n\n";
-          if (pageData.description)
-            body += pageData.description + "\n\n";
-          if (pageData.text)
-            body += pageData.text + "\n\n";
-          body += pageData.url;
-          query["body"] = body;
-        }
-      });
-    }
-    var str = [];
-    for (let p in query)
-       str.push(p + "=" + encodeURIComponent(query[p]));
-    if (str.length)
-      shareEndpoint = shareEndpoint + "?" + str.join("&");
-    return shareEndpoint;
-  }
-};
-
-SocialMark = {
-  get button() {
-    return document.getElementById("social-mark-button");
-  },
-
-  canMarkPage: function SSB_canMarkPage(aURI) {
-    // We only allow sharing of http or https
-    return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
-  },
-
-  // Called when the Social.provider changes
-  update: function SSB_updateButtonState() {
-    let markButton = this.button;
-    // always show button if provider supports marks
-    markButton.hidden = !SocialUI.enabled || Social.provider.pageMarkInfo == null;
-    markButton.disabled = markButton.hidden || !this.canMarkPage(gBrowser.currentURI);
-
-    // also update the relevent command's disabled state so the keyboard
-    // shortcut only works when available.
-    let cmd = document.getElementById("Social:TogglePageMark");
-    cmd.setAttribute("disabled", markButton.disabled ? "true" : "false");
-  },
-
-  togglePageMark: function(aCallback) {
-    if (this.button.disabled)
-      return;
-    this.toggleURIMark(gBrowser.currentURI, aCallback)
-  },
-
-  toggleURIMark: function(aURI, aCallback) {
-    let update = function(marked) {
-      this._updateMarkState(marked);
-      if (aCallback)
-        aCallback(marked);
-    }.bind(this);
-    Social.isURIMarked(aURI, function(marked) {
-      if (marked) {
-        Social.unmarkURI(aURI, update);
-      } else {
-        Social.markURI(aURI, update);
-      }
-    });
-  },
-
-  updateMarkState: function SSB_updateMarkState() {
-    this.update();
-    if (!this.button.hidden)
-      Social.isURIMarked(gBrowser.currentURI, this._updateMarkState.bind(this));
-  },
-
-  _updateMarkState: function(currentPageMarked) {
-    // callback for isURIMarked
-    let markButton = this.button;
-    let pageMarkInfo = SocialUI.enabled ? Social.provider.pageMarkInfo : null;
-
-    // Update the mark button, if present
-    if (!markButton || markButton.hidden || !pageMarkInfo)
-      return;
-
-    let imageURL;
-    if (!markButton.disabled && currentPageMarked) {
-      markButton.setAttribute("marked", "true");
-      markButton.setAttribute("label", pageMarkInfo.messages.markedLabel);
-      markButton.setAttribute("tooltiptext", pageMarkInfo.messages.markedTooltip);
-      imageURL = pageMarkInfo.images.marked;
-    } else {
-      markButton.removeAttribute("marked");
-      markButton.setAttribute("label", pageMarkInfo.messages.unmarkedLabel);
-      markButton.setAttribute("tooltiptext", pageMarkInfo.messages.unmarkedTooltip);
-      imageURL = pageMarkInfo.images.unmarked;
-    }
-    markButton.style.listStyleImage = "url(" + imageURL + ")";
   }
 };
 
 SocialMenu = {
   populate: function SocialMenu_populate() {
     let submenu = document.getElementById("menu_social-statusarea-popup");
     let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
     while (ambientMenuItems.length)
@@ -1016,23 +868,21 @@ SocialToolbar = {
 
     let parent = document.getElementById("social-notification-panel");
     while (parent.hasChildNodes()) {
       let frame = parent.firstChild;
       SharedFrame.forgetGroup(frame.id);
       parent.removeChild(frame);
     }
 
-    let tbi = document.getElementById("social-toolbar-item");
+    let tbi = document.getElementById("social-provider-button");
     if (tbi) {
-      // SocialMark is the last button allways
-      let next = SocialMark.button.previousSibling;
-      while (next != this.button) {
-        tbi.removeChild(next);
-        next = SocialMark.button.previousSibling;
+      // buttons after social-provider-button are ambient icons
+      while (tbi.nextSibling) {
+        tbi.parentNode.removeChild(tbi.nextSibling);
       }
     }
   },
 
   updateProfile: function SocialToolbar_updateProfile() {
     // Profile may not have been initialized yet, since it depends on a worker
     // response. In that case we'll be called again when it's available, via
     // social:profile-changed
@@ -1178,17 +1028,17 @@ SocialToolbar = {
       let ariaLabel = icon.label;
       // if there is a badge value, we must use a localizable string to insert it.
       if (badge)
         ariaLabel = gNavigatorBundle.getFormattedString("social.aria.toolbarButtonBadgeText",
                                                         [ariaLabel, badge]);
       toolbarButton.setAttribute("aria-label", ariaLabel);
     }
     let socialToolbarItem = document.getElementById("social-toolbar-item");
-    socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
+    socialToolbarItem.appendChild(toolbarButtons);
 
     for (let frame of createdFrames) {
       if (frame.socialErrorListener)
         frame.socialErrorListener.remove();
       if (frame.docShell) {
         frame.docShell.isActive = false;
         Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
       }
@@ -1532,17 +1382,17 @@ ToolbarHelper.prototype = {
         persistedById[id] = pent;
     }
 
     // create any buttons that do not exist yet if they have been persisted
     // as a part of the UI (otherwise they belong in the palette).
     for (let provider of Social.providers) {
       let id = this.idFromOrgin(provider.origin);
       if (this._getExistingButton(id))
-        return;
+        continue;
       let button = this._createButton(provider);
       if (button && persistedById.hasOwnProperty(id)) {
         let parent = persistedById[id];
         let pset = persistedById[id].getAttribute("currentset").split(',');
         let pi = pset.indexOf(id) + 1;
         let next = document.getElementById(pset[pi]);
         parent.insertItem(id, next, null, false);
       }
@@ -1766,9 +1616,135 @@ SocialStatus = {
                                              encodeURIComponent(src),
                                              null, null, null, null);
     let panel = aNotificationFrame.parentNode;
     sizeSocialPanelToContent(panel, aNotificationFrame);
   },
 
 };
 
+
+/**
+ * SocialMarks
+ *
+ * Handles updates to toolbox and signals all buttons to update when necessary.
+ */
+SocialMarks = {
+  update: function() {
+    // signal each button to update itself
+    let currentButtons = document.querySelectorAll('toolbarbutton[type="socialmark"]');
+    for (let elt of currentButtons)
+      elt.update();
+  },
+
+  getProviders: function() {
+    // only rely on providers that the user has placed in the UI somewhere. This
+    // also means that populateToolbarPalette must be called prior to using this
+    // method, otherwise you get a big fat zero. For our use case with context
+    // menu's, this is ok.
+    let tbh = this._toolbarHelper;
+    return [p for (p of Social.providers) if (p.markURL &&
+                                              document.getElementById(tbh.idFromOrgin(p.origin)))];
+  },
+
+  populateContextMenu: function() {
+    // only show a selection if enabled and there is more than one
+    let providers = this.getProviders();
+
+    // remove all previous entries by class
+    let menus = [m for (m of document.getElementsByClassName("context-socialmarks"))];
+    [m.parentNode.removeChild(m) for (m of menus)];
+
+    let contextMenus = [
+      {
+        type: "link",
+        id: "context-marklinkMenu",
+        label: "social.marklink.label"
+      },
+      {
+        type: "page",
+        id: "context-markpageMenu",
+        label: "social.markpage.label"
+      }
+    ];
+    for (let cfg of contextMenus) {
+      this._populateContextPopup(cfg, providers);
+    }
+  },
+
+  MENU_LIMIT: 3, // adjustable for testing
+  _populateContextPopup: function(menuInfo, providers) {
+    let menu = document.getElementById(menuInfo.id);
+    let popup = menu.firstChild;
+    for (let provider of providers) {
+      // We show up to MENU_LIMIT providers as single menuitems's at the top
+      // level of the context menu, if we have more than that, dump them *all*
+      // into the menu popup.
+      let mi = document.createElement("menuitem");
+      mi.setAttribute("oncommand", "gContextMenu.markLink(this.getAttribute('origin'));");
+      mi.setAttribute("origin", provider.origin);
+      mi.setAttribute("image", provider.iconURL);
+      if (providers.length <= this.MENU_LIMIT) {
+        // an extra class to make enable/disable easy
+        mi.setAttribute("class", "menuitem-iconic context-socialmarks context-mark"+menuInfo.type);
+        let menuLabel = gNavigatorBundle.getFormattedString(menuInfo.label, [provider.name]);
+        mi.setAttribute("label", menuLabel);
+        menu.parentNode.insertBefore(mi, menu);
+      } else {
+        mi.setAttribute("class", "menuitem-iconic context-socialmarks");
+        mi.setAttribute("label", provider.name);
+        popup.appendChild(mi);
+      }
+    }
+  },
+
+  populateToolbarPalette: function() {
+    this._toolbarHelper.populatePalette();
+    this.populateContextMenu();
+  },
+
+  setPosition: function(origin) {
+    // this is called during install, before the provider is enabled so we have
+    // to use the manifest rather than the provider instance as we do elsewhere.
+    let manifest = Social.getManifestByOrigin(origin);
+    if (!manifest.markURL)
+      return;
+    let tbh = this._toolbarHelper;
+    tbh.setPersistentPosition(tbh.idFromOrgin(origin));
+  },
+
+  removePosition: function(origin) {
+    let tbh = this._toolbarHelper;
+    tbh.removePersistence(tbh.idFromOrgin(origin));
+  },
+
+  removeProvider: function(origin) {
+    this._toolbarHelper.removeProviderButton(origin);
+  },
+
+  get _toolbarHelper() {
+    delete this._toolbarHelper;
+    this._toolbarHelper = new ToolbarHelper("social-mark-button", this._createButton.bind(this));
+    return this._toolbarHelper;
+  },
+
+  _createButton: function(provider) {
+    if (!provider.markURL)
+      return null;
+    let palette = document.getElementById("navigator-toolbox").palette;
+    let button = document.createElement("toolbarbutton");
+    button.setAttribute("type", "socialmark");
+    button.setAttribute("class", "toolbarbutton-1 social-mark-button");
+    button.style.listStyleImage = "url(" + provider.iconURL + ")";
+    button.setAttribute("origin", provider.origin);
+    button.setAttribute("id", this._toolbarHelper.idFromOrgin(provider.origin));
+    palette.appendChild(button);
+    return button
+  },
+
+  markLink: function(aOrigin, aUrl) {
+    // find the button for this provider, and open it
+    let id = this._toolbarHelper.idFromOrgin(aOrigin);
+    document.getElementById(id).markLink(aUrl);
+  }
+};
+
 })();
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -651,16 +651,24 @@ html|*#gcli-output-frame,
 .toolbarbutton-badge[badge]:not([badge=""])::after {
   content: attr(badge);
 }
 
 toolbarbutton[type="badged"] {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#toolbarbutton-badged");
 }
 
+toolbarbutton[type="socialmark"] {
+  -moz-binding: url("chrome://browser/content/socialmarks.xml#toolbarbutton-marks");
+}
+toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
+  width: 16px;
+  height: 16px;
+}
+
 /* Note the chatbox 'width' values are duplicated in socialchat.xml */
 chatbox {
   -moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
   transition: height 150ms ease-out, width 150ms ease-out;
   height: 285px;
   width: 260px; /* CHAT_WIDTH_OPEN in socialchat.xml */
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3440,18 +3440,17 @@ function BrowserToolboxCustomizeDone(aTo
   UpdateUrlbarSearchSplitterState();
   setUrlAndSearchBarWidthForConditionalForwardButton();
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
     BookmarkingUI.updateStarState();
-    SocialMark.updateMarkState();
-    SocialShare.update();
+    SocialUI.updateState();
   }
 
   TabsInTitlebar.allowedBy("customizing-toolbars", true);
 
   // Re-enable parts of the UI we disabled during the dialog
   var menubar = document.getElementById("main-menubar");
   for (let childNode of menubar.childNodes)
     childNode.setAttribute("disabled", false);
@@ -3909,20 +3908,17 @@ var XULBrowserWindow = {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         // Update starring UI
         BookmarkingUI.updateStarState();
-        if (SocialUI.enabled) {
-          SocialMark.updateMarkState();
-          SocialShare.update();
-        }
+        SocialUI.updateState();
       }
 
       // Show or hide browser chrome based on the whitelist
       if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
       } else {
         if (SessionStore.getTabValue(gBrowser.selectedTab, "appOrigin"))
           document.documentElement.setAttribute("disablechrome", "true");
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -739,21 +739,16 @@
             <menuseparator class="social-provider-menu" hidden="true"/>
             <menuitem class="social-addons-menuitem" command="Social:Addons"
                       label="&social.addons.label;"/>
             <menuitem label="&social.learnMore.label;"
                       accesskey="&social.learnMore.accesskey;"
                       oncommand="SocialUI.showLearnMore();"/>
           </menupopup>
         </toolbarbutton>
-        <toolbarbutton id="social-mark-button"
-                       class="toolbarbutton-1"
-                       hidden="true"
-                       disabled="true"
-                       command="Social:TogglePageMark"/>
       </toolbaritem>
 
       <hbox id="window-controls" hidden="true" pack="end">
         <toolbarbutton id="minimize-button"
                        tooltiptext="&fullScreenMinimize.tooltip;"
                        oncommand="window.minimize();"/>
 
         <toolbarbutton id="restore-button"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -303,42 +303,34 @@ nsContextMenu.prototype = {
     }
 
     // BiDi UI
     this.showItem("context-sep-bidi", top.gBidiUI);
     this.showItem("context-bidi-text-direction-toggle",
                   this.onTextInput && top.gBidiUI);
     this.showItem("context-bidi-page-direction-toggle",
                   !this.onTextInput && top.gBidiUI);
-    
-    // SocialMarks
-    let marksEnabled = SocialUI.enabled && Social.provider.pageMarkInfo;
-    let enablePageMark = marksEnabled && !(this.isContentSelected ||
-                            this.onTextInput || this.onLink || this.onImage ||
-                            this.onVideo || this.onAudio || this.onSocial);
-    let enableLinkMark = marksEnabled && ((this.onLink && !this.onMailtoLink &&
-                                           !this.onSocial) || this.onPlainTextLink);
-    if (enablePageMark) {
-      Social.isURIMarked(gBrowser.currentURI, function(marked) {
-        let label = marked ? "social.unmarkpage.label" : "social.markpage.label";
-        let provider = Social.provider || Social.defaultProvider;
-        let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
-        this.setItemAttr("context-markpage", "label", menuLabel);
-      }.bind(this));
-    }
-    this.showItem("context-markpage", enablePageMark);
-    if (enableLinkMark) {
-      Social.isURIMarked(this.linkURI, function(marked) {
-        let label = marked ? "social.unmarklink.label" : "social.marklink.label";
-        let provider = Social.provider || Social.defaultProvider;
-        let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
-        this.setItemAttr("context-marklink", "label", menuLabel);
-      }.bind(this));
-    }
-    this.showItem("context-marklink", enableLinkMark);
+
+    // SocialMarks. Marks does not work with text selections, only links. If
+    // there is more than MENU_LIMIT providers, we show a submenu for them,
+    // otherwise we have a menuitem per provider (added in SocialMarks class).
+    let markProviders = SocialMarks.getProviders();
+    let enablePageMarks = markProviders.length > 0 && !(this.onLink || this.onImage
+                            || this.onVideo || this.onAudio);
+    this.showItem("context-markpageMenu", enablePageMarks && markProviders.length > SocialMarks.MENU_LIMIT);
+    let enablePageMarkItems = enablePageMarks && markProviders.length <= SocialMarks.MENU_LIMIT;
+    let linkmenus = document.getElementsByClassName("context-markpage");
+    [m.hidden = !enablePageMarkItems for (m of linkmenus)];
+
+    let enableLinkMarks = markProviders.length > 0 &&
+                            ((this.onLink && !this.onMailtoLink) || this.onPlainTextLink);
+    this.showItem("context-marklinkMenu", enableLinkMarks && markProviders.length > SocialMarks.MENU_LIMIT);
+    let enableLinkMarkItems = enableLinkMarks && markProviders.length <= SocialMarks.MENU_LIMIT;
+    linkmenus = document.getElementsByClassName("context-marklink");
+    [m.hidden = !enableLinkMarkItems for (m of linkmenus)];
 
     // SocialShare
     let shareButton = SocialShare.shareButton;
     let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
     let pageShare = shareEnabled && !(this.isContentSelected ||
                             this.onTextInput || this.onLink || this.onImage ||
                             this.onVideo || this.onAudio);
     this.showItem("context-sharepage", pageShare);
@@ -1592,22 +1584,20 @@ nsContextMenu.prototype = {
     }
     else {
       PlacesUIUtils.showBookmarkDialog({ action: "edit"
                                        , type: "bookmark"
                                        , itemId: itemId
                                        }, window.top);
     }
   },
-
-  markLink: function CM_markLink() {
-    // send link to social
-    SocialMark.toggleURIMark(this.linkURI);
+  markLink: function CM_markLink(origin) {
+    // send link to social, if it is the page url linkURI will be null
+    SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null);
   },
-
   shareLink: function CM_shareLink() {
     SocialShare.sharePage(null, { url: this.linkURI.spec });
   },
 
   shareImage: function CM_shareImage() {
     SocialShare.sharePage(null, { url: this.imageURL, previews: [ this.mediaURL ] });
   },
 
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -24,16 +24,22 @@
                accesskey="&getUserMedia.selectMicrophone.accesskey;"
                control="webRTC-selectMicrophone-menulist"/>
         <menulist id="webRTC-selectMicrophone-menulist">
           <menupopup id="webRTC-selectMicrophone-menupopup"/>
         </menulist>
       </popupnotificationcontent>
     </popupnotification>
 
+    <popupnotification id="webapps-install-progress-notification" hidden="true">
+      <popupnotificationcontent id="webapps-install-progress-content" orient="vertical" align="start">
+        <separator class="thin"/>
+      </popupnotificationcontent>
+    </popupnotification>
+
     <popupnotification id="geolocation-notification" hidden="true">
       <popupnotificationcontent orient="vertical" align="start">
         <separator class="thin"/>
         <label id="geolocation-learnmore-link" class="text-link"/>
       </popupnotificationcontent>
     </popupnotification>
 
     <popupnotification id="servicesInstall-notification" hidden="true">
new file mode 100644
--- /dev/null
+++ b/browser/base/content/socialmarks.xml
@@ -0,0 +1,236 @@
+<?xml version="1.0"?>
+
+<bindings id="socialMarkBindings"
+    xmlns="http://www.mozilla.org/xbl"
+    xmlns:xbl="http://www.mozilla.org/xbl"
+    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+
+  <binding id="toolbarbutton-marks" display="xul:button"
+           extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
+    <content disabled="true">
+      <xul:panel anonid="panel" hidden="true" type="arrow" class="social-panel">
+        <xul:iframe type="content" flex="1" tooltip="aHTMLTooltip"
+                    class="social-panel-frame" context="contentAreaContextMenu"
+                    xbl:inherits="src,origin"/>
+      </xul:panel>
+      <xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
+      <xul:label class="toolbarbutton-text" crop="right" flex="1"
+                 xbl:inherits="value=label,accesskey,crop"/>
+    </content>
+    <implementation implements="nsIDOMEventListener, nsIObserver">
+      <field name="panel" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "panel");
+      </field>
+
+      <field name="content" readonly="true">
+        this.panel.firstChild;
+      </field>
+
+      <property name="contentWindow">
+        <getter>
+          return this.content.contentWindow;
+        </getter>
+      </property>
+
+      <property name="contentDocument">
+        <getter>
+          return this.content.contentDocument;
+        </getter>
+      </property>
+
+      <property name="provider">
+        <getter>
+          return Social._getProviderFromOrigin(this.getAttribute("origin"));
+        </getter>
+      </property>
+
+      <property name="isMarked">
+        <setter>
+          this._isMarked = val;
+          let provider = this.provider;
+          // we cannot size the image when we apply it via listStyleImage, so
+          // use the toolbar image
+          if (val)
+            this.setAttribute("image", provider.unmarkedIcon || provider.iconURL);
+          else
+            this.setAttribute("image", provider.markedIcon || provider.iconURL);
+        </setter>
+        <getter>
+          return this._isMarked;
+        </getter>
+      </property>
+
+      <method name="update">
+        <body><![CDATA[
+        // update the button for use with the current tab
+        let provider = this.provider;
+        if (this._dynamicResizer) {
+          this._dynamicResizer.stop();
+          this._dynamicResizer = null;
+        }
+        this.setAttribute("src", "about:blank");
+
+        // do we have a savable page loaded?
+        let aURI = gBrowser.currentURI;
+        this.disabled = !aURI || !(aURI.schemeIs('http') || aURI.schemeIs('https'));
+
+        if (this.disabled) {
+          this.isMarked = false;
+        } else {
+          Social.isURIMarked(provider.origin, aURI, (isMarked) => {
+            this.isMarked = isMarked;
+          });
+        }
+        this.setAttribute("label", provider.name);
+        this.setAttribute("tooltiptext", provider.name);
+        this.setAttribute("origin", provider.origin);
+        this.panel.hidePopup();
+        this.panel.hidden = true;
+        this.pageData = null;
+        ]]></body>
+      </method>
+
+      <method name="loadPanel">
+        <parameter name="pageData"/>
+        <body><![CDATA[
+        let provider = this.provider;
+        this.panel.hidden = false;
+        let URLTemplate = provider.markURL;
+        this.pageData = pageData || OpenGraphBuilder.getData(gBrowser);
+        let endpoint = OpenGraphBuilder.generateEndpointURL(URLTemplate, this.pageData);
+
+        // setup listeners
+        this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
+          if (event.target != this.contentDocument)
+            return;
+          this._loading = false;
+          this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
+          // add our resizer after the dom is ready
+          let DynamicResizeWatcher = Cu.import("resource:///modules/Social.jsm", {}).DynamicResizeWatcher;
+          this._dynamicResizer = new DynamicResizeWatcher();
+          this._dynamicResizer.start(this.panel, this.content);
+          // send the opengraph data
+          let evt = this.contentDocument.createEvent("CustomEvent");
+          evt.initCustomEvent("OpenGraphData", true, true, JSON.stringify(this.pageData));
+          this.contentDocument.documentElement.dispatchEvent(evt);
+
+          let contentWindow = this.contentWindow;
+          let markUpdate = function(event) {
+            // update the annotation based on this event, then update the
+            // icon as well
+            this.isMarked = JSON.parse(event.detail).marked;
+            let uri = Services.io.newURI(this.pageData.url, null, null);
+            if (this.isMarked) {
+              Social.markURI(provider.origin, uri);
+            } else {
+              Social.unmarkURI(provider.origin, uri, () => {
+                this.update();
+              });
+            }
+          }.bind(this);
+          contentWindow.addEventListener("socialMarkUpdate", markUpdate);
+          contentWindow.addEventListener("unload", function unload() {
+            contentWindow.removeEventListener("unload", unload);
+            contentWindow.removeEventListener("socialMarkUpdate", markUpdate);
+          });
+        }, true);
+        this._loading = true;
+        this.setAttribute("src", endpoint);
+        ]]></body>
+      </method>
+
+      <method name="markCurrentPage">
+        <parameter name="aOpenPanel"/>
+        <body><![CDATA[
+        // we always set the src on click if it has not been set for this tab,
+        // but we only want to open the panel if it was previously annotated.
+        let openPanel = this.isMarked || aOpenPanel;
+        let src = this.getAttribute("src");
+        if (!src || src == "about:blank") {
+          this.loadPanel();
+          this.isMarked = true;
+        }
+        if (openPanel)
+          this.panel.openPopup(this, "bottomcenter topright", 0, 0, false, false);
+        ]]></body>
+      </method>
+
+      <method name="markLink">
+        <parameter name="aUrl"/>
+        <body><![CDATA[
+        if (!aUrl) {
+          this.markCurrentPage(true);
+          return;
+        }
+        // initiated form an external source, such as gContextMenu, where
+        // pageData is passed into us. In this case, we always load the iframe
+        // and show it since the url may not be the browser tab, but an image,
+        // link, etc. inside the page. We also "update" the iframe to the
+        // previous url when it is closed.
+        this.setAttribute("src", "about:blank");
+        this.loadPanel({ url: aUrl });
+        this.isMarked = true;
+        this.panel.openPopup(this, "bottomcenter topright", 0, 0, false, false);
+        this.panel.addEventListener("popuphidden", function _hidden() {
+          this.panel.removeEventListener("popuphidden", _hidden);
+          this.update();
+        }.bind(this), false);
+        ]]></body>
+      </method>
+
+      <method name="dispatchPanelEvent">
+        <parameter name="name"/>
+        <body><![CDATA[
+        let evt = this.contentDocument.createEvent("CustomEvent");
+        evt.initCustomEvent(name, true, true, {});
+        this.contentDocument.documentElement.dispatchEvent(evt);
+        ]]></body>
+      </method>
+
+    </implementation>
+    <handlers>
+      <handler event="popupshown"><![CDATA[
+        // because the panel may be preloaded, we need to size the panel when
+        // showing as well as after load
+        let sizeSocialPanelToContent = Cu.import("resource:///modules/Social.jsm", {}).sizeSocialPanelToContent;
+        if (!this._loading && this.contentDocument.readyState == "complete") {
+          this.dispatchPanelEvent("socialFrameShow");
+          sizeSocialPanelToContent(this.panel, this.content);
+        } else {
+          this.content.addEventListener("load", function panelBrowserOnload(e) {
+            this.content.removeEventListener("load", panelBrowserOnload, true);
+            this.dispatchPanelEvent("socialFrameShow");
+            sizeSocialPanelToContent(this.panel, this.content);
+          }.bind(this), true);
+        }
+      ]]></handler>
+      <handler event="popuphidden"><![CDATA[
+        this.dispatchPanelEvent("socialFrameHide");
+      ]]></handler>
+      <handler event="command"><![CDATA[
+        this.markCurrentPage();
+      ]]></handler>
+      <handler event="DOMLinkAdded"><![CDATA[
+        // much of this logic is from DOMLinkHandler in browser.js, this sets
+        // the presence icon for a chat user, we simply use favicon style
+        // updating
+        let link = event.originalTarget;
+        let rel = link.rel && link.rel.toLowerCase();
+        if (!link || !link.ownerDocument || !rel || !link.href)
+          return;
+        if (link.rel.indexOf("icon") < 0)
+          return;
+
+        let uri = DOMLinkHandler.getLinkIconURI(link);
+        if (!uri)
+          return;
+
+        // we cannot size the image when we apply it via listStyleImage, so
+        // use the toolbar image
+        this.setAttribute("image", uri.spec);
+      ]]></handler>
+    </handlers>
+  </binding>
+
+</bindings>
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -7,33 +7,35 @@ MOCHITEST_BROWSER_FILES = \
 		 blocklist.xml \
 		 browser_blocklist.js \
 		 browser_defaults.js \
 		 browser_addons.js \
 		 browser_chat_tearoff.js \
                  browser_social_activation.js \
                  browser_social_perwindowPB.js \
                  browser_social_toolbar.js \
-                 browser_social_markButton.js \
                  browser_social_sidebar.js \
                  browser_social_flyout.js \
                  browser_social_mozSocial_API.js \
                  browser_social_isVisible.js \
                  browser_social_chatwindow.js \
                  browser_social_chatwindow_resize.js \
                  browser_social_chatwindowfocus.js \
                  browser_social_multiprovider.js \
                  browser_social_multiworker.js \
                  browser_social_errorPage.js \
+                 browser_social_marks.js \
                  browser_social_status.js \
                  browser_social_window.js \
                  social_activate.html \
                  social_activate_iframe.html \
                  browser_share.js \
                  social_panel.html \
-                 social_mark_image.png \
+                 social_mark.html \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  share.html \
+                 checked.jpg \
+                 unchecked.jpg \
                  $(NULL)
--- a/browser/base/content/test/social/browser_social_errorPage.js
+++ b/browser/base/content/test/social/browser_social_errorPage.js
@@ -52,16 +52,28 @@ function openChat(url, panelCallback, lo
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
   sbrowser.addEventListener("load", function load() {
     sbrowser.removeEventListener("load", load, true);
     callback();
   }, true);
 }
 
+function ensureWorkerLoaded(provider, callback) {
+  // once the worker responds to a ping we know it must be up.
+  let port = provider.getWorkerPort();
+  port.onmessage = function(msg) {
+    if (msg.data.topic == "pong") {
+      port.close();
+      callback();
+    }
+  }
+  port.postMessage({topic: "ping"})
+}
+
 let manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
   sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
 };
 
@@ -94,19 +106,23 @@ var tests = {
           // should now be on the correct page.
           is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "is now on social sidebar page");
           next();
         });
         sbrowser.contentDocument.getElementById("btnTryAgain").click();
       });
       sbrowser.contentDocument.getElementById("btnTryAgain").click();
     });
-    // go offline then attempt to load the sidebar - it should fail.
-    goOffline();
-    Services.prefs.setBoolPref("social.sidebar.open", true);
+    // we want the worker to be fully loaded before going offline, otherwise
+    // it might fail due to going offline.
+    ensureWorkerLoaded(Social.provider, function() {
+      // go offline then attempt to load the sidebar - it should fail.
+      goOffline();
+      Services.prefs.setBoolPref("social.sidebar.open", true);
+  });
   },
 
   testFlyout: function(next) {
     let panelCallbackCount = 0;
     let panel = document.getElementById("social-flyout-panel");
     // go offline and open a flyout.
     goOffline();
     openPanel(
deleted file mode 100644
--- a/browser/base/content/test/social/browser_social_markButton.js
+++ /dev/null
@@ -1,187 +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/. */
-
-let prefName = "social.enabled",
-    gFinishCB;
-
-function test() {
-  waitForExplicitFinish();
-
-  // Need to load a http/https/ftp/ftps page for the social mark button to appear
-  let tab = gBrowser.selectedTab = gBrowser.addTab("https://test1.example.com", {skipAnimation: true});
-  tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
-    tab.linkedBrowser.removeEventListener("load", tabLoad, true);
-    executeSoon(tabLoaded);
-  }, true);
-
-  registerCleanupFunction(function() {
-    Services.prefs.clearUserPref(prefName);
-    gBrowser.removeTab(tab);
-  });
-}
-
-function tabLoaded() {
-  ok(Social, "Social module loaded");
-
-  let manifest = { // normal provider
-    name: "provider 1",
-    origin: "https://example.com",
-    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
-    iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
-  };
-  runSocialTestWithProvider(manifest, function (finishcb) {
-    gFinishCB = finishcb;
-    testInitial();
-  });
-}
-
-function testInitial(finishcb) {
-  ok(Social.provider, "Social provider is active");
-  ok(Social.provider.enabled, "Social provider is enabled");
-  let port = Social.provider.getWorkerPort();
-  ok(port, "Social provider has a port to its FrameWorker");
-  port.close();
-
-  let markButton = SocialMark.button;
-  ok(markButton, "mark button exists");
-
-  // ensure the worker initialization and handshakes are all done and we
-  // have a profile and the worker has sent a page-mark-config msg.
-  waitForCondition(function() Social.provider.pageMarkInfo != null, function() {
-    is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' attribute before mark button is clicked");
-    // Check the strings from our worker actually ended up on the button.
-    is(markButton.getAttribute("tooltiptext"), "Mark this page", "check tooltip text is correct");
-    // Check the relative URL was resolved correctly (note this image has offsets of zero...)
-    is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
-
-    // Test the mark button command handler
-    SocialMark.togglePageMark(function() {
-      is(markButton.hasAttribute("marked"), true, "mark button should have 'marked' attribute after mark button is clicked");
-      is(markButton.getAttribute("tooltiptext"), "Unmark this page", "check tooltip text is correct");
-      // Check the URL and offsets were applied correctly
-      is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
-      SocialMark.togglePageMark(function() {
-        is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-        executeSoon(function() {
-          testStillMarkedIn2Tabs();
-        });
-      });
-    });
-  }, "provider didn't provide page-mark-config");
-}
-
-function testStillMarkedIn2Tabs() {
-  let toMark = "http://test2.example.com";
-  let markUri = Services.io.newURI(toMark, null, null);
-  let markButton = SocialMark.button;
-  let initialTab = gBrowser.selectedTab;
-  info("initialTab has loaded " + gBrowser.currentURI.spec);
-  is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' for the initial tab");
-
-  addTab(toMark, function(tab1) {
-    addTab(toMark, function(tab2) {
-      // should start without either page being marked.
-      is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' before we've done anything");
-      Social.isURIMarked(markUri, function(marked) {
-        ok(!marked, "page is unmarked in annotations");
-        markButton.click();
-        waitForCondition(function() markButton.hasAttribute("marked"), function() {
-          Social.isURIMarked(markUri, function(marked) {
-            ok(marked, "page is marked in annotations");
-            // and switching to the first tab (with the same URL) should still reflect marked.
-            selectBrowserTab(tab1, function() {
-              is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-              // wait for tabselect
-              selectBrowserTab(initialTab, function() {
-                waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-                  gBrowser.selectedTab = tab1;
-        
-                  SocialMark.togglePageMark(function() {
-                    Social.isURIMarked(gBrowser.currentURI, function(marked) {
-                      ok(!marked, "page is unmarked in annotations");
-                      is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-                      gBrowser.removeTab(tab1);
-                      gBrowser.removeTab(tab2);
-                      executeSoon(testStillMarkedAfterReopen);
-                    });
-                  });
-                }, "button has been unmarked");
-              });
-            });
-          });
-        }, "button has been marked");
-      });
-    });
-  });
-}
-
-function testStillMarkedAfterReopen() {
-  let toMark = "http://test2.example.com";
-  let markButton = SocialMark.button;
-
-  is(markButton.hasAttribute("marked"), false, "Reopen: SocialMark button should not have 'marked' for the initial tab");
-  addTab(toMark, function(tab) {
-    SocialMark.togglePageMark(function() {
-      is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-      gBrowser.removeTab(tab);
-      // should be on the initial unmarked tab now.
-      waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-        // now open the same URL - should be back to Marked.
-        addTab(toMark, function(tab) {
-          is(markButton.hasAttribute("marked"), true, "New tab to previously marked URL should reflect marked state");
-          SocialMark.togglePageMark(function() {
-            gBrowser.removeTab(tab);
-            executeSoon(testOnlyMarkCertainUrlsTabSwitch);
-          });
-        });
-      }, "button is now unmarked");
-    });
-  });
-}
-
-function testOnlyMarkCertainUrlsTabSwitch() {
-  let toMark = "http://test2.example.com";
-  let notSharable = "about:blank";
-  let markButton = SocialMark.button;
-  addTab(toMark, function(tab) {
-    ok(!markButton.hidden, "SocialMark button not hidden for http url");
-    addTab(notSharable, function(tab2) {
-      ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      selectBrowserTab(tab, function() {
-        ok(!markButton.disabled, "SocialMark button re-shown when switching back to http: url");
-        selectBrowserTab(tab2, function() {
-          ok(markButton.disabled, "SocialMark button re-hidden when switching back to about:blank");
-          gBrowser.removeTab(tab);
-          gBrowser.removeTab(tab2);
-          executeSoon(testOnlyMarkCertainUrlsSameTab);
-        });
-      });
-    });
-  });
-}
-
-function testOnlyMarkCertainUrlsSameTab() {
-  let toMark = "http://test2.example.com";
-  let notSharable = "about:blank";
-  let markButton = SocialMark.button;
-  addTab(toMark, function(tab) {
-    ok(!markButton.disabled, "SocialMark button not disabled for http url");
-    loadIntoTab(tab, notSharable, function() {
-      ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      loadIntoTab(tab, toMark, function() {
-        ok(!markButton.disabled, "SocialMark button re-enabled http url");
-        gBrowser.removeTab(tab);
-        executeSoon(testDisable);
-      });
-    });
-  });
-}
-
-function testDisable() {
-  let markButton = SocialMark.button;
-  Services.prefs.setBoolPref(prefName, false);
-  is(markButton.hidden, true, "SocialMark button should be hidden when pref is disabled");
-  gFinishCB();
-}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_marks.js
@@ -0,0 +1,340 @@
+/* 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/. */
+
+let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
+
+let manifest = { // builtin provider
+  name: "provider example.com",
+  origin: "https://example.com",
+  sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+  workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+  iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
+};
+let manifest2 = { // used for testing install
+  name: "provider test1",
+  origin: "https://test1.example.com",
+  workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
+  markURL: "https://test1.example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
+  markedIcon: "https://test1.example.com/browser/browser/base/content/test/social/unchecked.jpg",
+  unmarkedIcon: "https://test1.example.com/browser/browser/base/content/test/social/checked.jpg",
+
+  iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png",
+  version: 1
+};
+let manifest3 = { // used for testing install
+  name: "provider test2",
+  origin: "https://test2.example.com",
+  sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html",
+  iconURL: "https://test2.example.com/browser/browser/base/content/test/moz.png",
+  version: 1
+};
+function makeMarkProvider(origin) {
+  return { // used for testing install
+    name: "mark provider " + origin,
+    origin: "https://" + origin + ".example.com",
+    workerURL: "https://" + origin + ".example.com/browser/browser/base/content/test/social/social_worker.js",
+    markURL: "https://" + origin + ".example.com/browser/browser/base/content/test/social/social_mark.html?url=%{url}",
+    markedIcon: "https://" + origin + ".example.com/browser/browser/base/content/test/social/unchecked.jpg",
+    unmarkedIcon: "https://" + origin + ".example.com/browser/browser/base/content/test/social/checked.jpg",
+
+    iconURL: "https://" + origin + ".example.com/browser/browser/base/content/test/moz.png",
+    version: 1
+  }
+}
+
+function openWindowAndWaitForInit(callback) {
+  let topic = "browser-delayed-startup-finished";
+  let w = OpenBrowserWindow();
+  Services.obs.addObserver(function providerSet(subject, topic, data) {
+    Services.obs.removeObserver(providerSet, topic);
+    executeSoon(() => callback(w));
+  }, topic, false);
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
+  let toolbar = document.getElementById("nav-bar");
+  let currentsetAtStart = toolbar.currentSet;
+  runSocialTestWithProvider(manifest, function () {
+    runSocialTests(tests, undefined, undefined, function () {
+      Services.prefs.clearUserPref("social.remote-install.enabled");
+      // just in case the tests failed, clear these here as well
+      Services.prefs.clearUserPref("social.allowMultipleWorkers");
+      Services.prefs.clearUserPref("social.whitelist");
+
+      // This post-test test ensures that a new window maintains the same
+      // toolbar button set as when we started. That means our insert/removal of
+      // persistent id's is working correctly
+      is(currentsetAtStart, toolbar.currentSet, "toolbar currentset unchanged");
+      openWindowAndWaitForInit(function(w1) {
+        checkSocialUI(w1);
+        // Sometimes the new window adds other buttons to currentSet that are
+        // outside the scope of what we're checking. So we verify that all
+        // buttons from startup are in currentSet for a new window, and that the
+        // provider buttons are properly removed. (e.g on try, window-controls
+        // was not present in currentsetAtStart, but present on the second
+        // window)
+        let tb1 = w1.document.getElementById("nav-bar");
+        let startupSet = Set(toolbar.currentSet.split(','));
+        let newSet = Set(tb1.currentSet.split(','));
+        let intersect = Set([x for (x of startupSet) if (newSet.has(x))]);
+        let difference = Set([x for (x of newSet) if (!startupSet.has(x))]);
+        is(startupSet.size, intersect.size, "new window toolbar same as old");
+        // verify that our provider buttons are not in difference
+        let id = SocialMarks._toolbarHelper.idFromOrgin(manifest2.origin);
+        ok(!difference.has(id), "mark button not persisted at end");
+        w1.close();
+        finish();
+      });
+    });
+  });
+}
+
+var tests = {
+  testNoButtonOnInstall: function(next) {
+    // we expect the addon install dialog to appear, we need to accept the
+    // install from the dialog.
+    info("Waiting for install dialog");
+    let panel = document.getElementById("servicesInstall-notification");
+    PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
+      PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
+      info("servicesInstall-notification panel opened");
+      panel.button.click();
+    })
+
+    let id = "social-mark-button-" + manifest3.origin;
+    let toolbar = document.getElementById("nav-bar");
+    let currentset = toolbar.getAttribute("currentset").split(',');
+    ok(currentset.indexOf(id) < 0, "button is not part of currentset at start");
+
+    let activationURL = manifest3.origin + "/browser/browser/base/content/test/social/social_activate.html"
+    addTab(activationURL, function(tab) {
+      let doc = tab.linkedBrowser.contentDocument;
+      Social.installProvider(doc, manifest3, function(addonManifest) {
+        // enable the provider so we know the button would have appeared
+        SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
+          ok(provider, "provider is installed");
+          currentset = toolbar.getAttribute("currentset").split(',');
+          ok(currentset.indexOf(id) < 0, "button was not added to currentset");
+          Social.uninstallProvider(manifest3.origin, function() {
+            gBrowser.removeTab(tab);
+            next();
+          });
+        });
+      });
+    });
+  },
+
+  testButtonOnInstall: function(next) {
+    // we expect the addon install dialog to appear, we need to accept the
+    // install from the dialog.
+    info("Waiting for install dialog");
+    let panel = document.getElementById("servicesInstall-notification");
+    PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
+      PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
+      info("servicesInstall-notification panel opened");
+      panel.button.click();
+    })
+
+    let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
+    addTab(activationURL, function(tab) {
+      let doc = tab.linkedBrowser.contentDocument;
+      Social.installProvider(doc, manifest2, function(addonManifest) {
+        // at this point, we should have a button id in the currentset for our provider
+        let id = "social-mark-button-" + manifest2.origin;
+        let toolbar = document.getElementById("nav-bar");
+
+        waitForCondition(function() {
+                            let currentset = toolbar.getAttribute("currentset").split(',');
+                            return currentset.indexOf(id) >= 0;
+                         },
+                         function() {
+                           // no longer need the tab
+                           gBrowser.removeTab(tab);
+                           next();
+                         }, "mark button added to currentset");
+      });
+    });
+  },
+
+  testButtonOnEnable: function(next) {
+    // enable the provider now
+    SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
+      ok(provider, "provider is installed");
+      let id = "social-mark-button-" + manifest2.origin;
+      waitForCondition(function() { return document.getElementById(id) },
+                       function() {
+                         checkSocialUI(window);
+                         next();
+                       }, "button exists after enabling social");
+    });
+  },
+
+  testMarkPanel: function(next) {
+    // click on panel to open and wait for visibility
+    let provider = Social._getProviderFromOrigin(manifest2.origin);
+    ok(provider.enabled, "provider is enabled");
+    let id = "social-mark-button-" + provider.origin;
+    let btn = document.getElementById(id)
+    ok(btn, "got a mark button");
+    let port = provider.getWorkerPort();
+    ok(port, "got a port");
+
+    // verify markbutton is disabled when there is no browser url
+    ok(btn.disabled, "button is disabled");
+    let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
+    addTab(activationURL, function(tab) {
+      ok(!btn.disabled, "button is enabled");
+      port.onmessage = function (e) {
+        let topic = e.data.topic;
+        switch (topic) {
+          case "test-init-done":
+            ok(true, "test-init-done received");
+            ok(provider.profile.userName, "profile was set by test worker");
+            // first click marks the page, second click opens the page. We have to
+            // synthesize so the command event happens
+            EventUtils.synthesizeMouseAtCenter(btn, {});
+            // wait for the button to be marked, click to open panel
+            waitForCondition(function() btn.isMarked, function() {
+              EventUtils.synthesizeMouseAtCenter(btn, {});
+            }, "button is marked");
+            break;
+          case "got-social-panel-visibility":
+            ok(true, "got the panel message " + e.data.result);
+            if (e.data.result == "shown") {
+              // unmark the page via the button in the page
+              let doc = btn.contentDocument;
+              let unmarkBtn = doc.getElementById("unmark");
+              ok(unmarkBtn, "got the panel unmark button");
+              EventUtils.sendMouseEvent({type: "click"}, unmarkBtn, btn.contentWindow);
+            } else {
+              // page should no longer be marked
+              port.close();
+              waitForCondition(function() !btn.isMarked, function() {
+                // after closing the addons tab, verify provider is still installed
+                gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
+                  gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
+                    executeSoon(function () {
+                      ok(btn.disabled, "button is disabled");
+                      next();
+                    });
+                });
+                gBrowser.removeTab(tab);
+              }, "button unmarked");
+            }
+            break;
+        }
+      };
+      port.postMessage({topic: "test-init"});
+    });
+  },
+
+  testButtonOnDisable: function(next) {
+    // enable the provider now
+    let provider = Social._getProviderFromOrigin(manifest2.origin);
+    ok(provider, "provider is installed");
+    SocialService.removeProvider(manifest2.origin, function() {
+      let id = "social-mark-button-" + manifest2.origin;
+      waitForCondition(function() { return !document.getElementById(id) },
+                       function() {
+                         checkSocialUI(window);
+                         next();
+                       }, "button does not exist after disabling the provider");
+    });
+  },
+
+  testButtonOnUninstall: function(next) {
+    Social.uninstallProvider(manifest2.origin, function() {
+      // test that the button is no longer persisted
+      let id = "social-mark-button-" + manifest2.origin;
+      let toolbar = document.getElementById("nav-bar");
+      let currentset = toolbar.getAttribute("currentset").split(',');
+      is(currentset.indexOf(id), -1, "button no longer in currentset");
+      next();
+    });
+  },
+
+  testContextSubmenu: function(next) {
+    // install 4 providers to test that the menu's are added as submenus
+    let manifests = [
+      makeMarkProvider("sub1.test1"),
+      makeMarkProvider("sub2.test1"),
+      makeMarkProvider("sub1.test2"),
+      makeMarkProvider("sub2.test2")
+    ];
+    let installed = [];
+    let markLinkMenu = document.getElementById("context-marklinkMenu").firstChild;
+    let markPageMenu = document.getElementById("context-markpageMenu").firstChild;
+
+    function addProviders(callback) {
+      let manifest = manifests.pop();
+      if (!manifest) {
+        info("INSTALLATION FINISHED");
+        executeSoon(callback);
+        return;
+      }
+      info("INSTALLING " + manifest.origin);
+      let panel = document.getElementById("servicesInstall-notification");
+      PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
+        PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
+        info("servicesInstall-notification panel opened");
+        panel.button.click();
+      })
+
+      let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
+      let id = "social-mark-button-" + manifest.origin;
+      let toolbar = document.getElementById("nav-bar");
+      addTab(activationURL, function(tab) {
+        let doc = tab.linkedBrowser.contentDocument;
+        Social.installProvider(doc, manifest, function(addonManifest) {
+
+          waitForCondition(function() {
+            let currentset = toolbar.getAttribute("currentset").split(',');
+            return currentset.indexOf(id) >= 0;
+          },
+          function() {
+            // enable the provider so we know the button would have appeared
+            SocialService.addBuiltinProvider(manifest.origin, function(provider) {
+              waitForCondition(function() { return document.getElementById(id) },
+                               function() {
+                gBrowser.removeTab(tab);
+                installed.push(manifest.origin);
+                // checkSocialUI will properly check where the menus are located
+                checkSocialUI(window);
+                executeSoon(function() {
+                  addProviders(callback);
+                });
+              }, "button exists after enabling social");
+            });
+          }, "mark button added to currentset");
+        });
+      });
+    }
+
+    function removeProviders(callback) {
+      let origin = installed.pop();
+      if (!origin) {
+        executeSoon(callback);
+        return;
+      }
+      Social.uninstallProvider(origin, function(provider) {
+        executeSoon(function() {
+          removeProviders(callback);
+        });
+      });
+    }
+
+    addProviders(function() {
+      removeProviders(function() {
+        is(SocialMarks.getProviders().length, 0, "mark providers removed");
+        is(markLinkMenu.childNodes.length, 0, "marklink menu ok");
+        is(markPageMenu.childNodes.length, 0, "markpage menu ok");
+        checkSocialUI(window);
+        next();
+      });
+    });
+  }
+}
--- a/browser/base/content/test/social/browser_social_mozSocial_API.js
+++ b/browser/base/content/test/social/browser_social_mozSocial_API.js
@@ -30,25 +30,24 @@ var tests = {
     let gotSidebarMessage = false;
 
     function checkNext() {
       if (iconsReady && gotSidebarMessage)
         triggerIconPanel();
     }
 
     function triggerIconPanel() {
+      let pButton = document.getElementById("social-provider-button");
       waitForCondition(function() {
-        let mButton = document.getElementById("social-mark-button");
-        let pButton = document.getElementById("social-provider-button");
         // wait for a new button to be inserted inbetween the provider and mark
         // button
-        return pButton.nextSibling != mButton;
+        return !!pButton.nextSibling;
       }, function() {
         // Click the button to trigger its contentPanel
-        let statusIcon = document.getElementById("social-provider-button").nextSibling;
+        let statusIcon = pButton.nextSibling;
         EventUtils.synthesizeMouseAtCenter(statusIcon, {});
       }, "Status icon didn't become non-hidden");
     }
 
     let port = Social.provider.getWorkerPort();
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
--- a/browser/base/content/test/social/browser_social_toolbar.js
+++ b/browser/base/content/test/social/browser_social_toolbar.js
@@ -119,22 +119,21 @@ var tests = {
     Social.provider.setAmbientNotification(ambience3);
     
     try {
       Social.provider.setAmbientNotification(ambience4);
     } catch(e) {}
     let numIcons = Object.keys(Social.provider.ambientNotificationIcons).length;
     ok(numIcons == 3, "prevent adding more than 3 ambient notification icons");
 
-    let mButton = document.getElementById("social-mark-button");
     let pButton = document.getElementById("social-provider-button");
     waitForCondition(function() {
       // wait for a new button to be inserted inbetween the provider and mark
       // button
-      return pButton.nextSibling != mButton;
+      return !!pButton.nextSibling;
     }, function () {
       let statusIcon = pButton.nextSibling;
       let badge = statusIcon.getAttribute("badge");
       is(badge, "42", "status value is correct");
       // If there is a counter, the aria-label should reflect it.
       is(statusIcon.getAttribute("aria-label"), "Test Ambient 1 \u2046 (42)");
 
       ambience.counter = 0;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4cbbe18e6125b6591a6a5eb38b036e38b97b235a
GIT binary patch
literal 785
zc$@(d1Md8ZP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!qe(<TRCwBA
z{Qv(y10?_;fLL%zw7b|zDSi7iUFO#hga1tb{=Z;myLwlb^Tq$qFOz`MTYz|a#qvkn
z00M{s<N|E?r5yLaUonCI|D8Ym|KFxH|Nqz-{=aUi_y70z_y2ddnEh{$klhSY2M|Ck
z?RFMS`L8dmVfgpYhvDCUR))Vov;Y2Q5U!oTz-;frz`!Q}6kuXt?wQ8$^3_cS?xWKg
zUcUPQa^!jt0T4heY=8f7GW}%|<*1#)%D@ct(eFPD3^K9|3<5xn5AQQDNJ=v>T)V`;
z@ckRZ^-BvFRDz=zUcdbe(z^ph00a<=;QwFJOuv}<!5-hfiGcwWsCtGB4AOE849cnu
z3}3%60A0uM@#gJcqTCE@-{0M5`0?`(1JM7^Kw1C-h)LwncP%D%ZUK-p8JL(E7+BdE
z7#JBD7}(et7&ti@7(RUhdg(92b9v?OBEq~3EFAnOfejEqOftW|m@*es@H0Gq0Mx+2
zz`(%{F^G*7=nWx=K|pW(VqxVM6BA*0ar-1l-&L3a00G1z_xp=3BNsO}!_k8bK({e4
z7#K4!D5)?oh=?)#J$B&Vx9wYhvj6_c`kr5qMa-Ik;pLkz@Bjn>fB<6R`S)M^|L<SS
z48Fk(455(>|BcNUeogIRcpKyM|I+$37dS1glimmkFD}})gW=zwzYMS5d<5y+4Kn~B
zfLOK&$X(K!+@vG^?F;8Wpujsuj^CH~#m<J_|7;};RkSEl?sb-*SRxY(GsD|=pFjb4
z2xb640I`G|zm^K5Q(*$4K&+0LFx^B&1+b=AfB<45GRgZ(iVI?u00<yr4N&Ld=45#B
z>OBKAAHV@X05Or0^WMD&r@*7gVgLa|tO3(EZadC!`|i`}AU3iDK!5=N56SIJ3p?Tv
P00000NkvXXu0mjf%wuI)
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -230,41 +230,72 @@ function checkSocialUI(win) {
     _is(!!a, !!b, msg);
   }
   isbool(win.SocialSidebar.canShow, enabled, "social sidebar active?");
   if (enabled)
     isbool(win.SocialSidebar.opened, enabled, "social sidebar open?");
   isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?");
   isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?");
 
-  let markVisible = enabled && provider.pageMarkInfo;
-  let canMark = markVisible && win.SocialMark.canMarkPage(win.gBrowser.currentURI);
-  isbool(!win.SocialMark.button.hidden, markVisible, "SocialMark button visible?");
-  isbool(!win.SocialMark.button.disabled, canMark, "SocialMark button enabled?");
   isbool(!doc.getElementById("social-toolbar-item").hidden, active, "toolbar items visible?");
   if (active) {
     if (!enabled) {
       _ok(!win.SocialToolbar.button.style.listStyleImage, "toolbar button is default icon");
     } else {
       _is(win.SocialToolbar.button.style.listStyleImage, 'url("' + Social.defaultProvider.iconURL + '")', "toolbar button has provider icon");
     }
   }
   // the menus should always have the provider name
   if (provider) {
     for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
       _is(document.getElementById(id).getAttribute("label"), Social.provider.name, "element has the provider name");
+
+    let contextMenus = [
+      {
+        type: "link",
+        id: "context-marklinkMenu",
+        label: "social.marklink.label"
+      },
+      {
+        type: "page",
+        id: "context-markpageMenu",
+        label: "social.markpage.label"
+      }
+    ];
+
+    for (let c of contextMenus) {
+      let leMenu = document.getElementById(c.id);
+      let parent, menus;
+      let markProviders = SocialMarks.getProviders();
+      if (markProviders.length > SocialMarks.MENU_LIMIT) {
+        // menus should be in a submenu, not in the top level of the context menu
+        parent = leMenu.firstChild;
+        menus = document.getElementsByClassName("context-mark" + c.type);
+        _is(menus.length, 0, "menu's are not in main context menu\n");
+        menus = parent.childNodes;
+        _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
+      } else {
+        // menus should be in the top level of the context menu, not in a submenu
+        parent = leMenu.parentNode;
+        menus = document.getElementsByClassName("context-mark" + c.type);
+        _is(menus.length, markProviders.length, c.id + " menu exists for each mark provider");
+        menus = leMenu.firstChild.childNodes;
+        _is(menus.length, 0, "menu's are not in context submenu\n");
+      }
+      for (let m of menus)
+        _is(m.parentNode, parent, "menu has correct parent");
+    }
   }
 
   // and for good measure, check all the social commands.
   isbool(!doc.getElementById("Social:Toggle").hidden, active, "Social:Toggle visible?");
   isbool(!doc.getElementById("Social:ToggleSidebar").hidden, enabled, "Social:ToggleSidebar visible?");
   isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
   isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?");
   isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
-  _is(doc.getElementById("Social:TogglePageMark").getAttribute("disabled"), canMark ? "false" : "true", "Social:TogglePageMark enabled?");
 
   // broadcasters.
   isbool(!doc.getElementById("socialActiveBroadcaster").hidden, active, "socialActiveBroadcaster hidden?");
   // and report on overall success of failure of the various checks here.
   is(numGoodTests, numTests, "The Social UI tests succeeded.")
 }
 
 // blocklist testing
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/social_mark.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <link id="siteicon" rel="icon" href="./icon.png"/>
+    <title>Demo Mark Window</title>
+    <script type="text/javascript">
+    window.addEventListener("socialFrameShow", function(e) {
+      var port = navigator.mozSocial.getWorker().port;
+      port.postMessage({topic: "status-panel-visibility", result: "shown"});
+    }, false);
+    window.addEventListener("socialFrameHide", function(e) {
+      var port = navigator.mozSocial.getWorker().port;
+      port.postMessage({topic: "status-panel-visibility", result: "hidden"});
+    }, false);
+
+    function updateTextNode(parent, text) {
+      var textNode = parent.childNodes[0];
+      if (textNode)
+        parent.removeChild(textNode);
+      textNode = document.createTextNode(text);
+      parent.appendChild(textNode);
+    }
+    function onLoad() {
+      updateTextNode(document.getElementById("shared"), location.search);
+      socialMarkUpdate(true);
+    }
+    function socialMarkUpdate(isMarked) {
+        var evt = document.createEvent("CustomEvent");
+        evt.initCustomEvent("socialMarkUpdate", true, true, JSON.stringify({marked: isMarked}));
+        document.documentElement.dispatchEvent(evt);
+    }
+    var shareData;
+    addEventListener("OpenGraphData", function(e) {
+      shareData = JSON.parse(e.detail);
+      updateTextNode(document.getElementById("shared"), shareData.url);
+      socialMarkUpdate(true);
+    });
+    </script>
+</head>
+
+<body onload="onLoad()">
+  <div id="content">
+    <h3>This window shows the mark data</h3>
+    <div>Page Marked: <div id="shared" class="textbox"></div></div>
+    <button id="unmark" onclick="socialMarkUpdate(false); window.close()">Unmark</button>
+    <button onclick="window.close();">Close</button>
+  </div>
+</body>
+</html>
deleted file mode 100644
index fa1f8fb0e23b9689e51f667a5745898f929c0960..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/base/content/test/social/social_worker.js
+++ b/browser/base/content/test/social/social_worker.js
@@ -95,33 +95,16 @@ onconnect = function(e) {
           profile = {
             portrait: "https://example.com/portrait.jpg",
             userName: "trickster",
             displayName: "Kuma Lisa",
             profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
           };
         }
         port.postMessage({topic: "social.user-profile", data: profile});
-        port.postMessage({
-          topic: "social.page-mark-config",
-          data: {
-            images: {
-              // this one is relative to test we handle relative ones.
-              marked: "/browser/browser/base/content/test/social/social_mark_image.png",
-              // absolute to check we handle them too.
-              unmarked: "https://example.com/browser/browser/base/content/test/social/social_mark_image.png"
-            },
-            messages: {
-              unmarkedTooltip: "Mark this page",
-              markedTooltip: "Unmark this page",
-              unmarkedLabel: "Mark",
-              markedLabel: "Unmark",
-            }
-          }
-        });
         break;
       case "test-ambient-notification":
         apiPort.postMessage({topic: "social.ambient-notification", data: event.data.data});
         break;
       case "test-isVisible":
         sidebarPort.postMessage({topic: "test-isVisible"});
         break;
       case "test-isVisible-response":
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4d3e72b8018ea0be59ca08a45c2ab7c1b6dcd73c
GIT binary patch
literal 779
zc$@(X1N8ifP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ
zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!ok>JNRCwBA
z{Qv(y10?_;fLL%z*k!g$ed1M`{)1n};NP$R|JlC2xElMZ^Tq$qFOz`MTYz|a#qvkn
z00M{s<N|D%yXWP<!0o^OpMLr8|C+o1{u{3T^Ivbu_5Z)WzyH6x#q583gzRRJI)DIT
zu}E)cx*(sw=HGt?pZ|;utPD&H4F7?6f`>4JiySk906PN%6Eg$DG&^R7pQleUJU{x3
z;pMv@AV;nT5dZ<i@`sU)lZl;4bc#0zD+^HLA7GHkax*XpurV;)|IEN3{f~j+(pv_G
zZ-0OW?V8G<5*)?w`t4_s-W?zUAb?nYu?k8vb2IUSIGY{<Z3ohZ!VC;@ybKJgd<+a<
zelsutUB__y^~YZ}+zf2r-`!{U@$(M@(ErasdH@25={u{47B?r8029!5W*}w<1}h^_
zf(^*#1Umf_(15={1C;+i|1KiT%fQ0Hj}q7b0mSr$L&mhih?)NZ&{Z5D0~jF&u>xr!
z4v0b0Krj7yagAS0OoZXZ?UNvVS78PK1Q5#?E;(IpRz~iFFBlkp0{v?&!oZ*cbh;QP
z1H*yGfB$X0_wDCTMt0T&?td%{9~c;3zWD+VKo9^3Ag2EuJmSCp{$~zWWMGI?Vqh>6
z{m;;I_7{WC^0)uj9J+MDT81-8_}81ot=ZWO|Ni`Cc=hHZNZ)Rl0RRESBKK^|rKXiy
zI$t=&IT`-{W8nDr?zh;7OJ_g#gjz8$2!j=MbXdH$@e@mAVqs=@`|cAc01v?o00<zK
zYkeW9Kspr>ETTZHj-D{xL`4O#rdWUgVj?oh`%8)oVwC_0AYu(r=i%mLc=75z12iAN
z0YCsTk&^S?y$7ejqsU?a0Yt0;(>HEA&T#wg)9D~KvIIbY0RX^P>0zF&!}|aL002ov
JPDHLkV1h<LU9$iH
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -116,16 +116,17 @@ browser.jar:
 *       content/browser/downloadManagerOverlay.xul    (content/downloadManagerOverlay.xul)
 *       content/browser/jsConsoleOverlay.xul          (content/jsConsoleOverlay.xul)
 *       content/browser/softwareUpdateOverlay.xul  (content/softwareUpdateOverlay.xul)
 #endif
 *       content/browser/viewSourceOverlay.xul         (content/viewSourceOverlay.xul)
 #ifdef XP_WIN
         content/browser/win6BrowserOverlay.xul        (content/win6BrowserOverlay.xul)
 #endif
+        content/browser/socialmarks.xml               (content/socialmarks.xml)
         content/browser/socialchat.xml                (content/socialchat.xml)
 # the following files are browser-specific overrides
 *       content/browser/license.html                  (/toolkit/content/license.html)
 % override chrome://global/content/license.html chrome://browser/content/license.html
 #ifdef MOZ_SAFE_BROWSING
         content/browser/report-phishing-overlay.xul     (content/report-phishing-overlay.xul)
         content/browser/blockedSite.xhtml               (content/blockedSite.xhtml)
 % overlay chrome://browser/content/browser.xul chrome://browser/content/report-phishing-overlay.xul
--- a/browser/components/search/test/browser_healthreport.js
+++ b/browser/components/search/test/browser_healthreport.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 function test() {
+  requestLongerTimeout(2);
   waitForExplicitFinish();
 
   try {
     let cm = Components.classes["@mozilla.org/categorymanager;1"]
                        .getService(Components.interfaces.nsICategoryManager);
     cm.getCategoryEntry("healthreport-js-provider-default", "SearchesProvider");
   } catch (ex) {
     // Health Report disabled, or no SearchesProvider.
--- a/browser/devtools/app-manager/app-validator.js
+++ b/browser/devtools/app-manager/app-validator.js
@@ -55,17 +55,17 @@ AppValidator.prototype._fetchManifest = 
     return deferred.promise;
   }
   req.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
   req.onload = (function () {
     let manifest = null;
     try {
       manifest = JSON.parse(req.responseText);
     } catch(e) {
-      this.error(strings.formatStringFromName("validator.invalidManifestJSON", [manifestURL, e], 2));
+      this.error(strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2));
     }
     deferred.resolve(manifest);
   }).bind(this);
   req.onerror = (function () {
     this.error(strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2));
     deferred.resolve(null);
   }).bind(this);
 
@@ -104,17 +104,17 @@ AppValidator.prototype.validateManifest 
   if (!manifest.name) {
     this.error(strings.GetStringFromName("validator.missNameManifestProperty"));
     return;
   }
 
   if (!manifest.icons || Object.keys(manifest.icons).length == 0) {
     this.warning(strings.GetStringFromName("validator.missIconsManifestProperty"));
   } else if (!manifest.icons["128"]) {
-    this.warning(strings.GetStringFromName("validator.missIconForMarketplace"));
+    this.warning(strings.GetStringFromName("validator.missIconMarketplace"));
   }
 }
 
 AppValidator.prototype.validateType = function (manifest) {
   let appType = manifest.type || "web";
   if (["web", "privileged", "certified"].indexOf(appType) === -1) {
     this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1));
   } else if (this.project.type == "hosted" &&
--- a/browser/devtools/app-manager/test/Makefile.in
+++ b/browser/devtools/app-manager/test/Makefile.in
@@ -2,10 +2,11 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_CHROME_FILES = \
 		test_template.html \
 		test_connection_store.html \
 		test_device_store.html \
 		test_projects_store.html \
+		test_remain_connected.html \
 		hosted_app.manifest \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/test_remain_connected.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+
+<!--
+Bug 912646 - Closing app toolbox causes phone to disconnect
+-->
+
+<html>
+
+  <head>
+    <meta charset="utf8">
+    <title></title>
+
+    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  </head>
+
+  <body>
+
+    <script type="application/javascript;version=1.8">
+      const Cu = Components.utils;
+
+      Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+      DebuggerServer.init(function () { return true; });
+      DebuggerServer.addBrowserActors();
+
+      window.onload = function() {
+        SimpleTest.waitForExplicitFinish();
+
+        Cu.import("resource:///modules/devtools/gDevTools.jsm");
+
+        const {devtools} =
+          Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+        const {require} = devtools;
+
+        const {Connection, ConnectionManager} =
+          require("devtools/client/connection-manager");
+        const ConnectionStore =
+          require("devtools/app-manager/connection-store");
+
+        let connection = ConnectionManager.createConnection();
+
+        connection.host = null; // force pipe
+        connection.port = null;
+
+        let been_through_connecting = false;
+        let been_through_connected = false;
+        let been_through_disconnected = false;
+
+        is(connection.status, Connection.Status.DISCONNECTED,
+           "status updated (diconnected)");
+
+        connection.once("connecting", () => {
+          SimpleTest.executeSoon(() => {
+            been_through_connecting = true;
+            is(connection.status, Connection.Status.CONNECTING,
+               "status updated (connecting)");
+          })
+        });
+
+        connection.once("connected", () => {
+          SimpleTest.executeSoon(() => {
+            been_through_connected = true;
+            is(connection.status, Connection.Status.CONNECTED,
+               "status updated (connected)");
+            cycleToolbox();
+          })
+        });
+
+        function cycleToolbox() {
+          connection.client.listTabs(response => {
+            let options = {
+              form: response.tabs[0],
+              client: connection.client,
+              chrome: true
+            };
+            devtools.TargetFactory.forRemoteTab(options).then(target => {
+              let hostType = devtools.Toolbox.HostType.WINDOW;
+              gDevTools.showToolbox(target,
+                                    null,
+                                    hostType).then(toolbox => {
+                SimpleTest.executeSoon(() => {
+                  toolbox.once("destroyed", onDestroyToolbox);
+                  toolbox.destroy();
+                });
+              });
+            });
+          });
+        }
+
+        function onDestroyToolbox() {
+          is(connection.status, Connection.Status.CONNECTED,
+             "toolbox cycled, still connected");
+          connection.disconnect();
+        }
+
+        connection.once("disconnected", () => {
+          SimpleTest.executeSoon(() => {
+            been_through_disconnected = true;
+            is(connection.status, Connection.Status.DISCONNECTED,
+               "status updated (disconnected)");
+            connection.destroy();
+            finishUp();
+          })
+        });
+
+        function finishUp() {
+          ok(been_through_connecting &&
+             been_through_connected &&
+             been_through_disconnected, "All updates happened");
+          DebuggerServer.destroy();
+          SimpleTest.finish();
+        }
+
+        connection.connect();
+      }
+
+    </script>
+  </body>
+</html>
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -1643,17 +1643,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
     }
   });
 }(this));
 
 /* CmdScreenshot ----------------------------------------------------------- */
 
 (function(module) {
   XPCOMUtils.defineLazyModuleGetter(this, "LayoutHelpers",
-                                    "resource:///modules/devtools/LayoutHelpers.jsm");
+                                    "resource://gre/modules/devtools/LayoutHelpers.jsm");
 
   // String used as an indication to generate default file name in the following
   // format: "Screen Shot yyyy-mm-dd at HH.MM.SS.png"
   const FILENAME_DEFAULT_VALUE = " ";
 
   /**
    * 'screenshot' command
    */
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -14,17 +14,17 @@ const FETCH_SOURCE_RESPONSE_DELAY = 50; 
 const FRAME_STEP_CLEAR_DELAY = 100; // ms
 const CALL_STACK_PAGE_SIZE = 25; // frames
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
 Cu.import("resource:///modules/source-editor.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -446,16 +446,17 @@ SourcesView.prototype = Heritage.extend(
    *         An object containing the breakpoint container, checkbox,
    *         line number and line text nodes.
    */
   _createBreakpointView: function(aOptions) {
     let { lineNumber, lineText } = aOptions;
 
     let checkbox = document.createElement("checkbox");
     checkbox.setAttribute("checked", "true");
+    checkbox.className = "dbg-breakpoint-checkbox";
 
     let lineNumberNode = document.createElement("label");
     lineNumberNode.className = "plain dbg-breakpoint-line";
     lineNumberNode.setAttribute("value", lineNumber);
 
     let lineTextNode = document.createElement("label");
     lineTextNode.className = "plain dbg-breakpoint-text";
     lineTextNode.setAttribute("value", lineText);
--- a/browser/devtools/framework/connect/connect.js
+++ b/browser/devtools/framework/connect/connect.js
@@ -164,12 +164,17 @@ function handleConnectionTimeout() {
  */
 function openToolbox(form, chrome=false) {
   let options = {
     form: form,
     client: gClient,
     chrome: chrome
   };
   devtools.TargetFactory.forRemoteTab(options).then((target) => {
-    gDevTools.showToolbox(target, "webconsole", devtools.Toolbox.HostType.WINDOW);
+    let hostType = devtools.Toolbox.HostType.WINDOW;
+    gDevTools.showToolbox(target, "webconsole", hostType).then((toolbox) => {
+      toolbox.once("destroyed", function() {
+        gClient.close();
+      });
+    });
     window.close();
   });
 }
--- a/browser/devtools/framework/target.js
+++ b/browser/devtools/framework/target.js
@@ -41,17 +41,18 @@ exports.TargetFactory = {
   },
 
   /**
    * Return a promise of a Target for a remote tab.
    * @param {Object} options
    *        The options object has the following properties:
    *        {
    *          form: the remote protocol form of a tab,
-   *          client: a DebuggerClient instance,
+   *          client: a DebuggerClient instance
+   *                  (caller owns this and is responsible for closing),
    *          chrome: true if the remote target is the whole process
    *        }
    *
    * @return A promise of a target object
    */
   forRemoteTab: function TF_forRemoteTab(options) {
     let targetPromise = promiseTargets.get(options);
     if (targetPromise == null) {
@@ -328,16 +329,29 @@ TabTarget.prototype = {
     this._webProgressListener = new TabWebProgressListener(this);
     this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
     this.tab.addEventListener("TabClose", this);
     this.tab.parentNode.addEventListener("TabSelect", this);
     this.tab.ownerDocument.defaultView.addEventListener("unload", this);
   },
 
   /**
+   * Teardown event listeners.
+   */
+  _teardownListeners: function TabTarget__teardownListeners() {
+    if (this._webProgressListener) {
+      this._webProgressListener.destroy();
+    }
+
+    this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
+    this._tab.removeEventListener("TabClose", this);
+    this._tab.parentNode.removeEventListener("TabSelect", this);
+  },
+
+  /**
    * Setup listeners for remote debugging, updating existing ones as necessary.
    */
   _setupRemoteListeners: function TabTarget__setupRemoteListeners() {
     this.client.addListener("tabDetached", this.destroy);
 
     this._onTabNavigated = function onRemoteTabNavigated(aType, aPacket) {
       let event = Object.create(null);
       event.url = aPacket.url;
@@ -354,16 +368,24 @@ TabTarget.prototype = {
         this.emit("navigate", event);
         this._navWindow = null;
       }
     }.bind(this);
     this.client.addListener("tabNavigated", this._onTabNavigated);
   },
 
   /**
+   * Teardown listeners for remote debugging.
+   */
+  _teardownRemoteListeners: function TabTarget__teardownRemoteListeners() {
+    this.client.removeListener("tabNavigated", this._onTabNavigated);
+    this.client.removeListener("tabDetached", this.destroy);
+  },
+
+  /**
    * Handle tabs events.
    */
   handleEvent: function (event) {
     switch (event.type) {
       case "TabClose":
       case "unload":
         this.destroy();
         break;
@@ -407,59 +429,62 @@ TabTarget.prototype = {
     this.emit("close");
 
     // First of all, do cleanup tasks that pertain to both remoted and
     // non-remoted targets.
     this.off("thread-resumed", this._handleThreadState);
     this.off("thread-paused", this._handleThreadState);
 
     if (this._tab) {
-      if (this._webProgressListener) {
-        this._webProgressListener.destroy();
-      }
-
-      this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
-      this._tab.removeEventListener("TabClose", this);
-      this._tab.parentNode.removeEventListener("TabSelect", this);
+      this._teardownListeners();
     }
 
     // If this target was not remoted, the promise will be resolved before the
     // function returns.
     if (this._tab && !this._client) {
-      targets.delete(this._tab);
-      this._tab = null;
-      this._client = null;
-      this._form = null;
-      this._remote = null;
-
+      this._cleanup();
       this._destroyer.resolve(null);
     } else if (this._client) {
       // If, on the other hand, this target was remoted, the promise will be
       // resolved after the remote connection is closed.
-      this.client.removeListener("tabNavigated", this._onTabNavigated);
-      this.client.removeListener("tabDetached", this.destroy);
+      this._teardownRemoteListeners();
 
-      this._client.close(function onClosed() {
-        if (this._tab) {
-          targets.delete(this._tab);
-        } else {
-          promiseTargets.delete(this._form);
-        }
-        this._client = null;
-        this._tab = null;
-        this._form = null;
-        this._remote = null;
-
+      if (this.isLocalTab) {
+        // We started with a local tab and created the client ourselves, so we
+        // should close it.
+        this._client.close(() => {
+          this._cleanup();
+          this._destroyer.resolve(null);
+        });
+      } else {
+        // The client was handed to us, so we are not responsible for closing
+        // it.
+        this._cleanup();
         this._destroyer.resolve(null);
-      }.bind(this));
+      }
     }
 
     return this._destroyer.promise;
   },
 
+  /**
+   * Clean up references to what this target points to.
+   */
+  _cleanup: function TabTarget__cleanup() {
+    if (this._tab) {
+      targets.delete(this._tab);
+    } else {
+      promiseTargets.delete(this._form);
+    }
+    this._client = null;
+    this._tab = null;
+    this._form = null;
+    this._remote = null;
+  },
+
   toString: function() {
     return 'TabTarget:' + (this._tab ? this._tab : (this._form && this._form.actor));
   },
 };
 
 
 /**
  * WebProgressListener for TabTarget.
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -842,18 +842,17 @@ Toolbox.prototype = {
     while(container.firstChild) {
       container.removeChild(container.firstChild);
     }
 
     outstanding.push(this._host.destroy());
 
     this._telemetry.destroy();
 
-    // Targets need to be notified that the toolbox is being torn down, so that
-    // remote protocol connections can be gracefully terminated.
+    // Targets need to be notified that the toolbox is being torn down.
     if (this._target) {
       this._target.off("close", this.destroy);
       outstanding.push(this._target.destroy());
     }
     this._target = null;
 
     promise.all(outstanding).then(function() {
       this.emit("destroyed");
--- a/browser/devtools/inspector/breadcrumbs.js
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -6,17 +6,17 @@
 
 const {Cc, Cu, Ci} = require("chrome");
 
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 let promise = require("sdk/core/promise");
 
 const LOW_PRIORITY_ELEMENTS = {
   "HEAD": true,
   "BASE": true,
   "BASEFONT": true,
--- a/browser/devtools/inspector/highlighter.js
+++ b/browser/devtools/inspector/highlighter.js
@@ -2,24 +2,37 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {Cu, Cc, Ci} = require("chrome");
 
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 let EventEmitter = require("devtools/shared/event-emitter");
 
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
   // add ":visited" and ":link" after bug 713106 is fixed
 
+exports._forceBasic = {value: false};
+
+exports.Highlighter = function Highlighter(aTarget, aInspector, aToolbox) {
+  if (aTarget.isLocalTab && !exports._forceBasic.value) {
+    return new LocalHighlighter(aTarget, aInspector, aToolbox);
+  } else {
+    return new BasicHighlighter(aTarget, aInspector, aToolbox);
+  }
+}
+
+exports.LocalHighlighter = LocalHighlighter;
+exports.BasicHighlighter = BasicHighlighter;
+
 /**
  * A highlighter mechanism.
  *
  * The highlighter is built dynamically into the browser element.
  * The caller is in charge of destroying the highlighter (ie, the highlighter
  * won't be destroyed if a new tab is selected for example).
  *
  * API:
@@ -66,39 +79,37 @@ const PSEUDO_CLASSES = [":hover", ":acti
 
 /**
  * Constructor.
  *
  * @param aTarget The inspection target.
  * @param aInspector Inspector panel.
  * @param aToolbox The toolbox holding the inspector.
  */
-function Highlighter(aTarget, aInspector, aToolbox)
+function LocalHighlighter(aTarget, aInspector, aToolbox)
 {
   this.target = aTarget;
   this.tab = aTarget.tab;
   this.toolbox = aToolbox;
   this.browser = this.tab.linkedBrowser;
   this.chromeDoc = this.tab.ownerDocument;
   this.chromeWin = this.chromeDoc.defaultView;
   this.inspector = aInspector
 
   EventEmitter.decorate(this);
 
   this._init();
 }
 
-exports.Highlighter = Highlighter;
-
-Highlighter.prototype = {
+LocalHighlighter.prototype = {
   get selection() {
     return this.inspector.selection;
   },
 
-  _init: function Highlighter__init()
+  _init: function LocalHighlighter__init()
   {
     this.toggleLockState = this.toggleLockState.bind(this);
     this.unlockAndFocus = this.unlockAndFocus.bind(this);
     this.updateInfobar = this.updateInfobar.bind(this);
     this.highlight = this.highlight.bind(this);
 
     let stack = this.browser.parentNode;
     this.win = this.browser.contentWindow;
@@ -152,17 +163,17 @@ Highlighter.prototype = {
 
     this.hidden = true;
     this.highlight();
   },
 
   /**
    * Destroy the nodes. Remove listeners.
    */
-  destroy: function Highlighter_destroy()
+  destroy: function LocalHighlighter_destroy()
   {
     this.inspectButton.removeEventListener("command", this.unlockAndFocus);
     this.inspectButton = null;
 
     this.toolbox.off("select", this.onToolSelected);
     this.toolbox = null;
 
     this.selection.off("new-node", this.highlight);
@@ -190,17 +201,17 @@ Highlighter.prototype = {
     this.tabbrowser = null;
 
     this.emit("closed");
   },
 
   /**
    * Show the outline, and select a node.
    */
-  highlight: function Highlighter_highlight()
+  highlight: function LocalHighlighter_highlight()
   {
     if (this.selection.reason != "highlighter") {
       this.lock();
     }
 
     let canHighlightNode = this.selection.isNode() &&
                           this.selection.isConnected() &&
                           this.selection.isElementNode();
@@ -220,17 +231,17 @@ Highlighter.prototype = {
       this.disabled = true;
       this.hide();
     }
   },
 
   /**
    * Update the highlighter size and position.
    */
-  invalidateSize: function Highlighter_invalidateSize()
+  invalidateSize: function LocalHighlighter_invalidateSize()
   {
     let canHiglightNode = this.selection.isNode() &&
                           this.selection.isConnected() &&
                           this.selection.isElementNode();
 
     if (!canHiglightNode)
       return;
 
@@ -328,76 +339,76 @@ Highlighter.prototype = {
       this.selection.setNode(this.startNode);
       this.lock();
     }
   },
 
   /**
    * Focus the browser before unlocking.
    */
-  unlockAndFocus: function Highlighter_unlockAndFocus() {
+  unlockAndFocus: function LocalHighlighter_unlockAndFocus() {
     if (this.locked === false) return;
     this.chromeWin.focus();
     this.unlock();
   },
 
   /**
    * Hide the infobar
    */
-   hideInfobar: function Highlighter_hideInfobar() {
+   hideInfobar: function LocalHighlighter_hideInfobar() {
      this.nodeInfo.container.setAttribute("force-transitions", "true");
      this.nodeInfo.container.setAttribute("hidden", "true");
    },
 
   /**
    * Show the infobar
    */
-   showInfobar: function Highlighter_showInfobar() {
+   showInfobar: function LocalHighlighter_showInfobar() {
      this.nodeInfo.container.removeAttribute("hidden");
      this.moveInfobar();
      this.nodeInfo.container.removeAttribute("force-transitions");
    },
 
   /**
    * Hide the outline
    */
-   hideOutline: function Highlighter_hideOutline() {
+   hideOutline: function LocalHighlighter_hideOutline() {
      this.outline.setAttribute("hidden", "true");
    },
 
   /**
    * Show the outline
    */
-   showOutline: function Highlighter_showOutline() {
+   showOutline: function LocalHighlighter_showOutline() {
      if (this._highlighting)
        this.outline.removeAttribute("hidden");
    },
 
   /**
    * Build the node Infobar.
    *
    * <box class="highlighter-nodeinfobar-container">
-   *   <box class="Highlighter-nodeinfobar-arrow-top"/>
+   *   <box class="highlighter-nodeinfobar-arrow-top"/>
    *   <hbox class="highlighter-nodeinfobar">
    *     <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"/>
    *     <hbox class="highlighter-nodeinfobar-text">
    *       <xhtml:span class="highlighter-nodeinfobar-tagname"/>
    *       <xhtml:span class="highlighter-nodeinfobar-id"/>
    *       <xhtml:span class="highlighter-nodeinfobar-classes"/>
    *       <xhtml:span class="highlighter-nodeinfobar-pseudo-classes"/>
    *     </hbox>
    *     <toolbarbutton class="highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"/>
    *   </hbox>
-   *   <box class="Highlighter-nodeinfobar-arrow-bottom"/>
+   *   <box class="highlighter-nodeinfobar-arrow-bottom"/>
    * </box>
    *
    * @param nsIDOMElement aParent
    *        The container of the infobar.
    */
-  buildInfobar: function Highlighter_buildInfobar(aParent)
+  buildInfobar: function LocalHighlighter_buildInfobar(aParent)
   {
     let container = this.chromeDoc.createElement("box");
     container.className = "highlighter-nodeinfobar-container";
     container.setAttribute("position", "top");
     container.setAttribute("disabled", "true");
 
     let nodeInfobar = this.chromeDoc.createElement("hbox");
     nodeInfobar.className = "highlighter-nodeinfobar";
@@ -484,17 +495,17 @@ Highlighter.prototype = {
   /**
    * Highlight a rectangular region.
    *
    * @param object aRect
    *        The rectangle region to highlight.
    * @returns boolean
    *          True if the rectangle was highlighted, false otherwise.
    */
-  highlightRectangle: function Highlighter_highlightRectangle(aRect)
+  highlightRectangle: function LocalHighlighter_highlightRectangle(aRect)
   {
     if (!aRect) {
       this.unhighlight();
       return;
     }
 
     let oldRect = this._contentRect;
 
@@ -527,26 +538,26 @@ Highlighter.prototype = {
     this._highlightRect = aRectScaled; // and save the scaled rect.
 
     return;
   },
 
   /**
    * Clear the highlighter surface.
    */
-  unhighlight: function Highlighter_unhighlight()
+  unhighlight: function LocalHighlighter_unhighlight()
   {
     this._highlighting = false;
     this.hideOutline();
   },
 
   /**
    * Update node information (tagName#id.class)
    */
-  updateInfobar: function Highlighter_updateInfobar()
+  updateInfobar: function LocalHighlighter_updateInfobar()
   {
     if (!this.selection.isElementNode()) {
       this.nodeInfo.tagNameLabel.textContent = "";
       this.nodeInfo.idLabel.textContent = "";
       this.nodeInfo.classesBox.textContent = "";
       this.nodeInfo.pseudoClassesBox.textContent = "";
       return;
     }
@@ -572,17 +583,17 @@ Highlighter.prototype = {
 
     let pseudoBox = this.nodeInfo.pseudoClassesBox;
     pseudoBox.textContent = pseudos.join("");
   },
 
   /**
    * Move the Infobar to the right place in the highlighter.
    */
-  moveInfobar: function Highlighter_moveInfobar()
+  moveInfobar: function LocalHighlighter_moveInfobar()
   {
     if (this._highlightRect) {
       let winHeight = this.win.innerHeight * this.zoom;
       let winWidth = this.win.innerWidth * this.zoom;
 
       let rect = {top: this._highlightRect.top,
                   left: this._highlightRect.left,
                   width: this._highlightRect.width,
@@ -639,65 +650,65 @@ Highlighter.prototype = {
       this.nodeInfo.container.setAttribute("position", "top");
       this.nodeInfo.container.setAttribute("hide-arrow", "true");
     }
   },
 
   /**
    * Store page zoom factor.
    */
-  computeZoomFactor: function Highlighter_computeZoomFactor() {
+  computeZoomFactor: function LocalHighlighter_computeZoomFactor() {
     this.zoom =
       this.win.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils)
       .fullZoom;
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
-  attachMouseListeners: function Highlighter_attachMouseListeners()
+  attachMouseListeners: function LocalHighlighter_attachMouseListeners()
   {
     this.browser.addEventListener("mousemove", this, true);
     this.browser.addEventListener("click", this, true);
     this.browser.addEventListener("dblclick", this, true);
     this.browser.addEventListener("mousedown", this, true);
     this.browser.addEventListener("mouseup", this, true);
   },
 
-  detachMouseListeners: function Highlighter_detachMouseListeners()
+  detachMouseListeners: function LocalHighlighter_detachMouseListeners()
   {
     this.browser.removeEventListener("mousemove", this, true);
     this.browser.removeEventListener("click", this, true);
     this.browser.removeEventListener("dblclick", this, true);
     this.browser.removeEventListener("mousedown", this, true);
     this.browser.removeEventListener("mouseup", this, true);
   },
 
-  attachPageListeners: function Highlighter_attachPageListeners()
+  attachPageListeners: function LocalHighlighter_attachPageListeners()
   {
     this.browser.addEventListener("resize", this, true);
     this.browser.addEventListener("scroll", this, true);
     this.browser.addEventListener("MozAfterPaint", this, true);
   },
 
-  detachPageListeners: function Highlighter_detachPageListeners()
+  detachPageListeners: function LocalHighlighter_detachPageListeners()
   {
     this.browser.removeEventListener("resize", this, true);
     this.browser.removeEventListener("scroll", this, true);
     this.browser.removeEventListener("MozAfterPaint", this, true);
   },
 
   /**
    * Generic event handler.
    *
    * @param nsIDOMEvent aEvent
    *        The DOM event object.
    */
-  handleEvent: function Highlighter_handleEvent(aEvent)
+  handleEvent: function LocalHighlighter_handleEvent(aEvent)
   {
     switch (aEvent.type) {
       case "click":
         this.handleClick(aEvent);
         break;
       case "mousemove":
         this.brieflyIgnorePageEvents();
         this.handleMouseMove(aEvent);
@@ -718,17 +729,17 @@ Highlighter.prototype = {
         break;
     }
   },
 
   /**
    * Disable the CSS transitions for a short time to avoid laggy animations
    * during scrolling or resizing.
    */
-  brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
+  brieflyDisableTransitions: function LocalHighlighter_brieflyDisableTransitions()
   {
     if (this.transitionDisabler) {
       this.chromeWin.clearTimeout(this.transitionDisabler);
     } else {
       this.outline.setAttribute("disable-transitions", "true");
       this.nodeInfo.container.setAttribute("disable-transitions", "true");
     }
     this.transitionDisabler =
@@ -737,17 +748,17 @@ Highlighter.prototype = {
         this.nodeInfo.container.removeAttribute("disable-transitions");
         this.transitionDisabler = null;
       }.bind(this), 500);
   },
 
   /**
    * Don't listen to page events while inspecting with the mouse.
    */
-  brieflyIgnorePageEvents: function Highlighter_brieflyIgnorePageEvents()
+  brieflyIgnorePageEvents: function LocalHighlighter_brieflyIgnorePageEvents()
   {
     // The goal is to keep smooth animations while inspecting.
     // CSS Transitions might be interrupted because of a MozAfterPaint
     // event that would triger an invalidateSize() call.
     // So we don't listen to events that would trigger an invalidateSize()
     // call.
     //
     // Side effect, zoom levels are not updated during this short period.
@@ -768,17 +779,17 @@ Highlighter.prototype = {
   },
 
   /**
    * Handle clicks.
    *
    * @param nsIDOMEvent aEvent
    *        The DOM event.
    */
-  handleClick: function Highlighter_handleClick(aEvent)
+  handleClick: function LocalHighlighter_handleClick(aEvent)
   {
     // Stop inspection when the user clicks on a node.
     if (aEvent.button == 0) {
       this.lock();
       let node = this.selection.node;
       this.selection.setNode(node, "highlighter-lock");
       aEvent.preventDefault();
       aEvent.stopPropagation();
@@ -786,34 +797,79 @@ Highlighter.prototype = {
   },
 
   /**
    * Handle mousemoves in panel.
    *
    * @param nsiDOMEvent aEvent
    *        The MouseEvent triggering the method.
    */
-  handleMouseMove: function Highlighter_handleMouseMove(aEvent)
+  handleMouseMove: function LocalHighlighter_handleMouseMove(aEvent)
   {
     let doc = aEvent.target.ownerDocument;
 
     // This should never happen, but just in case, we don't let the
     // highlighter highlight browser nodes.
     if (doc && doc != this.chromeDoc) {
       let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
         aEvent.clientX, aEvent.clientY);
       if (element && element != this.selection.node) {
         this.selection.setNode(element, "highlighter");
       }
     }
   },
 };
 
+// BasicHighlighter. Doesn't implement any fancy features. Just change
+// the outline of the selected node. Works with remote target.
+
+function BasicHighlighter(aTarget, aInspector)
+{
+  this.walker = aInspector.walker;
+  this.selection = aInspector.selection;
+  this.highlight = this.highlight.bind(this);
+  this.selection.on("new-node-front", this.highlight);
+  EventEmitter.decorate(this);
+  this.locked = true;
+}
+
+BasicHighlighter.prototype = {
+  destroy: function() {
+    this.selection.off("new-node-front", this.highlight);
+    this.walker = null;
+    this.selection = null;
+  },
+  toggleLockState: function() {
+    this.locked = !this.locked;
+    if (this.locked) {
+      this.walker.cancelPick();
+    } else {
+      this.emit("unlocked");
+      this.walker.pick().then(
+        (node) => this._onPick(node),
+        () => this._onPick(null)
+      );
+    }
+  },
+  highlight: function() {
+    this.walker.highlight(this.selection.nodeFront);
+  },
+  _onPick: function(node) {
+    if (node) {
+      this.selection.setNodeFront(node);
+    }
+    this.locked = true;
+    this.emit("locked");
+  },
+  hide: function() {},
+  show: function() {},
+}
+
 ///////////////////////////////////////////////////////////////////////////
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils)
 });
 
-XPCOMUtils.defineLazyGetter(Highlighter.prototype, "strings", function () {
+XPCOMUtils.defineLazyGetter(LocalHighlighter.prototype, "strings", function () {
     return Services.strings.createBundle(
             "chrome://browser/locale/devtools/inspector.properties");
 });
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -77,30 +77,16 @@ InspectorPanel.prototype = {
 
     this.breadcrumbs = new HTMLBreadcrumbs(this);
 
     if (this.target.isLocalTab) {
       this.browser = this.target.tab.linkedBrowser;
       this.scheduleLayoutChange = this.scheduleLayoutChange.bind(this);
       this.browser.addEventListener("resize", this.scheduleLayoutChange, true);
 
-      this.highlighter = new Highlighter(this.target, this, this._toolbox);
-      let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
-      button.hidden = false;
-      this.onLockStateChanged = function() {
-        if (this.highlighter.locked) {
-          button.removeAttribute("checked");
-          this._toolbox.raise();
-        } else {
-          button.setAttribute("checked", "true");
-        }
-      }.bind(this);
-      this.highlighter.on("locked", this.onLockStateChanged);
-      this.highlighter.on("unlocked", this.onLockStateChanged);
-
       // Show a warning when the debugger is paused.
       // We show the warning only when the inspector
       // is selected.
       this.updateDebuggerPausedWarning = function() {
         let notificationBox = this._toolbox.getNotificationBox();
         let notification = notificationBox.getNotificationWithValue("inspector-script-paused");
         if (!notification && this._toolbox.currentToolId == "inspector" &&
             this.target.isThreadPaused) {
@@ -119,16 +105,29 @@ InspectorPanel.prototype = {
 
       }.bind(this);
       this.target.on("thread-paused", this.updateDebuggerPausedWarning);
       this.target.on("thread-resumed", this.updateDebuggerPausedWarning);
       this._toolbox.on("select", this.updateDebuggerPausedWarning);
       this.updateDebuggerPausedWarning();
     }
 
+    this.highlighter = new Highlighter(this.target, this, this._toolbox);
+    let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
+    this.onLockStateChanged = function() {
+      if (this.highlighter.locked) {
+        button.removeAttribute("checked");
+        this._toolbox.raise();
+      } else {
+        button.setAttribute("checked", "true");
+      }
+    }.bind(this);
+    this.highlighter.on("locked", this.onLockStateChanged);
+    this.highlighter.on("unlocked", this.onLockStateChanged);
+
     this._initMarkup();
     this.isReady = false;
 
     this.once("markuploaded", function() {
       this.isReady = true;
 
       // All the components are initialized. Let's select a node.
       this._selection.setNodeFront(defaultSelection);
--- a/browser/devtools/inspector/inspector.xul
+++ b/browser/devtools/inspector/inspector.xul
@@ -66,17 +66,16 @@
   <box flex="1" class="devtools-responsive-container">
     <vbox flex="1">
       <toolbar id="inspector-toolbar"
         class="devtools-toolbar"
         nowindowdrag="true">
         <toolbarbutton id="inspector-inspect-toolbutton"
           tooltiptext="&inspector.selectButton.tooltip;"
           class="devtools-toolbarbutton"
-          hidden="true"
           oncommand="inspector.highlighter.toggleLockState()"/>
         <arrowscrollbox id="inspector-breadcrumbs"
           class="breadcrumbs-widget-container"
           flex="1" orient="horizontal"
           clicktoscroll="true"/>
         <textbox id="inspector-searchbox"
           type="search"
           timeout="50"
--- a/browser/devtools/inspector/test/Makefile.in
+++ b/browser/devtools/inspector/test/Makefile.in
@@ -36,10 +36,11 @@ MOCHITEST_BROWSER_FILES := \
 		browser_inspector_bug_831693_combinator_suggestions.js \
 		browser_inspector_bug_831693_search_suggestions.html \
 		browser_inspector_bug_835722_infobar_reappears.js \
 		browser_inspector_bug_840156_destroy_after_navigation.js \
 		browser_inspector_reload.js \
 		browser_inspector_select_last_selected.js \
 		browser_inspector_select_last_selected.html \
 		browser_inspector_select_last_selected2.html \
+		browser_inspector_basic_highlighter.js \
 		head.js \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_basic_highlighter.js
@@ -0,0 +1,60 @@
+/* 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/. */
+
+
+function test()
+{
+  let inspector, doc;
+  let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+  let {require} = devtools;
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    doc = content.document;
+    waitForFocus(setupTest, content);
+  }, true);
+
+  content.location = "data:text/html,<h1>foo<h1><h2>bar</h2>";
+
+  function setupTest()
+  {
+    let h = require("devtools/inspector/highlighter");
+    h._forceBasic.value = true;
+    openInspector(runTests);
+  }
+
+  function runTests(aInspector)
+  {
+    inspector = aInspector;
+    let h1 = doc.querySelector("h1");
+    inspector.selection.once("new-node-front", () => executeSoon(testH1Selected));
+    inspector.selection.setNode(h1);
+  }
+
+  function testH1Selected() {
+    let h1 = doc.querySelector("h1");
+    let nodes = doc.querySelectorAll(":-moz-devtools-highlighted");
+    is(nodes.length, 1, "only one node selected");
+    is(nodes[0], h1, "h1 selected");
+    inspector.selection.once("new-node-front", () => executeSoon(testNoNodeSelected));
+    inspector.selection.setNode(null);
+  }
+
+  function testNoNodeSelected() {
+    ok(doc.querySelectorAll(":-moz-devtools-highlighted").length, 0, "no node selected");
+    finishUp();
+  }
+
+  function finishUp() {
+    let h = require("devtools/inspector/highlighter");
+    h._forceBasic.value = false;
+    gBrowser.removeCurrentTab();
+    finish();
+  }
+}
+
+
--- a/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
+++ b/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
@@ -27,17 +27,17 @@ function test()
   {
     inspector = aInspector;
     inspector.selection.setNode(node);
     inspector.once("inspector-updated", () => {
       let parentNode = node.parentNode;
       parentNode.removeChild(node);
 
       let tmp = {};
-      Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
+      Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
       ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
 
       // Wait for the inspector to process the mutation
       inspector.once("inspector-updated", () => {
         is(inspector.selection.node, parentNode, "parent of selection got selected");
         finishUp();
       });
     });
--- a/browser/devtools/inspector/test/browser_inspector_destroyselection.js
+++ b/browser/devtools/inspector/test/browser_inspector_destroyselection.js
@@ -27,17 +27,17 @@ function test()
   {
     inspector = aInspector;
     inspector.selection.setNode(node);
 
     iframe.parentNode.removeChild(iframe);
     iframe = null;
 
     let tmp = {};
-    Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
+    Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
     ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
     ok(!inspector.selection.isConnected(), "Selection considered as disconnected");
 
     finishUp();
   }
 
   function finishUp() {
     node = null;
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -8,17 +8,17 @@ const Cc = Components.classes;
 
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
 
 let tempScope = {};
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
 
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", tempScope);
 let TargetFactory = devtools.TargetFactory;
 
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
 let console = tempScope.console;
 
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 
 const promise = devtools.require("sdk/core/promise");
 
 function LayoutView(aInspector, aWindow)
 {
   this.inspector = aInspector;
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -12,17 +12,17 @@ const PAGE_SIZE = 10;
 const PREVIEW_AREA = 700;
 const DEFAULT_MAX_CHILDREN = 100;
 
 let {UndoStack} = require("devtools/shared/undo");
 let EventEmitter = require("devtools/shared/event-emitter");
 let {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
 let promise = require("sdk/core/promise");
 
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Templater.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyGetter(this, "DOMParser", function() {
  return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
 });
 loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
--- a/browser/devtools/profiler/profiler.xul
+++ b/browser/devtools/profiler/profiler.xul
@@ -11,21 +11,23 @@
 <?xml-stylesheet href="chrome://browser/content/devtools/widgets.css"?>
 
 <!DOCTYPE window [
 <!ENTITY % profilerDTD SYSTEM "chrome://browser/locale/devtools/profiler.dtd">
   %profilerDTD;
 ]>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="text/javascript" src="sidebar.js"/>
   <box flex="1" id="profiler-chrome" class="devtools-responsive-container">
     <vbox class="profiler-sidebar">
       <toolbar class="devtools-toolbar">
         <hbox id="profiler-controls">
           <toolbarbutton id="profiler-start"
+            tooltiptext="&startProfiler.tooltip;"
             class="devtools-toolbarbutton"
             disabled="true"/>
           <toolbarbutton id="profiler-import"
             class="devtools-toolbarbutton"
             disabled="true"
             label="&importProfile.label;"/>
         </hbox>
       </toolbar>
--- a/browser/devtools/profiler/sidebar.js
+++ b/browser/devtools/profiler/sidebar.js
@@ -14,16 +14,19 @@ const {
   PROFILE_IDLE,
   PROFILE_COMPLETED,
   PROFILE_RUNNING,
   L10N_BUNDLE
 } = require("devtools/profiler/consts");
 
 loader.lazyGetter(this, "L10N", () => new ViewHelpers.L10N(L10N_BUNDLE));
 
+let stopProfilingString = L10N.getStr("profiler.stopProfilerString");
+let startProfilingString = L10N.getStr("profiler.startProfilerString");
+
 function Sidebar(el) {
   EventEmitter.decorate(this);
 
   this.document = el.ownerDocument;
   this.widget = new SideMenuWidget(el, { showArrows: true });
   this.widget.notice = L10N.getStr("profiler.sidebarNotice");
 
   this.widget.addEventListener("select", (ev) => {
@@ -88,30 +91,34 @@ Sidebar.prototype = Heritage.extend(Widg
   },
 
   getItemByProfile: function (profile) {
     return this.getItemByValue(profile.uid.toString());
   },
 
   setProfileState: function (profile, state) {
     let item = this.getItemByProfile(profile);
+    let doc = this.document;
     let label = item.target.querySelector(".profiler-sidebar-item > hbox > span");
+    let toggleButton = doc.getElementById("profiler-start");
 
     switch (state) {
       case PROFILE_IDLE:
         item.target.setAttribute("state", "idle");
         label.textContent = L10N.getStr("profiler.stateIdle");
         break;
       case PROFILE_RUNNING:
         item.target.setAttribute("state", "running");
         label.textContent = L10N.getStr("profiler.stateRunning");
+        toggleButton.setAttribute("tooltiptext",stopProfilingString);
         break;
       case PROFILE_COMPLETED:
         item.target.setAttribute("state", "completed");
         label.textContent = L10N.getStr("profiler.stateCompleted");
+        toggleButton.setAttribute("tooltiptext",startProfilingString);
         break;
       default: // Wrong state, do nothing.
         return;
     }
 
     item.attachment.state = state;
     this.emit("stateChanged", item);
   }
--- a/browser/devtools/profiler/test/browser_profiler_run.js
+++ b/browser/devtools/profiler/test/browser_profiler_run.js
@@ -35,33 +35,36 @@ function startRecording() {
   let record = gPanel.controls.record;
   ok(record, "Record button exists.");
   ok(!record.getAttribute("checked"), "Record button is unchecked");
 
   gPanel.once("started", () => {
     let item = gPanel.sidebar.getItemByProfile(gPanel.recordingProfile);
     is(item.attachment.name, "Profile 1");
     is(item.attachment.state, PROFILE_RUNNING);
+    is(record.getAttribute("tooltiptext"), "Stop profiling");
 
     gPanel.controller.isActive(function (err, isActive) {
       ok(isActive, "Profiler is running");
       deferred.resolve();
     });
   });
 
   record.click();
   return deferred.promise;
 }
 
 function stopRecording() {
   let deferred = promise.defer();
+  let record = gPanel.controls.record;
 
   gPanel.once("parsed", () => {
     let item = gPanel.sidebar.getItemByProfile(gPanel.activeProfile);
     is(item.attachment.state, PROFILE_COMPLETED);
+    is(record.getAttribute("tooltiptext"), "Start profiling");
 
     function assertSample() {
       let [ win, doc ] = getProfileInternals();
       let sample = doc.getElementsByClassName("samplePercentage");
 
       if (sample.length <= 0) {
         return void setTimeout(assertSample, 100);
       }
@@ -86,16 +89,17 @@ function startRecordingAgain() {
   ok(!record.getAttribute("checked"), "Record button is unchecked");
 
   gPanel.once("started", () => {
     ok(gPanel.activeProfile !== gPanel.recordingProfile);
 
     let item = gPanel.sidebar.getItemByProfile(gPanel.recordingProfile);
     is(item.attachment.name, "Profile 2");
     is(item.attachment.state, PROFILE_RUNNING);
+    is(record.getAttribute("tooltiptext"), "Stop profiling");
 
     deferred.resolve();
   });
 
   record.click();
   return deferred.promise;
 }
 
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -21,17 +21,17 @@ let promise = require("sdk/core/promise"
 let Telemetry = require("devtools/shared/telemetry");
 let TargetFactory = require("devtools/framework/target").TargetFactory;
 const escodegen = require("escodegen/escodegen");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource:///modules/source-editor.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/reflect.jsm");
 Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 
deleted file mode 100644
--- a/browser/devtools/shared/LayoutHelpers.jsm
+++ /dev/null
@@ -1,384 +0,0 @@
-/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-  "resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "PlatformKeys", function() {
-  return Services.strings.createBundle(
-    "chrome://global-platform/locale/platformKeys.properties");
-});
-
-this.EXPORTED_SYMBOLS = ["LayoutHelpers"];
-
-this.LayoutHelpers = LayoutHelpers = {
-
-  /**
-   * Compute the position and the dimensions for the visible portion
-   * of a node, relativalely to the root window.
-   *
-   * @param nsIDOMNode aNode
-   *        a DOM element to be highlighted
-   */
-  getDirtyRect: function LH_getDirectyRect(aNode) {
-    let frameWin = aNode.ownerDocument.defaultView;
-    let clientRect = aNode.getBoundingClientRect();
-
-    // Go up in the tree of frames to determine the correct rectangle.
-    // clientRect is read-only, we need to be able to change properties.
-    rect = {top: clientRect.top,
-            left: clientRect.left,
-            width: clientRect.width,
-            height: clientRect.height};
-
-    // We iterate through all the parent windows.
-    while (true) {
-
-      // Does the selection overflow on the right of its window?
-      let diffx = frameWin.innerWidth - (rect.left + rect.width);
-      if (diffx < 0) {
-        rect.width += diffx;
-      }
-
-      // Does the selection overflow on the bottom of its window?
-      let diffy = frameWin.innerHeight - (rect.top + rect.height);
-      if (diffy < 0) {
-        rect.height += diffy;
-      }
-
-      // Does the selection overflow on the left of its window?
-      if (rect.left < 0) {
-        rect.width += rect.left;
-        rect.left = 0;
-      }
-
-      // Does the selection overflow on the top of its window?
-      if (rect.top < 0) {
-        rect.height += rect.top;
-        rect.top = 0;
-      }
-
-      // Selection has been clipped to fit in its own window.
-
-      // Are we in the top-level window?
-      if (frameWin.parent === frameWin || !frameWin.frameElement) {
-        break;
-      }
-
-      // We are in an iframe.
-      // We take into account the parent iframe position and its
-      // offset (borders and padding).
-      let frameRect = frameWin.frameElement.getBoundingClientRect();
-
-      let [offsetTop, offsetLeft] =
-        this.getIframeContentOffset(frameWin.frameElement);
-
-      rect.top += frameRect.top + offsetTop;
-      rect.left += frameRect.left + offsetLeft;
-
-      frameWin = frameWin.parent;
-    }
-
-    return rect;
-  },
-
-  /**
-   * Compute the absolute position and the dimensions of a node, relativalely
-   * to the root window.
-   *
-   * @param nsIDOMNode aNode
-   *        a DOM element to get the bounds for
-   * @param nsIWindow aContentWindow
-   *        the content window holding the node
-   */
-  getRect: function LH_getRect(aNode, aContentWindow) {
-    let frameWin = aNode.ownerDocument.defaultView;
-    let clientRect = aNode.getBoundingClientRect();
-
-    // Go up in the tree of frames to determine the correct rectangle.
-    // clientRect is read-only, we need to be able to change properties.
-    rect = {top: clientRect.top + aContentWindow.pageYOffset,
-            left: clientRect.left + aContentWindow.pageXOffset,
-            width: clientRect.width,
-            height: clientRect.height};
-
-    // We iterate through all the parent windows.
-    while (true) {
-
-      // Are we in the top-level window?
-      if (frameWin.parent === frameWin || !frameWin.frameElement) {
-        break;
-      }
-
-      // We are in an iframe.
-      // We take into account the parent iframe position and its
-      // offset (borders and padding).
-      let frameRect = frameWin.frameElement.getBoundingClientRect();
-
-      let [offsetTop, offsetLeft] =
-        this.getIframeContentOffset(frameWin.frameElement);
-
-      rect.top += frameRect.top + offsetTop;
-      rect.left += frameRect.left + offsetLeft;
-
-      frameWin = frameWin.parent;
-    }
-
-    return rect;
-  },
-
-  /**
-   * Returns iframe content offset (iframe border + padding).
-   * Note: this function shouldn't need to exist, had the platform provided a
-   * suitable API for determining the offset between the iframe's content and
-   * its bounding client rect. Bug 626359 should provide us with such an API.
-   *
-   * @param aIframe
-   *        The iframe.
-   * @returns array [offsetTop, offsetLeft]
-   *          offsetTop is the distance from the top of the iframe and the
-   *            top of the content document.
-   *          offsetLeft is the distance from the left of the iframe and the
-   *            left of the content document.
-   */
-  getIframeContentOffset: function LH_getIframeContentOffset(aIframe) {
-    let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
-
-    // In some cases, the computed style is null
-    if (!style) {
-      return [0, 0];
-    }
-
-    let paddingTop = parseInt(style.getPropertyValue("padding-top"));
-    let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
-
-    let borderTop = parseInt(style.getPropertyValue("border-top-width"));
-    let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
-
-    return [borderTop + paddingTop, borderLeft + paddingLeft];
-  },
-
-  /**
-   * Apply the page zoom factor.
-   */
-  getZoomedRect: function LH_getZoomedRect(aWin, aRect) {
-    // get page zoom factor, if any
-    let zoom =
-      aWin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-        .getInterface(Components.interfaces.nsIDOMWindowUtils)
-        .fullZoom;
-
-    // adjust rect for zoom scaling
-    let aRectScaled = {};
-    for (let prop in aRect) {
-      aRectScaled[prop] = aRect[prop] * zoom;
-    }
-
-    return aRectScaled;
-  },
-
-
-  /**
-   * Find an element from the given coordinates. This method descends through
-   * frames to find the element the user clicked inside frames.
-   *
-   * @param DOMDocument aDocument the document to look into.
-   * @param integer aX
-   * @param integer aY
-   * @returns Node|null the element node found at the given coordinates.
-   */
-  getElementFromPoint: function LH_elementFromPoint(aDocument, aX, aY) {
-    let node = aDocument.elementFromPoint(aX, aY);
-    if (node && node.contentDocument) {
-      if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
-        let rect = node.getBoundingClientRect();
-
-        // Gap between the iframe and its content window.
-        let [offsetTop, offsetLeft] = LayoutHelpers.getIframeContentOffset(node);
-
-        aX -= rect.left + offsetLeft;
-        aY -= rect.top + offsetTop;
-
-        if (aX < 0 || aY < 0) {
-          // Didn't reach the content document, still over the iframe.
-          return node;
-        }
-      }
-      if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
-          node instanceof Ci.nsIDOMHTMLFrameElement) {
-        let subnode = this.getElementFromPoint(node.contentDocument, aX, aY);
-        if (subnode) {
-          node = subnode;
-        }
-      }
-    }
-    return node;
-  },
-
-  /**
-   * Scroll the document so that the element "elem" appears in the viewport.
-   *
-   * @param Element elem the element that needs to appear in the viewport.
-   * @param bool centered true if you want it centered, false if you want it to
-   * appear on the top of the viewport. It is true by default, and that is
-   * usually what you want.
-   */
-  scrollIntoViewIfNeeded:
-  function LH_scrollIntoViewIfNeeded(elem, centered) {
-    // We want to default to centering the element in the page,
-    // so as to keep the context of the element.
-    centered = centered === undefined? true: !!centered;
-
-    let win = elem.ownerDocument.defaultView;
-    let clientRect = elem.getBoundingClientRect();
-
-    // The following are always from the {top, bottom, left, right}
-    // of the viewport, to the {top, …} of the box.
-    // Think of them as geometrical vectors, it helps.
-    // The origin is at the top left.
-
-    let topToBottom = clientRect.bottom;
-    let bottomToTop = clientRect.top - win.innerHeight;
-    let leftToRight = clientRect.right;
-    let rightToLeft = clientRect.left - win.innerWidth;
-    let xAllowed = true;  // We allow one translation on the x axis,
-    let yAllowed = true;  // and one on the y axis.
-
-    // Whatever `centered` is, the behavior is the same if the box is
-    // (even partially) visible.
-
-    if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
-      win.scrollBy(0, topToBottom - elem.offsetHeight);
-      yAllowed = false;
-    } else
-    if ((bottomToTop < 0 || !centered) && bottomToTop >= -elem.offsetHeight) {
-      win.scrollBy(0, bottomToTop + elem.offsetHeight);
-      yAllowed = false;
-    }
-
-    if ((leftToRight > 0 || !centered) && leftToRight <= elem.offsetWidth) {
-      if (xAllowed) {
-        win.scrollBy(leftToRight - elem.offsetWidth, 0);
-        xAllowed = false;
-      }
-    } else
-    if ((rightToLeft < 0 || !centered) && rightToLeft >= -elem.offsetWidth) {
-      if (xAllowed) {
-        win.scrollBy(rightToLeft + elem.offsetWidth, 0);
-        xAllowed = false;
-      }
-    }
-
-    // If we want it centered, and the box is completely hidden,
-    // then we center it explicitly.
-
-    if (centered) {
-
-      if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
-        win.scroll(win.scrollX,
-                   win.scrollY + clientRect.top
-                   - (win.innerHeight - elem.offsetHeight) / 2);
-      }
-
-      if (xAllowed && (leftToRight <= 0 || rightToLeft <= 0)) {
-        win.scroll(win.scrollX + clientRect.left
-                   - (win.innerWidth - elem.offsetWidth) / 2,
-                   win.scrollY);
-      }
-    }
-
-    if (win.parent !== win) {
-      // We are inside an iframe.
-      LH_scrollIntoViewIfNeeded(win.frameElement, centered);
-    }
-  },
-
-  /**
-   * Check if a node and its document are still alive
-   * and attached to the window.
-   *
-   * @param aNode
-   */
-  isNodeConnected: function LH_isNodeConnected(aNode)
-  {
-    try {
-      let connected = (aNode.ownerDocument && aNode.ownerDocument.defaultView &&
-                      !(aNode.compareDocumentPosition(aNode.ownerDocument.documentElement) &
-                      aNode.DOCUMENT_POSITION_DISCONNECTED));
-      return connected;
-    } catch (e) {
-      // "can't access dead object" error
-      return false;
-    }
-  },
-
-  /**
-   * Prettifies the modifier keys for an element.
-   *
-   * @param Node aElemKey
-   *        The key element to get the modifiers from.
-   * @param boolean aAllowCloverleaf
-   *        Pass true to use the cloverleaf symbol instead of a descriptive string.
-   * @return string
-   *         A prettified and properly separated modifier keys string.
-   */
-  prettyKey: function LH_prettyKey(aElemKey, aAllowCloverleaf)
-  {
-    let elemString = "";
-    let elemMod = aElemKey.getAttribute("modifiers");
-
-    if (elemMod.match("accel")) {
-      if (Services.appinfo.OS == "Darwin") {
-        // XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
-        // Orion adds variable height lines.
-        if (!aAllowCloverleaf) {
-          elemString += "Cmd-";
-        } else {
-          elemString += PlatformKeys.GetStringFromName("VK_META") +
-                        PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-        }
-      } else {
-        elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
-                      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-      }
-    }
-    if (elemMod.match("access")) {
-      if (Services.appinfo.OS == "Darwin") {
-        elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
-                      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-      } else {
-        elemString += PlatformKeys.GetStringFromName("VK_ALT") +
-                      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-      }
-    }
-    if (elemMod.match("shift")) {
-      elemString += PlatformKeys.GetStringFromName("VK_SHIFT") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-    if (elemMod.match("alt")) {
-      elemString += PlatformKeys.GetStringFromName("VK_ALT") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-    if (elemMod.match("ctrl") || elemMod.match("control")) {
-      elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-    if (elemMod.match("meta")) {
-      elemString += PlatformKeys.GetStringFromName("VK_META") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-
-    return elemString +
-      (aElemKey.getAttribute("keycode").replace(/^.*VK_/, "") ||
-       aElemKey.getAttribute("key")).toUpperCase();
-  }
-};
--- a/browser/devtools/shared/test/browser_layoutHelpers.js
+++ b/browser/devtools/shared/test/browser_layoutHelpers.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that scrollIntoViewIfNeeded works properly.
 
 let imported = {};
-Components.utils.import("resource:///modules/devtools/LayoutHelpers.jsm",
+Components.utils.import("resource://gre/modules/devtools/LayoutHelpers.jsm",
     imported);
 registerCleanupFunction(function () {
   imported = {};
 });
 
 let LayoutHelpers = imported.LayoutHelpers;
 
 const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_layoutHelpers.html";
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -5,17 +5,17 @@
 let {devtools} = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
 let TiltManager = devtools.require("devtools/tilt/tilt").TiltManager;
 let TiltGL = devtools.require("devtools/tilt/tilt-gl");
 let {EPSILON, TiltMath, vec3, mat3, mat4, quat4} = devtools.require("devtools/tilt/tilt-math");
 let TiltUtils = devtools.require("devtools/tilt/tilt-utils");
 let {TiltVisualizer} = devtools.require("devtools/tilt/tilt-visualizer");
 
 let tempScope = {};
-Components.utils.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
+Components.utils.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
 
 
 const DEFAULT_HTML = "data:text/html," +
   "<DOCTYPE html>" +
   "<html>" +
     "<head>" +
       "<meta charset='utf-8'/>" +
--- a/browser/devtools/tilt/tilt-utils.js
+++ b/browser/devtools/tilt/tilt-utils.js
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 
 const STACK_THICKNESS = 15;
 
 /**
  * Module containing various helper functions used throughout Tilt.
  */
 this.TiltUtils = {};
 module.exports = this.TiltUtils;
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -441,18 +441,16 @@
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/MmsService.manifest
 @BINPATH@/components/MmsService.js
-@BINPATH@/components/SmsService.manifest
-@BINPATH@/components/SmsService.js
 @BINPATH@/components/RILContentHelper.js
 @BINPATH@/components/MobileMessageDatabaseService.manifest
 @BINPATH@/components/MobileMessageDatabaseService.js
 @BINPATH@/components/WifiWorker.js
 @BINPATH@/components/WifiWorker.manifest
 @BINPATH@/components/DOMWifiManager.js
 @BINPATH@/components/DOMWifiManager.manifest
 #endif
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -640,19 +640,20 @@ just addresses the organization to follo
 <!ENTITY social.learnMore.label "Learn more…">
 <!ENTITY social.learnMore.accesskey "l">
 <!ENTITY social.closeNotificationItem.label "Not Now">
 
 <!ENTITY social.chatBar.commandkey "c">
 <!ENTITY social.chatBar.label "Focus chats">
 <!ENTITY social.chatBar.accesskey "c">
 
-<!-- labels are set dynamically, see browser.properties -->
-<!ENTITY social.markpage.accesskey "m">
-<!ENTITY social.marklink.accesskey "M">
+<!ENTITY social.markpage.accesskey "P">
+<!ENTITY social.markpageMenu.label "Save Page To…">
+<!ENTITY social.marklink.accesskey "L">
+<!ENTITY social.marklinkMenu.label "Save Link To…">
 
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
 
 <!ENTITY webrtcIndicatorButton.label "Camera / Microphone Access">
 <!ENTITY webrtcIndicatorButton.tooltip "Display sites you are currently sharing your camera or microphone with">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -402,16 +402,17 @@ dataReportingNotification.button.label  
 dataReportingNotification.button.accessKey  = C
 
 # Webapps notification popup
 webapps.install = Install
 webapps.install.accesskey = I
 #LOCALIZATION NOTE (webapps.requestInstall) %1$S is the web app name, %2$S is the site from which the web app is installed
 webapps.requestInstall = Do you want to install "%1$S" from this site (%2$S)?
 webapps.install.success = Application Installed
+webapps.install.inprogress = Installation in progress
 
 # LOCALIZATION NOTE (fullscreen.entered): displayed when we enter HTML5 fullscreen mode, %S is the domain name of the focused website (e.g. mozilla.com).
 fullscreen.entered=%S is now fullscreen.
 # LOCALIZATION NOTE (fullscreen.rememberDecision): displayed when we enter HTML5 fullscreen mode, %S is the domain name of the focused website (e.g. mozilla.com).
 fullscreen.rememberDecision=Remember decision for %S
 
 service.toolbarbutton.label=Services
 service.toolbarbutton.tooltiptext=Services
@@ -425,21 +426,19 @@ service.install.learnmore=Learn More…
 # LOCALIZATION NOTE (social.turnOff.label): %S is the name of the social provider
 social.turnOff.label=Turn off %S
 social.turnOff.accesskey=T
 # LOCALIZATION NOTE (social.turnOn.label): %S is the name of the social provider
 social.turnOn.label=Turn on %S
 social.turnOn.accesskey=T
 
 # LOCALIZATION NOTE (social.markpage.label): %S is the name of the social provider
-social.markpage.label=Send Page to %S
-social.unmarkpage.label=Remove Page from %S
+social.markpage.label=Save Page to %S
 # LOCALIZATION NOTE (social.marklink.label): %S is the name of the social provider
-social.marklink.label=Send Link to %S
-social.unmarklink.label=Remove Link from %S
+social.marklink.label=Save Link to %S
 
 # LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
 social.error.message=%1$S is unable to connect with %2$S right now.
 social.error.tryAgain.label=Try Again
 social.error.tryAgain.accesskey=T
 social.error.closeSidebar.label=Close This Sidebar
 social.error.closeSidebar.accesskey=C
 
--- a/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/app-manager.properties
@@ -1,23 +1,33 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+# LOCALIZATION NOTE (device.deviceSize): %1$S is the device's width, %2$S is
+# the device's height, %3$S is the device's pixel density.
+# Example: 800x480 (86 DPI).
 device.deviceSize=Device size: %1$Sx%2$S (%3$S DPI)
+# LOCALIZATION NOTE (connection.connectedToDevice, connection.connectTo):
+# %1$S is the host name, %2$S is the port number.
 connection.connectedToDevice=Connected to %1$S
 connection.connectTo=Connect to %1$S:%2$S
 project.filePickerTitle=Select a webapp folder
-
 validator.nonExistingFolder=The project folder doesn't exists
 validator.expectProjectFolder=The project folder ends up being a file
 validator.wrongManifestFileName=Packaged apps require a manifest file that can only be named 'manifest.webapp' at project root folder
 validator.invalidManifestURL=Invalid manifest URL '%S'
+# LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
+# %1$S is the error message, %2$S is the URI of the manifest.
 validator.invalidManifestJSON=The webapp manifest isn't a valid JSON file: %1$S at: %2$S
 validator.noAccessManifestURL=Unable to read manifest file: %1$S at: %2$S
+# LOCALIZATION NOTE (validator.invalidHostedManifestURL): %1$S is the URI of
+# the manifest, %2$S is the error message.
 validator.invalidHostedManifestURL=Invalid hosted manifest URL '%1$S': %2$S
 validator.invalidProjectType=Unknown project type '%S'
+# LOCALIZATION NOTE (validator.missNameManifestProperty, validator.missIconsManifestProperty):
+# don't translate 'icons' and 'name'.
 validator.missNameManifestProperty=Missing mandatory 'name' in Manifest.
 validator.missIconsManifestProperty=Missing 'icons' in Manifest.
-validator.missIconForMarketplace=app submission to the Marketplace needs at least an 128 icon
+validator.missIconMarketplace=app submission to the Marketplace needs at least a 128px icon
 validator.invalidAppType=Unknown app type: '%S'.
 validator.invalidHostedPriviledges=Hosted App can't be type '%S'.
 validator.noCertifiedSupport='certified' apps are not fully supported on the App manager.
--- a/browser/locales/en-US/chrome/browser/devtools/profiler.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/profiler.dtd
@@ -7,9 +7,13 @@
 <!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
   - keep it in English, or another language commonly spoken among web developers.
   - You want to make that choice consistent across the developer tools.
   - A good criteria is the language in which you'd find the best
   - documentation on web development on the web. -->
 
 <!-- LOCALIZATION NOTE (profiler.importProfile): This string is displayed
   -  on a button that opens a dialog to import a saved profile data file. -->
-<!ENTITY importProfile.label "Import…">
\ No newline at end of file
+<!ENTITY importProfile.label "Import…">
+
+<!-- LOCALIZATION NOTE (profiler.startProfiler): This string is displayed
+  -  on a button that starts a new profile  -->
+<!ENTITY startProfiler.tooltip "Start profiling">
--- a/browser/locales/en-US/chrome/browser/devtools/profiler.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/profiler.properties
@@ -84,20 +84,30 @@ profiler.stateRunning=Running
 profiler.stateCompleted=Completed
 
 # LOCALIZATION NOTE (profiler.sidebarNotice)
 # This string is displayed in the profiler sidebar when there are no
 # existing profiles to show (usually happens when the user opens the
 # profiler for the first time).
 profiler.sidebarNotice=There are no profiles yet.
 
+# LOCALIZATION NOTE (profiler.startProfilerString)
+# This string is displayed on the profiler button when no active
+# profiling sessions are running
+profiler.startProfilerString=Start profiling
+
+# LOCALIZATION NOTE (profiler.stopProfilerString)
+# This string is displayed on the profiler button when an active
+# profiling session is running
+profiler.stopProfilerString=Stop profiling
+
 # LOCALIZATION NOTE (profiler.save)
 # This string is displayed as a label for a button that opens a Save File
 # dialog where user can save generated profiler to a file.
 profiler.save=Save
 
 # LOCALIZATION NOTE (profiler.saveFileAs)
 # This string as a title for a Save File dialog.
 profiler.saveFileAs=Save Profile As
 
 # LOCALIZATION NOTE (profiler.openFile)
 # This string as a title for a Open File dialog.
-profiler.openFile=Import Profile
\ No newline at end of file
+profiler.openFile=Import Profile
--- a/browser/metro/base/content/apzc.js
+++ b/browser/metro/base/content/apzc.js
@@ -10,16 +10,17 @@ let Cr = Components.results;
 
 /**
  * Handler for APZC display port and pan begin/end notifications.
  * These notifications are only sent by widget/windows/winrt code when
  * the pref: layers.async-pan-zoom.enabled is true.
  */
 
 var APZCObserver = {
+  _debugEvents: false,
   init: function() {
     this._enabled = Services.prefs.getBoolPref(kAsyncPanZoomEnabled);
     if (!this._enabled) {
       return;
     }
 
     let os = Services.obs;
     os.addObserver(this, "apzc-request-content-repaint", false);
@@ -90,35 +91,33 @@ var APZCObserver = {
         x: displayPort.x + scrollTo.x,
         y: displayPort.y + scrollTo.y,
         w: displayPort.width,
         h: displayPort.height,
         scale: resolution,
         id: scrollId
       });
 
-      Util.dumpLn("APZC scrollId: " + scrollId);
-      Util.dumpLn("APZC scrollTo.x: " + scrollTo.x + ", scrollTo.y: " + scrollTo.y);
-      Util.dumpLn("APZC setResolution: " + resolution);
-      Util.dumpLn("APZC setDisplayPortForElement: displayPort.x: " +
-                  displayPort.x + ", displayPort.y: " + displayPort.y +
-                  ", displayPort.width: " + displayPort.width +
-                  ", displayort.height: " + displayPort.height);
+      if (this._debugEvents) {
+        Util.dumpLn("APZC scrollId: " + scrollId);
+        Util.dumpLn("APZC scrollTo.x: " + scrollTo.x + ", scrollTo.y: " + scrollTo.y);
+        Util.dumpLn("APZC setResolution: " + resolution);
+        Util.dumpLn("APZC setDisplayPortForElement: displayPort.x: " +
+                    displayPort.x + ", displayPort.y: " + displayPort.y +
+                    ", displayPort.width: " + displayPort.width +
+                    ", displayort.height: " + displayPort.height);
+      }
     } else if (aTopic == "apzc-handle-pan-begin") {
       // When we're panning, hide the main scrollbars by setting imprecise
       // input (which sets a property on the browser which hides the scrollbar
       // via CSS).  This reduces jittering from left to right. We may be able
       // to get rid of this once we implement axis locking in /gfx APZC.
-      Util.dumpLn("APZC pan-begin");
       if (InputSourceHelper.isPrecise) {
         InputSourceHelper._imprecise();
       }
-
-    } else if (aTopic == "apzc-handle-pan-end") {
-      Util.dumpLn("APZC pan-end");
     }
   },
 
   receiveMessage: function(aMessage) {
     let json = aMessage.json;
     switch (aMessage.name) {
       case "scroll": {
         let data = json.viewId + " " + json.presShellId + " (" + json.scrollOffset.x + ", " + json.scrollOffset.y + ")";
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -29,17 +29,17 @@ pref("metro.debug.selection.dumpRanges",
 pref("metro.debug.selection.dumpEvents", false);
 
 // Enable tab-modal prompts
 pref("prompts.tab_modal.enabled", true);
 
 
 // Enable off main thread compositing
 pref("layers.offmainthreadcomposition.enabled", true);
-pref("layers.async-pan-zoom.enabled", false);
+pref("layers.async-pan-zoom.enabled", true);
 pref("layers.componentalpha.enabled", false);
 pref("gfx.azpc.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
 pref("gfx.axis.fling_friction", "0.002");
 
 // Enable Microsoft TSF support by default for imes.
 pref("intl.enable_tsf_support", true);
 
 pref("general.autoScroll", true);
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -1,20 +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/. */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = ["Social", "OpenGraphBuilder"];
+this.EXPORTED_SYMBOLS = ["Social", "OpenGraphBuilder", "DynamicResizeWatcher", "sizeSocialPanelToContent"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
+// The minimum sizes for the auto-resize panel code.
+const PANEL_MIN_HEIGHT = 100;
+const PANEL_MIN_WIDTH = 330;
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
   "resource://gre/modules/SocialService.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
@@ -298,114 +302,72 @@ this.Social = {
     if (!oldProvider && this.providers.length)
       oldProvider = this.providers[0];
     this.provider = oldProvider;
     if (provider)
       SocialService.removeProvider(origin);
   },
 
   // Page Marking functionality
-  _getMarkablePageUrl: function Social_getMarkablePageUrl(aURI) {
-    let uri = aURI.clone();
-    try {
-      // Setting userPass on about:config throws.
-      uri.userPass = "";
-    } catch (e) {}
-    return uri.spec;
-  },
-
-  isURIMarked: function(aURI, aCallback) {
+  isURIMarked: function(origin, aURI, aCallback) {
     promiseGetAnnotation(aURI).then(function(val) {
       if (val) {
         let providerList = JSON.parse(val);
-        val = providerList.indexOf(this.provider.origin) >= 0;
+        val = providerList.indexOf(origin) >= 0;
       }
       aCallback(!!val);
-    }.bind(this));
+    }).then(null, Cu.reportError);
   },
 
-  markURI: function(aURI, aCallback) {
-    // this should not be called if this.provider or the port is null
-    if (!this.provider) {
-      Cu.reportError("Can't mark a page when no provider is current");
-      return;
-    }
-    let port = this.provider.getWorkerPort();
-    if (!port) {
-      Cu.reportError("Can't mark page as no provider port is available");
-      return;
-    }
-
+  markURI: function(origin, aURI, aCallback) {
     // update or set our annotation
     promiseGetAnnotation(aURI).then(function(val) {
 
       let providerList = val ? JSON.parse(val) : [];
-      let marked = providerList.indexOf(this.provider.origin) >= 0;
+      let marked = providerList.indexOf(origin) >= 0;
       if (marked)
         return;
-      providerList.push(this.provider.origin);
+      providerList.push(origin);
       // we allow marking links in a page that may not have been visited yet.
       // make sure there is a history entry for the uri, then annotate it.
       let place = {
         uri: aURI,
         visits: [{
           visitDate: Date.now() + 1000,
           transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
         }]
       };
       PlacesUtils.asyncHistory.updatePlaces(place, {
         handleError: function () Cu.reportError("couldn't update history for socialmark annotation"),
         handleResult: function () {},
         handleCompletion: function () {
-          promiseSetAnnotation(aURI, providerList).then();
-          // post to the provider
-          let url = this._getMarkablePageUrl(aURI);
-          port.postMessage({
-            topic: "social.page-mark",
-            data: { url: url, 'marked': true }
-          });
-          port.close();
-          if (aCallback)
-            schedule(function() { aCallback(true); } );
-        }.bind(this)
+          promiseSetAnnotation(aURI, providerList).then(function() {
+            if (aCallback)
+              schedule(function() { aCallback(true); } );
+          }).then(null, Cu.reportError);
+        }
       });
-    }.bind(this));
+    }).then(null, Cu.reportError);
   },
-  
-  unmarkURI: function(aURI, aCallback) {
+
+  unmarkURI: function(origin, aURI, aCallback) {
     // this should not be called if this.provider or the port is null
-    if (!this.provider) {
-      Cu.reportError("Can't mark a page when no provider is current");
-      return;
-    }
-    let port = this.provider.getWorkerPort();
-    if (!port) {
-      Cu.reportError("Can't mark page as no provider port is available");
-      return;
-    }
-
     // set our annotation
     promiseGetAnnotation(aURI).then(function(val) {
       let providerList = val ? JSON.parse(val) : [];
-      let marked = providerList.indexOf(this.provider.origin) >= 0;
+      let marked = providerList.indexOf(origin) >= 0;
       if (marked) {
         // remove the annotation
-        providerList.splice(providerList.indexOf(this.provider.origin), 1);
-        promiseSetAnnotation(aURI, providerList).then();
+        providerList.splice(providerList.indexOf(origin), 1);
+        promiseSetAnnotation(aURI, providerList).then(function() {
+          if (aCallback)
+            schedule(function() { aCallback(false); } );
+        }).then(null, Cu.reportError);
       }
-      // post to the provider regardless
-      let url = this._getMarkablePageUrl(aURI);
-      port.postMessage({
-        topic: "social.page-mark",
-        data: { url: url, 'marked': false }
-      });
-      port.close();
-      if (aCallback)
-        schedule(function() { aCallback(false); } );
-    }.bind(this));
+    }).then(null, Cu.reportError);
   },
 
   setErrorListener: function(iframe, errorHandler) {
     if (iframe.socialErrorListener)
       return iframe.socialErrorListener;
     return new SocialErrorListener(iframe, errorHandler);
   }
 };
@@ -476,17 +438,118 @@ SocialErrorListener.prototype = {
   },
 
   onProgressChange: function SPL_onProgressChange() {},
   onStatusChange: function SPL_onStatusChange() {},
   onSecurityChange: function SPL_onSecurityChange() {},
 };
 
 
+function sizeSocialPanelToContent(panel, iframe) {
+  let doc = iframe.contentDocument;
+  if (!doc || !doc.body) {
+    return;
+  }
+  // We need an element to use for sizing our panel.  See if the body defines
+  // an id for that element, otherwise use the body itself.
+  let body = doc.body;
+  let bodyId = body.getAttribute("contentid");
+  if (bodyId) {
+    body = doc.getElementById(bodyId) || doc.body;
+  }
+  // offsetHeight/Width don't include margins, so account for that.
+  let cs = doc.defaultView.getComputedStyle(body);
+  let width = PANEL_MIN_WIDTH;
+  let height = PANEL_MIN_HEIGHT;
+  // if the panel is preloaded prior to being shown, cs will be null.  in that
+  // case use the minimum size for the panel until it is shown.
+  if (cs) {
+    let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
+    height = Math.max(computedHeight, height);
+    let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
+    width = Math.max(computedWidth, width);
+  }
+  iframe.style.width = width + "px";
+  iframe.style.height = height + "px";
+  // since we do not use panel.sizeTo, we need to adjust the arrow ourselves
+  if (panel.state == "open")
+    panel.adjustArrowPosition();
+}
+
+function DynamicResizeWatcher() {
+  this._mutationObserver = null;
+}
+
+DynamicResizeWatcher.prototype = {
+  start: function DynamicResizeWatcher_start(panel, iframe) {
+    this.stop(); // just in case...
+    let doc = iframe.contentDocument;
+    this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
+      sizeSocialPanelToContent(panel, iframe);
+    });
+    // Observe anything that causes the size to change.
+    let config = {attributes: true, characterData: true, childList: true, subtree: true};
+    this._mutationObserver.observe(doc, config);
+    // and since this may be setup after the load event has fired we do an
+    // initial resize now.
+    sizeSocialPanelToContent(panel, iframe);
+  },
+  stop: function DynamicResizeWatcher_stop() {
+    if (this._mutationObserver) {
+      try {
+        this._mutationObserver.disconnect();
+      } catch (ex) {
+        // may get "TypeError: can't access dead object" which seems strange,
+        // but doesn't seem to indicate a real problem, so ignore it...
+      }
+      this._mutationObserver = null;
+    }
+  }
+}
+
+
 this.OpenGraphBuilder = {
+  generateEndpointURL: function(URLTemplate, pageData) {
+    // support for existing oexchange style endpoints by supporting their
+    // querystring arguments. parse the query string template and do
+    // replacements where necessary the query names may be different than ours,
+    // so we could see u=%{url} or url=%{url}
+    let [endpointURL, queryString] = URLTemplate.split("?");
+    let query = {};
+    if (queryString) {
+      queryString.split('&').forEach(function (val) {
+        let [name, value] = val.split('=');
+        let p = /%\{(.+)\}/.exec(value);
+        if (!p) {
+          // preserve non-template query vars
+          query[name] = value;
+        } else if (pageData[p[1]]) {
+          query[name] = pageData[p[1]];
+        } else if (p[1] == "body") {
+          // build a body for emailers
+          let body = "";
+          if (pageData.title)
+            body += pageData.title + "\n\n";
+          if (pageData.description)
+            body += pageData.description + "\n\n";
+          if (pageData.text)
+            body += pageData.text + "\n\n";
+          body += pageData.url;
+          query["body"] = body;
+        }
+      });
+    }
+    var str = [];
+    for (let p in query)
+       str.push(p + "=" + encodeURIComponent(query[p]));
+    if (str.length)
+      endpointURL = endpointURL + "?" + str.join("&");
+    return endpointURL;
+  },
+
   getData: function(browser) {
     let res = {
       url: this._validateURL(browser, browser.currentURI.spec),
       title: browser.contentDocument.title,
       previews: []
     };
     this._getMetaData(browser, res);
     this._getLinkData(browser, res);
--- a/browser/modules/webappsUI.jsm
+++ b/browser/modules/webappsUI.jsm
@@ -112,23 +112,41 @@ this.webappsUI = {
     }
   },
 
   doInstall: function(aData, aWindow) {
     let browser = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIWebNavigation)
                          .QueryInterface(Ci.nsIDocShell)
                          .chromeEventHandler;
-    let chromeWin = browser.ownerDocument.defaultView;
+    let chromeDoc = browser.ownerDocument;
+    let chromeWin = chromeDoc.defaultView;
+    let popupProgressContent =
+      chromeDoc.getElementById("webapps-install-progress-content");
+
     let bundle = chromeWin.gNavigatorBundle;
 
+    let notification;
+
     let mainAction = {
       label: bundle.getString("webapps.install"),
       accessKey: bundle.getString("webapps.install.accesskey"),
       callback: () => {
+        notification.remove();
+
+        notification = chromeWin.PopupNotifications.
+                        show(browser,
+                             "webapps-install-progress",
+                             bundle.getString("webapps.install.inprogress"),
+                             "webapps-notification-icon");
+
+        let progressMeter = chromeDoc.createElement("progressmeter");
+        progressMeter.setAttribute("mode", "undetermined");
+        popupProgressContent.appendChild(progressMeter);
+
         let manifestURL = aData.app.manifestURL;
         if (aData.app.manifest && aData.app.manifest.appcache_path) {
           this.downloads[manifestURL] = Promise.defer();
         }
 
         let app = WebappsInstaller.init(aData);
 
         if (app) {
@@ -145,16 +163,18 @@ this.webappsUI = {
                   if (this.downloads[manifestURL]) {
                     yield this.downloads[manifestURL].promise;
                   }
                   installationSuccessNotification(aData, app, bundle);
                 } catch (ex) {
                   Cu.reportError("Error installing webapp: " + ex);
                   // TODO: Notify user that the installation has failed
                 } finally {
+                  popupProgressContent.removeChild(progressMeter);
+                  notification.remove();
                   delete this.downloads[manifestURL];
                 }
               }.bind(this));
             });
         } else {
           DOMApplicationRegistry.denyInstall(aData);
         }
       }
@@ -169,18 +189,21 @@ this.webappsUI = {
       host = requestingURI.host;
     } catch(e) {
       host = requestingURI.spec;
     }
 
     let message = bundle.getFormattedString("webapps.requestInstall",
                                             [manifest.name, host], 2);
 
-    chromeWin.PopupNotifications.show(browser, "webapps-install", message,
-                                    "webapps-notification-icon", mainAction);
+    notification = chromeWin.PopupNotifications.show(browser,
+                                                     "webapps-install",
+                                                     message,
+                                                     "webapps-notification-icon",
+                                                     mainAction);
 
   }
 }
 
 function installationSuccessNotification(aData, app, aBundle) {
   let launcher = {
     observe: function(aSubject, aTopic) {
       if (aTopic == "alertclickcallback") {
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1179,16 +1179,17 @@ toolbar[iconsize="small"] #webrtc-status
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install-progress"],
 .popup-notification-icon[popupid="webapps-install"] {
   list-style-image: url(chrome://global/skin/icons/webapps-64.png);
 }
 
 .popup-notification-icon[popupid="mixed-content-blocked"] {
   list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
 }
 
--- a/browser/themes/linux/devtools/debugger.css
+++ b/browser/themes/linux/devtools/debugger.css
@@ -134,16 +134,23 @@
 
 .dbg-breakpoint-line {
   font-weight: 600;
 }
 
 .dbg-breakpoint-text {
   -moz-margin-start: 10px !important;
   font-style: italic;
+  font-size: 90%;
+}
+
+.dbg-breakpoint-checkbox {
+  width: 16px;
+  height: 16px;
+  margin: 2px;
 }
 
 /* Watch expressions view */
 
 #expressions {
   min-height: 10px;
   max-height: 125px;
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3400,16 +3400,17 @@ toolbarbutton.chevron > .toolbarbutton-m
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install-progress"],
 .popup-notification-icon[popupid="webapps-install"] {
   list-style-image: url(chrome://global/skin/icons/webapps-64.png);
 }
 
 .popup-notification-icon[popupid="mixed-content-blocked"] {
   list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
 }
 @media (min-resolution: 2dppx) {
--- a/browser/themes/osx/devtools/debugger.css
+++ b/browser/themes/osx/devtools/debugger.css
@@ -132,16 +132,23 @@
 
 .dbg-breakpoint-line {
   font-weight: 600;
 }
 
 .dbg-breakpoint-text {
   -moz-margin-start: 10px !important;
   font-style: italic;
+  font-size: 90%;
+}
+
+.dbg-breakpoint-checkbox {
+  width: 16px;
+  height: 16px;
+  margin: 2px;
 }
 
 /* Watch expressions view */
 
 #expressions {
   min-height: 10px;
   max-height: 125px;
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2453,16 +2453,17 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
 }
 
+.popup-notification-icon[popupid="webapps-install-progress"],
 .popup-notification-icon[popupid="webapps-install"] {
   list-style-image: url(chrome://global/skin/icons/webapps-64.png);
 }
 
 .popup-notification-icon[popupid="mixed-content-blocked"] {
   list-style-image: url(chrome://browser/skin/mixed-content-blocked-64.png);
 }
 
--- a/browser/themes/windows/devtools/debugger.css
+++ b/browser/themes/windows/devtools/debugger.css
@@ -130,16 +130,23 @@
 
 .dbg-breakpoint-line {
   font-weight: 600;
 }
 
 .dbg-breakpoint-text {
   -moz-margin-start: 10px !important;
   font-style: italic;
+  font-size: 90%;
+}
+
+.dbg-breakpoint-checkbox {
+  width: 16px;
+  height: 16px;
+  margin: 2px;
 }
 
 /* Watch expressions view */
 
 #expressions {
   min-height: 10px;
   max-height: 125px;
 }
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -0,0 +1,76 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors;
+
+import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
+import org.mozilla.gecko.annotationProcessors.utils.GeneratableEntryPointIterator;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class AnnotationProcessor {
+    public static final String OUTFILE = "GeneratedJNIWrappers.cpp";
+    public static final String HEADERFILE = "GeneratedJNIWrappers.h";
+
+    public static void main(String[] args) {
+        // We expect a list of jars on the commandline. If missing, whinge about it.
+        if (args.length <= 1) {
+            System.err.println("Usage: java AnnotationProcessor jarfiles ...");
+            System.exit(1);
+        }
+
+        System.out.println("Processing annotations...");
+
+        // We want to produce the same output as last time as often as possible. Ordering of
+        // generated statements, therefore, needs to be consistent.
+        Arrays.sort(args);
+
+        // Start the clock!
+        long s = System.currentTimeMillis();
+
+        // Get an iterator over the classes in the jar files given...
+        Iterator<Class<?>> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
+
+        CodeGenerator generatorInstance = new CodeGenerator();
+
+        while (jarClassIterator.hasNext()) {
+            Class<?> aClass = jarClassIterator.next();
+
+            // Get an iterator over the appropriately generated methods of this class
+            Iterator<MethodWithAnnotationInfo> methodIterator = new GeneratableEntryPointIterator(aClass.getDeclaredMethods());
+
+            // Iterate all annotated methods in this class..
+            while (methodIterator.hasNext()) {
+                MethodWithAnnotationInfo aMethodTuple = methodIterator.next();
+                generatorInstance.generateMethod(aMethodTuple, aClass);
+            }
+
+        }
+
+        writeOutputFiles(generatorInstance);
+        long e = System.currentTimeMillis();
+        System.out.println("Annotation processing complete in " + (e - s) + "ms");
+    }
+
+    private static void writeOutputFiles(CodeGenerator aGenerator) {
+        try {
+            FileOutputStream outStream = new FileOutputStream(OUTFILE);
+            outStream.write(aGenerator.getWrapperFileContents());
+        } catch (IOException e) {
+            System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
+            e.printStackTrace(System.err);
+        }
+
+        try {
+            FileOutputStream headerStream = new FileOutputStream(HEADERFILE);
+            headerStream.write(aGenerator.getHeaderFileContents());
+        } catch (IOException e) {
+            System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
+            e.printStackTrace(System.err);
+        }
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -0,0 +1,341 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors;
+
+import org.mozilla.gecko.annotationProcessors.utils.Utils;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class CodeGenerator {
+    // Buffers holding the strings to ultimately be written to the output files.
+    private final StringBuilder wrapperStartupCode = new StringBuilder();
+    private final StringBuilder wrapperMethodBodies = new StringBuilder();
+    private final StringBuilder headerFields = new StringBuilder();
+    private final StringBuilder headerMethods = new StringBuilder();
+
+    private final HashSet<String> seenClasses = new HashSet<String>();
+
+    private final String GENERATED_COMMENT = "// GENERATED CODE\n" +
+            "// Generated by the Java program at /build/annotationProcessors at compile time from\n" +
+            "// annotations on Java methods. To update, change the annotations on the corresponding Java\n" +
+            "// methods and rerun the build. Manually updating this file will cause your build to fail.\n\n";
+
+    public CodeGenerator() {
+        // Write the file header things. Includes and so forth.
+        // GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with
+        // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerFields
+        // with headerMethods.
+        wrapperStartupCode.append(GENERATED_COMMENT);
+        wrapperStartupCode.append(
+                "#include \"nsXPCOMStrings.h\"\n" +
+                "#include \"AndroidBridge.h\"\n" +
+                "#include \"AndroidBridgeUtilities.h\"\n" +
+                "\n" +
+                "#ifdef DEBUG\n" +
+                "#define ALOG_BRIDGE(args...) ALOG(args)\n" +
+                "#else\n" +
+                "#define ALOG_BRIDGE(args...) ((void)0)\n" +
+                "#endif\n" +
+                "\n" +
+                "using namespace mozilla;\n" +
+                "void AndroidBridge::InitStubs(JNIEnv *jEnv) {\n" +
+                "    ALOG_BRIDGE(\"%s\", __PRETTY_FUNCTION__);\n" +
+                "    initInit();\n");
+        // Now we write the various GetStaticMethodID calls here...
+
+        headerFields.append("protected:\n\n");
+        headerMethods.append(GENERATED_COMMENT);
+        headerMethods.append("public:\n\n");
+    }
+
+    /**
+     * Append the appropriate generated code to the buffers for the method provided.
+     *
+     * @param aMethodTuple The Java method, plus the name for the generated method.
+     * @param aClass       The class to which the Java method belongs.
+     */
+    public void generateMethod(MethodWithAnnotationInfo aMethodTuple, Class<?> aClass) {
+        // Unpack the tuple and extract some useful fields from the Method..
+        Method aMethod = aMethodTuple.method;
+        String CMethodName = aMethodTuple.wrapperName;
+
+        String javaMethodName = aMethod.getName();
+
+        ensureClassHeaderAndStartup(aClass);
+
+        writeHeaderField(CMethodName);
+        writeStartupCode(CMethodName, javaMethodName, aMethod, aClass);
+
+        // Get the C++ method signature for this method.
+        String implementationSignature = Utils.getCImplementationMethodSignature(aMethod, CMethodName);
+        String headerSignature = Utils.getCHeaderMethodSignature(aMethod, CMethodName, aMethodTuple.isStatic);
+
+        // Add the header signature to the header file.
+        headerMethods.append(headerSignature);
+        headerMethods.append(";\n");
+
+        // Use the implementation signature to generate the method body...
+        writeMethodBody(implementationSignature, CMethodName, aMethod, aClass, aMethodTuple.isStatic, aMethodTuple.isMultithreaded);
+    }
+
+    /**
+     * Writes the appropriate header and startup code to ensure the existence of a reference to the
+     * class specified. If this is already done, does nothing.
+     *
+     * @param aClass The target class.
+     */
+    private void ensureClassHeaderAndStartup(Class<?> aClass) {
+        String className = aClass.getCanonicalName();
+        if (seenClasses.contains(className)) {
+            return;
+        }
+
+        // Add a field to hold the reference...
+        headerFields.append("\njclass ");
+        headerFields.append(Utils.getClassReferenceName(aClass));
+        headerFields.append(";\n");
+
+        // Add startup code to populate it..
+        wrapperStartupCode.append('\n');
+        wrapperStartupCode.append(Utils.getStartupLineForClass(aClass));
+
+        seenClasses.add(className);
+    }
+
+    /**
+     * Generates the method body of the C++ wrapper function for the Java method indicated.
+     *
+     * @param methodSignature The previously-generated C++ method signature for the method to be
+     *                        generated.
+     * @param aCMethodName    The C++ method name for the method to be generated.
+     * @param aMethod         The Java method to be wrapped by the C++ method being generated.
+     * @param aClass          The Java class to which the method belongs.
+     */
+    private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod, Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded) {
+        Class<?>[] argumentTypes = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
+        wrapperMethodBodies.append('\n');
+        wrapperMethodBodies.append(methodSignature);
+
+        wrapperMethodBodies.append(" {\n" +
+                                   "    ALOG_BRIDGE(\"%s\", __PRETTY_FUNCTION__);\n");
+
+        // Static stubs check the bridge instance has been created before trying to run.
+        if (aIsStaticBridgeMethod) {
+            wrapperMethodBodies.append("    if (!sBridge) {\n" +
+                                       "        ALOG_BRIDGE(\"Aborted: No sBridge - %s\", __PRETTY_FUNCTION__);\n" +
+                                       "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                       "    }\n\n");
+        }
+        wrapperMethodBodies.append("    JNIEnv *env = ");
+        if (!aIsMultithreaded) {
+            wrapperMethodBodies.append("GetJNIEnv();\n");
+        } else {
+            wrapperMethodBodies.append("GetJNIForThread();\n");
+        }
+        wrapperMethodBodies.append("    if (!env) {\n" +
+                                   "        ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                   "    }\n\n");
+
+        boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.doesReturnObjectType(aMethod);
+
+        // Determine the number of local refs required for our local frame..
+        // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
+        int localReferencesNeeded = Utils.enumerateReferenceArguments(aMethod);
+        if (isObjectReturningMethod) {
+            localReferencesNeeded++;
+        }
+        wrapperMethodBodies.append("    if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n" +
+                                   "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        env->ExceptionDescribe();\n"+
+                                   "        env->ExceptionClear();\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                   "    }\n\n");
+
+        // Marshall arguments, if we have any.
+        boolean hasArguments = argumentTypes.length != 0;
+
+        // We buffer the arguments to the call separately to avoid needing to repeatedly iterate the
+        // argument list while building this line. In the coming code block, we simultaneously
+        // construct any argument marshalling code (Creation of jstrings, placement of arguments
+        // into an argument array, etc. and the actual argument list passed to the function (in
+        // argumentContent).
+        StringBuilder argumentContent = new StringBuilder();
+        if (hasArguments) {
+            argumentContent.append(", ");
+            // If we have >2 arguments, use the jvalue[] calling approach.
+            if (argumentTypes.length > 2) {
+                wrapperMethodBodies.append("    jvalue args[").append(argumentTypes.length).append("];\n");
+                for (int aT = 0; aT < argumentTypes.length; aT++) {
+                    wrapperMethodBodies.append("    args[").append(aT).append("].");
+                    wrapperMethodBodies.append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
+                }
+
+                // The only argument is the array of arguments.
+                argumentContent.append("args");
+                wrapperMethodBodies.append('\n');
+            } else {
+                // Otherwise, use the vanilla calling approach.
+                boolean needsNewline = false;
+                for (int aT = 0; aT < argumentTypes.length; aT++) {
+                    // If the argument is a string-esque type, create a jstring from it, otherwise
+                    // it can be passed directly.
+                    if (Utils.isCharSequence(argumentTypes[aT])) {
+                        wrapperMethodBodies.append("    jstring j").append(aT).append(" = NewJavaString(env, a").append(aT).append(");\n");
+                        needsNewline = true;
+                        // Ensure we refer to the newly constructed Java string - not to the original
+                        // parameter to the wrapper function.
+                        argumentContent.append('j').append(aT);
+                    } else {
+                        argumentContent.append('a').append(aT);
+                    }
+                    if (aT != argumentTypes.length - 1) {
+                        argumentContent.append(", ");
+                    }
+                }
+                if (needsNewline) {
+                    wrapperMethodBodies.append('\n');
+                }
+            }
+        }
+
+        wrapperMethodBodies.append("    ");
+        if (!returnType.getCanonicalName().equals("void")) {
+            if (isObjectReturningMethod) {
+                wrapperMethodBodies.append("jobject");
+            } else {
+                wrapperMethodBodies.append(Utils.getCReturnType(returnType));
+            }
+            wrapperMethodBodies.append(" temp = ");
+        }
+
+        boolean isStaticJavaMethod = Utils.isMethodStatic(aMethod);
+
+        // The call into Java
+        wrapperMethodBodies.append("env->");
+        wrapperMethodBodies.append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
+        if (argumentTypes.length > 2) {
+            wrapperMethodBodies.append('A');
+        }
+
+        wrapperMethodBodies.append('(');
+        // If the underlying Java method is nonstatic, we provide the target object to the JNI.
+        if (!isStaticJavaMethod) {
+            wrapperMethodBodies.append("aTarget, ");
+        } else {
+            // If the stub to be generated is static, we need to use the singleton to access the class
+            // reference.
+            if (aIsStaticBridgeMethod) {
+                wrapperMethodBodies.append("sBridge->");
+            }
+            // If this is a static underlyin Java method, we need to use the class reference in our
+            // call.
+            wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", ");
+        }
+
+        // Write the method id out..
+        if (aIsStaticBridgeMethod) {
+            wrapperMethodBodies.append("sBridge->");
+        }
+        wrapperMethodBodies.append('j');
+        wrapperMethodBodies.append(aCMethodName);
+
+        // Tack on the arguments, if any..
+        wrapperMethodBodies.append(argumentContent);
+        wrapperMethodBodies.append(");\n\n");
+
+        // Check for exception and return the failure value..
+        wrapperMethodBodies.append("    if (env->ExceptionCheck()) {\n" +
+                                   "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        env->ExceptionDescribe();\n" +
+                                   "        env->ExceptionClear();\n" +
+                                   "        env->PopLocalFrame(NULL);\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                                   "    }\n");
+
+        // If we're returning an object, pop the callee's stack frame extracting our ref as the return
+        // value.
+        if (isObjectReturningMethod) {
+            wrapperMethodBodies.append("    ");
+            wrapperMethodBodies.append(Utils.getCReturnType(returnType));
+            wrapperMethodBodies.append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
+                                       "    ALOG_BRIDGE(\"Exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                       "    return ret;\n");
+        } else if (!returnType.getCanonicalName().equals("void")) {
+            // If we're a primitive-returning function, just return the directly-obtained primative
+            // from the call to Java.
+            wrapperMethodBodies.append("    env->PopLocalFrame(NULL);\n" +
+                                       "    ALOG_BRIDGE(\"Exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                       "    return temp;\n");
+        } else {
+            // If we don't return anything, just pop the stack frame and move on with life.
+            wrapperMethodBodies.append("    ALOG_BRIDGE(\"Exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                                       "    env->PopLocalFrame(NULL);\n");
+        }
+        wrapperMethodBodies.append("}\n");
+    }
+
+    /**
+     * Generates the code to get the method id of the given method on startup.
+     *
+     * @param aCMethodName    The C method name of the method being generated.
+     * @param aJavaMethodName The name of the Java method being wrapped.
+     * @param aMethod         The Java method being wrapped.
+     */
+    private void writeStartupCode(String aCMethodName, String aJavaMethodName, Method aMethod, Class<?> aClass) {
+        wrapperStartupCode.append("    j");
+        wrapperStartupCode.append(aCMethodName);
+        wrapperStartupCode.append(" = get");
+        if (Utils.isMethodStatic(aMethod)) {
+            wrapperStartupCode.append("Static");
+        }
+        wrapperStartupCode.append("Method(\"");
+        wrapperStartupCode.append(aJavaMethodName);
+        wrapperStartupCode.append("\", \"");
+        wrapperStartupCode.append(Utils.getTypeSignatureString(aMethod));
+        wrapperStartupCode.append("\");\n");
+    }
+
+    /**
+     * Create a method id field in the header file for the C method name provided.
+     *
+     * @param aMethodName C method name to generate a method id field for.
+     */
+    private void writeHeaderField(String aMethodName) {
+        headerFields.append("jmethodID j");
+        headerFields.append(aMethodName);
+        headerFields.append(";\n");
+    }
+
+    /**
+     * Get the finalised bytes to go into the generated wrappers file.
+     *
+     * @return The bytes to be written to the wrappers file.
+     */
+    public byte[] getWrapperFileContents() {
+        wrapperStartupCode.append("}\n");
+        wrapperStartupCode.append(wrapperMethodBodies);
+        wrapperMethodBodies.setLength(0);
+        return wrapperStartupCode.toString().getBytes();
+    }
+
+    /**
+     * Get the finalised bytes to go into the generated header file.
+     *
+     * @return The bytes to be written to the header file.
+     */
+    public byte[] getHeaderFileContents() {
+        headerFields.append('\n');
+        headerFields.append(headerMethods);
+        headerMethods.setLength(0);
+        return headerFields.toString().getBytes();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/MethodWithAnnotationInfo.java
@@ -0,0 +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/. */
+
+package org.mozilla.gecko.annotationProcessors;
+
+import java.lang.reflect.Method;
+
+/**
+ * Object holding method and annotation. Used by GeneratableEntryPointIterator.
+ */
+public class MethodWithAnnotationInfo {
+    public final Method method;
+    public final String wrapperName;
+    public final boolean isStatic;
+    public final boolean isMultithreaded;
+
+    public MethodWithAnnotationInfo(Method aMethod, String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded) {
+        method = aMethod;
+        wrapperName = aWrapperName;
+        isStatic = aIsStatic;
+        isMultithreaded = aIsMultithreaded;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.annotationProcessors.classloader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * A classloader which can be initialised with a list of jar files and which can provide an iterator
+ * over the top level classes in the jar files it was initialised with.
+ * classNames is kept sorted to ensure iteration order is consistent across program invocations.
+ * Otherwise, we'd forever be reporting the outdatedness of the generated code as we permute its
+ * contents.
+ * This classloader does not support inner classes. (You probably shouldn't be putting JNI entry
+ * points in inner classes anyway)
+ */
+public class IterableJarLoadingURLClassLoader extends URLClassLoader {
+    LinkedList<String> classNames = new LinkedList<String>();
+
+    /**
+     * Create an instance and return its iterator. Provides an iterator over the classes in the jar
+     * files provided as arguments.
+     * Inner classes are not supported.
+     *
+     * @param args A list of jar file names an iterator over the classes of which is desired.
+     * @return An iterator over the top level classes in the jar files provided, in arbitrary order.
+     */
+    public static Iterator<Class<?>> getIteratorOverJars(String[] args) {
+        URL[] urlArray = new URL[args.length];
+        LinkedList<String> aClassNames = new LinkedList<String>();
+
+        for (int i = 0; i < args.length; i++) {
+            try {
+                urlArray[i] = (new File(args[i])).toURI().toURL();
+
+                Enumeration<JarEntry> entries = new JarFile(args[i]).entries();
+                while (entries.hasMoreElements()) {
+                    JarEntry e = entries.nextElement();
+                    String fName = e.getName();
+                    if (!fName.endsWith(".class")) {
+                        continue;
+                    }
+                    final String className = fName.substring(0, fName.length() - 6).replace('/', '.');
+                    // Inner classes are not supported.
+                    if (className.contains("$")) {
+                        continue;
+                    }
+                    aClassNames.add(className);
+                }
+            } catch (IOException e) {
+                System.err.println("Error loading jar file \"" + args[i] + '"');
+                e.printStackTrace(System.err);
+            }
+        }
+        Collections.sort(aClassNames);
+        return new JarClassIterator(new IterableJarLoadingURLClassLoader(urlArray, aClassNames));
+    }
+
+    /**
+     * Constructs a classloader capable of loading all classes given as URLs in urls. Used by static
+     * method above.
+     *
+     * @param urls        URLs for all classes the new instance shall be capable of loading.
+     * @param aClassNames A list of names of the classes this instance shall be capable of loading.
+     */
+    protected IterableJarLoadingURLClassLoader(URL[] urls, LinkedList<String> aClassNames) {// Array to populate with URLs for each class in the given jars.
+        super(urls);
+        classNames = aClassNames;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/classloader/JarClassIterator.java
@@ -0,0 +1,43 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.classloader;
+
+import java.util.Iterator;
+
+/**
+ * Class for iterating over an IterableJarLoadingURLClassLoader's classes.
+ */
+public class JarClassIterator implements Iterator<Class<?>> {
+    private IterableJarLoadingURLClassLoader mTarget;
+    private Iterator<String> mTargetClassListIterator;
+
+    public JarClassIterator(IterableJarLoadingURLClassLoader aTarget) {
+        mTarget = aTarget;
+        mTargetClassListIterator = aTarget.classNames.iterator();
+    }
+
+    @Override
+    public boolean hasNext() {
+        return mTargetClassListIterator.hasNext();
+    }
+
+    @Override
+    public Class<?> next() {
+        String className = mTargetClassListIterator.next();
+        try {
+            return mTarget.loadClass(className);
+        } catch (ClassNotFoundException e) {
+            System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
+            e.printStackTrace();
+            System.exit(2);
+        }
+        return null;
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException("Removal of classes from iterator not supported.");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/utils/AlphabeticMethodComparator.java
@@ -0,0 +1,28 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.utils;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+public class AlphabeticMethodComparator implements Comparator<Method> {
+    @Override
+    public int compare(Method lhs, Method rhs) {
+        // Initially, attempt to differentiate the methods be name alone..
+        String lName = lhs.getName();
+        String rName = rhs.getName();
+
+        int ret = lName.compareTo(rName);
+        if (ret != 0) {
+            return ret;
+        }
+
+        // The names were the same, so we need to compare signatures to find their uniqueness..
+        lName = Utils.getTypeSignatureString(lhs);
+        rName = Utils.getTypeSignatureString(rhs);
+
+        return lName.compareTo(rName);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/utils/GeneratableEntryPointIterator.java
@@ -0,0 +1,111 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.utils;
+
+import org.mozilla.gecko.annotationProcessors.MethodWithAnnotationInfo;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Iterator over the methods in a given method list which have the GeneratableAndroidBridgeTarget
+ * annotation. Returns an object containing both the annotation (Which may contain interesting
+ * parameters) and the argument.
+ */
+public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotationInfo> {
+    private final Method[] mMethods;
+    private MethodWithAnnotationInfo mNextReturnValue;
+    private int mMethodIndex;
+
+    public GeneratableEntryPointIterator(Method[] aMethods) {
+        // Sort the methods into alphabetical order by name, to ensure we always iterate methods
+        // in the same order..
+        Arrays.sort(aMethods, new AlphabeticMethodComparator());
+        mMethods = aMethods;
+
+        findNextValue();
+    }
+
+    /**
+     * Find and cache the next appropriately annotated method, plus the annotation parameter, if
+     * one exists. Otherwise cache null, so hasNext returns false.
+     */
+    private void findNextValue() {
+        while (mMethodIndex < mMethods.length) {
+            Method candidateMethod = mMethods[mMethodIndex];
+            mMethodIndex++;
+            for (Annotation annotation : candidateMethod.getDeclaredAnnotations()) {
+                // GeneratableAndroidBridgeTarget has a parameter. Use Reflection to obtain it.
+                Class<? extends Annotation> annotationType = annotation.annotationType();
+                final String annotationTypeName = annotationType.getName();
+
+                if (annotationTypeName.equals("org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget")) {
+                    String stubName = null;
+                    boolean isStaticStub = false;
+                    boolean isMultithreadedStub = false;
+                    try {
+                        // Determine the explicitly-given name of the stub to generate, if any.
+                        final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
+                        stubNameMethod.setAccessible(true);
+                        stubName = (String) stubNameMethod.invoke(annotation);
+
+                        // Detemine if the generated stub should be static.
+                        final Method staticStubMethod = annotationType.getDeclaredMethod("generateStatic");
+                        staticStubMethod.setAccessible(true);
+                        isStaticStub = (Boolean) staticStubMethod.invoke(annotation);
+
+                        // Determine if the generated stub is to allow calls from multiple threads.
+                        final Method multithreadedStubMethod = annotationType.getDeclaredMethod("allowMultithread");
+                        multithreadedStubMethod.setAccessible(true);
+                        isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation);
+                    } catch (NoSuchMethodException e) {
+                        System.err.println("Unable to find expected field on GeneratableAndroidBridgeTarget annotation. Did the signature change?");
+                        e.printStackTrace(System.err);
+                        System.exit(3);
+                    } catch (IllegalAccessException e) {
+                        System.err.println("IllegalAccessException reading fields on GeneratableAndroidBridgeTarget annotation. Seems the semantics of Reflection have changed...");
+                        e.printStackTrace(System.err);
+                        System.exit(4);
+                    } catch (InvocationTargetException e) {
+                        System.err.println("InvocationTargetException reading fields on GeneratableAndroidBridgeTarget annotation. This really shouldn't happen.");
+                        e.printStackTrace(System.err);
+                        System.exit(5);
+                    }
+                    // If the method name was not explicitly given in the annotation generate one...
+                    if (stubName.isEmpty()) {
+                        String aMethodName = candidateMethod.getName();
+                        stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1);
+                    }
+
+                    mNextReturnValue = new MethodWithAnnotationInfo(candidateMethod, stubName, isStaticStub, isMultithreadedStub);
+                    return;
+                }
+            }
+        }
+        mNextReturnValue = null;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return mNextReturnValue != null;
+    }
+
+    @Override
+    public MethodWithAnnotationInfo next() {
+        MethodWithAnnotationInfo ret = mNextReturnValue;
+        findNextValue();
+        return ret;
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException("Removal of methods from GeneratableEntryPointIterator not supported.");
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/utils/Utils.java
@@ -0,0 +1,530 @@
+/* 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/. */
+
+package org.mozilla.gecko.annotationProcessors.utils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+
+/**
+ * A collection of utility methods used by CodeGenerator. Largely used for translating types.
+ */
+public class Utils {
+
+    // A collection of lookup tables to simplify the functions to follow...
+    private static final HashMap<String, String> sBasicCTypes = new HashMap<String, String>();
+
+    static {
+        sBasicCTypes.put("void", "void");
+        sBasicCTypes.put("int", "int32_t");
+        sBasicCTypes.put("boolean", "bool");
+        sBasicCTypes.put("long", "int64_t");
+        sBasicCTypes.put("double", "jdouble");
+        sBasicCTypes.put("float", "jfloat");
+        sBasicCTypes.put("char", "uint16_t");
+        sBasicCTypes.put("byte", "int8_t");
+        sBasicCTypes.put("short", "int16_t");
+    }
+
+    private static final HashMap<String, String> sArrayCTypes = new HashMap<String, String>();
+
+    static {
+        sArrayCTypes.put("int", "jintArray");
+        sArrayCTypes.put("boolean", "jbooleanArray");
+        sArrayCTypes.put("long", "jlongArray");
+        sArrayCTypes.put("double", "jdoubleArray");
+        sArrayCTypes.put("float", "jfloatArray");
+        sArrayCTypes.put("char", "jcharArray");
+        sArrayCTypes.put("byte", "jbyteArray");
+        sArrayCTypes.put("short", "jshortArray");
+    }
+
+    private static final HashMap<String, String> sStaticCallTypes = new HashMap<String, String>();
+
+    static {
+        sStaticCallTypes.put("void", "CallStaticVoidMethod");
+        sStaticCallTypes.put("int", "CallStaticIntMethod");
+        sStaticCallTypes.put("boolean", "CallStaticBooleanMethod");
+        sStaticCallTypes.put("long", "CallStaticLongMethod");
+        sStaticCallTypes.put("double", "CallStaticDoubleMethod");
+        sStaticCallTypes.put("float", "CallStaticFloatMethod");
+        sStaticCallTypes.put("char", "CallStaticCharMethod");
+        sStaticCallTypes.put("byte", "CallStaticByteMethod");
+        sStaticCallTypes.put("short", "CallStaticShortMethod");
+    }
+
+    private static final HashMap<String, String> sInstanceCallTypes = new HashMap<String, String>();
+
+    static {
+        sInstanceCallTypes.put("void", "CallVoidMethod");
+        sInstanceCallTypes.put("int", "CallIntMethod");
+        sInstanceCallTypes.put("boolean", "CallBooleanMethod");
+        sInstanceCallTypes.put("long", "CallLongMethod");
+        sInstanceCallTypes.put("double", "CallDoubleMethod");
+        sInstanceCallTypes.put("float", "CallFloatMethod");
+        sInstanceCallTypes.put("char", "CallCharMethod");
+        sInstanceCallTypes.put("byte", "CallByteMethod");
+        sInstanceCallTypes.put("short", "CallShortMethod");
+    }
+
+    private static final HashMap<String, String> sFailureReturns = new HashMap<String, String>();
+
+    static {
+        sFailureReturns.put("void", "");
+        sFailureReturns.put("int", " 0");
+        sFailureReturns.put("boolean", " false");
+        sFailureReturns.put("long", " 0");
+        sFailureReturns.put("double", " 0.0");
+        sFailureReturns.put("float", " 0.0");
+        sFailureReturns.put("char", " 0");
+        sFailureReturns.put("byte", " 0");
+        sFailureReturns.put("short", " 0");
+    }
+
+    private static final HashMap<String, String> sCanonicalSignatureParts = new HashMap<String, String>();
+
+    static {
+        sCanonicalSignatureParts.put("void", "V");
+        sCanonicalSignatureParts.put("int", "I");
+        sCanonicalSignatureParts.put("boolean", "Z");
+        sCanonicalSignatureParts.put("long", "J");
+        sCanonicalSignatureParts.put("double", "D");
+        sCanonicalSignatureParts.put("float", "F");
+        sCanonicalSignatureParts.put("char", "C");
+        sCanonicalSignatureParts.put("byte", "B");
+        sCanonicalSignatureParts.put("short", "S");
+    }
+
+
+    private static final HashMap<String, String> sDefaultParameterValues = new HashMap<String, String>();
+
+    static {
+        sDefaultParameterValues.put("int", "0");
+        sDefaultParameterValues.put("boolean", "false");
+        sDefaultParameterValues.put("long", "0");
+        sDefaultParameterValues.put("double", "0");
+        sDefaultParameterValues.put("float", "0.0");
+        sDefaultParameterValues.put("char", "0");
+        sDefaultParameterValues.put("byte", "0");
+        sDefaultParameterValues.put("short", "0");
+    }
+
+    /**
+     * Get the C type corresponding to the provided type parameter. Used for generating argument
+     * types for the wrapper method.
+     *
+     * @param type Class to determine the corresponding JNI type for.
+     * @return true if the type an object type, false otherwise.
+     */
+    public static String getCParameterType(Class<?> type) {
+        String name = type.getCanonicalName();
+        if (sBasicCTypes.containsKey(name)) {
+            return sBasicCTypes.get(name);
+        }
+        // Are we dealing with an array type?
+        int len = name.length();
+        if (name.endsWith("[]")) {
+            // Determine if it is a 2D array - these map to jobjectArrays
+            name = name.substring(0, len - 2);
+            if (name.endsWith("[]")) {
+                return "jobjectArray";
+            } else {
+                // Which flavour of Array is it?
+                if (sArrayCTypes.containsKey(name)) {
+                    return sArrayCTypes.get(name);
+                }
+                return "jobjectArray";
+            }
+        }
+        // Not an array type, check the remaining possibilities before we fall back to jobject
+
+        // Check for CharSequences (Strings and things that are string-like)
+        if (isCharSequence(type)) {
+            return "const nsAString&";
+        }
+
+        if (name.equals("java.lang.Class")) {
+            // You're doing reflection on Java objects from inside C, returning Class objects
+            // to C, generating the corresponding code using this Java program. Really?!
+            return "jclass";
+        }
+        if (name.equals("java.lang.Throwable")) {
+            return "jthrowable";
+        }
+        return "jobject";
+    }
+
+    /**
+     * For a given Java type, get the corresponding C++ type if we're returning it from a function.
+     *
+     * @param type The Java return type.
+     * @return A string representation of the C++ return type.
+     */
+    public static String getCReturnType(Class<?> type) {
+        // Since there's only one thing we want to do differently...
+        String cParameterType = getCParameterType(type);
+        if (cParameterType.equals("const nsAString&")) {
+            return "jstring";
+        } else {
+            return cParameterType;
+        }
+    }
+
+    /**
+     * Gets the appropriate JNI call function to use to invoke a Java method with the given return
+     * type. This, plus a call postfix (Such as "A") forms a complete JNI call function name.
+     *
+     * @param aReturnType The Java return type of the method being generated.
+     * @param isStatic Boolean indicating if the underlying Java method is declared static.
+     * @return A string representation of the JNI call function prefix to use.
+     */
+    public static String getCallPrefix(Class<?> aReturnType, boolean isStatic) {
+        String name = aReturnType.getCanonicalName();
+        if (isStatic) {
+            if (sStaticCallTypes.containsKey(name)) {
+                return sStaticCallTypes.get(name);
+            }
+            return "CallStaticObjectMethod";
+        } else {
+            if (sInstanceCallTypes.containsKey(name)) {
+                return sInstanceCallTypes.get(name);
+            }
+            return "CallObjectMethod";
+        }
+    }
+
+    /**
+     * On failure, the generated method returns a null-esque value. This helper method gets the
+     * appropriate failure return value for a given Java return type, plus a leading space.
+     *
+     * @param type Java return type of method being generated
+     * @return String representation of the failure return value to be used in the generated code.
+     */
+    public static String getFailureReturnForType(Class<?> type) {
+        String name = type.getCanonicalName();
+        if (sFailureReturns.containsKey(name)) {
+            return sFailureReturns.get(name);
+        }
+        return " nullptr";
+    }
+
+    /**
+     * Get the canonical JNI type signature for a method.
+     *
+     * @param aMethod The method to generate a signature for.
+     * @return The canonical JNI type signature for this method.
+     */
+    public static String getTypeSignatureString(Method aMethod) {
+        Class<?>[] arguments = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('(');
+        // For each argument, write its signature component to the buffer..
+        for (int i = 0; i < arguments.length; i++) {
+            writeTypeSignature(sb, arguments[i]);
+        }
+        sb.append(')');
+        // Write the return value's signature..
+        writeTypeSignature(sb, returnType);
+        return sb.toString();
+    }
+
+    /**
+     * Helper method used by getTypeSignatureString to build the signature. Write the subsignature
+     * of a given type into the buffer.
+     *
+     * @param sb The buffer to write into.
+     * @param c  The type of the element to write the subsignature of.
+     */
+    private static void writeTypeSignature(StringBuilder sb, Class<?> c) {
+        String name = c.getCanonicalName().replaceAll("\\.", "/");
+        // Determine if this is an array type and, if so, peel away the array operators..
+        int len = name.length();
+        while (name.endsWith("[]")) {
+            sb.append('[');
+            name = name.substring(0, len - 2);
+        }
+
+        // Look in the hashmap for the remainder...
+        if (sCanonicalSignatureParts.containsKey(name)) {
+            // It was a primitive type, so lookup was a success.
+            sb.append(sCanonicalSignatureParts.get(name));
+        } else {
+            // It was a reference type - generate.
+            sb.append('L');
+            sb.append(name);
+            sb.append(';');
+        }
+    }
+
+    /**
+     * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
+     * generating header files and method bodies.
+     *
+     * @param aMethod      The Java method to generate the corresponding wrapper signature for.
+     * @param aCMethodName The name of the generated method this is to be the signatgure for.
+     * @return The generated method signature.
+     */
+    public static String getCImplementationMethodSignature(Method aMethod, String aCMethodName) {
+        Class<?>[] argumentTypes = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        StringBuilder retBuffer = new StringBuilder();
+        // Write return type..
+        retBuffer.append(getCReturnType(returnType));
+        retBuffer.append(" AndroidBridge::");
+        retBuffer.append(aCMethodName);
+        retBuffer.append('(');
+
+        // For an instance method, the first argument is the target object.
+        if (!isMethodStatic(aMethod)) {
+            retBuffer.append("jobject aTarget");
+            if (argumentTypes.length > 0) {
+                retBuffer.append(", ");
+            }
+        }
+
+        // Write argument types...
+        for (int aT = 0; aT < argumentTypes.length; aT++) {
+            retBuffer.append(getCParameterType(argumentTypes[aT]));
+            retBuffer.append(" a");
+            // We, imaginatively, call our arguments a1, a2, a3...
+            // The only way to preserve the names from Java would be to parse the
+            // Java source, which would be computationally hard.
+            retBuffer.append(aT);
+            if (aT != argumentTypes.length - 1) {
+                retBuffer.append(", ");
+            }
+        }
+        retBuffer.append(')');
+        return retBuffer.toString();
+    }
+
+    /**
+     * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
+     * generating header files and method bodies.
+     *
+     * @param aMethod      The Java method to generate the corresponding wrapper signature for.
+     * @param aCMethodName The name of the generated method this is to be the signatgure for.
+     * @return The generated method signature.
+     */
+    public static String getCHeaderMethodSignature(Method aMethod, String aCMethodName, boolean aIsStaticStub) {
+        Class<?>[] argumentTypes = aMethod.getParameterTypes();
+
+        // The annotations on the parameters of this method, in declaration order.
+        // Importantly - the same order as those in argumentTypes.
+        Annotation[][] argumentAnnotations = aMethod.getParameterAnnotations();
+        Class<?> returnType = aMethod.getReturnType();
+
+        StringBuilder retBuffer = new StringBuilder();
+
+        // Add the static keyword, if applicable.
+        if (aIsStaticStub) {
+            retBuffer.append("static ");
+        }
+
+        // Write return type..
+        retBuffer.append(getCReturnType(returnType));
+        retBuffer.append(' ');
+        retBuffer.append(aCMethodName);
+        retBuffer.append('(');
+
+        // For an instance method, the first argument is the target object.
+        if (!isMethodStatic(aMethod)) {
+            retBuffer.append("jobject aTarget");
+            if (argumentTypes.length > 0) {
+                retBuffer.append(", ");
+            }
+        }
+
+        // Write argument types...
+        for (int aT = 0; aT < argumentTypes.length; aT++) {
+            retBuffer.append(getCParameterType(argumentTypes[aT]));
+            retBuffer.append(" a");
+            // We, imaginatively, call our arguments a1, a2, a3...
+            // The only way to preserve the names from Java would be to parse the
+            // Java source, which would be computationally hard.
+            retBuffer.append(aT);
+
+            // Append the default value, if there is one..
+            retBuffer.append(getDefaultValueString(argumentTypes[aT], argumentAnnotations[aT]));
+
+            if (aT != argumentTypes.length - 1) {
+                retBuffer.append(", ");
+            }
+        }
+        retBuffer.append(')');
+        return retBuffer.toString();
+    }
+
+    /**
+     * If the given Annotation[] contains an OptionalGeneratedParameter annotation then return a
+     * string assigning an argument of type aArgumentType to the default value for that type.
+     * Otherwise, return the empty string.
+     *
+     * @param aArgumentType        The type of the argument to consider.
+     * @param aArgumentAnnotations The annotations on the argument to consider.
+     * @return An appropriate string to append to the signature of this argument assigning it to a
+     *         default value (Or not, as applicable).
+     */
+    public static String getDefaultValueString(Class<?> aArgumentType, Annotation[] aArgumentAnnotations) {
+        for (int i = 0; i < aArgumentAnnotations.length; i++) {
+            Class<? extends Annotation> annotationType = aArgumentAnnotations[i].annotationType();
+            final String annotationTypeName = annotationType.getName();
+            if (annotationTypeName.equals("org.mozilla.gecko.mozglue.OptionalGeneratedParameter")) {
+                return " = " + getDefaultParameterValueForType(aArgumentType);
+            }
+        }
+        return "";
+    }
+
+    /**
+     * Helper method to return an appropriate default parameter value for an argument of a given type.
+     * The lookup table contains values for primitive types and strings. All other object types default
+     * to null pointers.
+     *
+     * @param aArgumentType The parameter type for which a default value is desired.
+     * @return An appropriate string representation of the default value selected, for use in generated
+     *         C++ code.
+     */
+    private static String getDefaultParameterValueForType(Class<?> aArgumentType) {
+        String typeName = aArgumentType.getCanonicalName();
+        if (sDefaultParameterValues.containsKey(typeName)) {
+            return sDefaultParameterValues.get(typeName);
+        } else if (isCharSequence(aArgumentType)) {
+            return "EmptyString()";
+        } else {
+            return "nullptr";
+        }
+    }
+
+    /**
+     * Helper method that returns the number of reference types in the arguments of m.
+     *
+     * @param m The method to consider.
+     * @return How many of the arguments of m are nonprimitive.
+     */
+    public static int enumerateReferenceArguments(Method m) {
+        int ret = 0;
+        Class<?>[] args = m.getParameterTypes();
+        for (int i = 0; i < args.length; i++) {
+            String name = args[i].getCanonicalName();
+            if (!sBasicCTypes.containsKey(name)) {
+                ret++;
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Helper method that returns true iff the given method has a string argument.
+     *
+     * @param m The method to consider.
+     * @return True if the given method has a string argument, false otherwise.
+     */
+    public static boolean hasStringArgument(Method m) {
+        Class<?>[] args = m.getParameterTypes();
+        for (int i = 0; i < args.length; i++) {
+            if (isCharSequence(args[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Write the argument array assignment line for the given argument type. Does not support array
+     * types.
+     *
+     * @param type    Type of this argument according to the target Java method's signature.
+     * @param argName Wrapper function argument name corresponding to this argument.
+     */
+    public static String getArrayArgumentMashallingLine(Class<?> type, String argName) {
+        StringBuilder sb = new StringBuilder();
+
+        String name = type.getCanonicalName();
+        if (sCanonicalSignatureParts.containsKey(name)) {
+            sb.append(sCanonicalSignatureParts.get(name).toLowerCase());
+            sb.append(" = ").append(argName).append(";\n");
+        } else {
+            if (isCharSequence(type)) {
+                sb.append("l = NewJavaString(env, ").append(argName).append(");\n");
+            } else {
+                sb.append("l = ").append(argName).append(";\n");
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Returns true if the method provided returns an object type. Returns false if it returns a
+     * primitive type.
+     *
+     * @param aMethod The method to consider.
+     * @return true if the method provided returns an object type, false otherwise.
+     */
+    public static boolean doesReturnObjectType(Method aMethod) {
+        Class<?> returnType = aMethod.getReturnType();
+        return !sBasicCTypes.containsKey(returnType.getCanonicalName());
+    }
+
+    /**
+     * For a given Java class, get the name of the value in C++ which holds a reference to it.
+     *
+     * @param aClass Target Java class.
+     * @return The name of the C++ jclass entity referencing the given class.
+     */
+    public static String getClassReferenceName(Class<?> aClass) {
+        String className = aClass.getSimpleName();
+        return 'm' + className + "Class";
+    }
+
+    /**
+     * Generate a line to get a global reference to the Java class given.
+     *
+     * @param aClass The target Java class.
+     * @return The generated code to populate the reference to the class.
+     */
+    public static String getStartupLineForClass(Class<?> aClass) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("    ");
+        sb.append(getClassReferenceName(aClass));
+        sb.append(" = getClassGlobalRef(\"");
+        sb.append(aClass.getCanonicalName().replaceAll("\\.", "/"));
+        sb.append("\");\n");
+        return sb.toString();
+    }
+
+    /**
+     * Helper method to determine if this object implements CharSequence
+     * @param aClass Class to check for CharSequence-esqueness
+     * @return True if the given class implements CharSequence, false otherwise.
+     */
+    public static boolean isCharSequence(Class aClass) {
+        if (aClass.getCanonicalName().equals("java.lang.CharSequence")) {
+            return true;
+        }
+        Class[] interfaces = aClass.getInterfaces();
+        for (Class c : interfaces) {
+            if (c.getCanonicalName().equals("java.lang.CharSequence")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Helper method to read the modifier bits of the given method to determine if it is static.
+     * @param aMethod The Method to check.
+     * @return true of the method is declared static, false otherwise.
+     */
+    public static boolean isMethodStatic(Method aMethod) {
+        int aMethodModifiers = aMethod.getModifiers();
+        return Modifier.isStatic(aMethodModifiers);
+    }
+}
--- a/build/virtualenv/populate_virtualenv.py
+++ b/build/virtualenv/populate_virtualenv.py
@@ -8,36 +8,20 @@
 from __future__ import print_function, unicode_literals
 
 import distutils.sysconfig
 import os
 import shutil
 import subprocess
 import sys
 
-from distutils.version import StrictVersion
-
 
 # Minimum version of Python required to build.
-MINIMUM_PYTHON_VERSION = StrictVersion('2.7.3')
 MINIMUM_PYTHON_MAJOR = 2
-
-
-UPGRADE_WINDOWS = '''
-Please upgrade to the latest MozillaBuild development environments. See
-https://developer.mozilla.org/en-US/docs/Developer_Guide/Build_Instructions/Windows_Prerequisites
-'''.lstrip()
-
-UPGRADE_OTHER = '''
-Run |mach bootstrap| to ensure your system is up to date.
-
-If you still receive this error, your shell environment is likely detecting
-another Python version. Ensure a modern Python can be found in the paths
-defined by the $PATH environment variable and try again.
-'''.lstrip()
+MINIMUM_PYTHON_MINOR = 7
 
 
 class VirtualenvManager(object):
     """Contains logic for managing virtualenvs for building the tree."""
 
     def __init__(self, topsrcdir, topobjdir, virtualenv_path, log_handle,
         manifest_path):
         """Create a new manager.
@@ -354,30 +338,23 @@ class VirtualenvManager(object):
         and call .ensure() and .activate() to make the virtualenv active.
         """
 
         execfile(self.activate_path, dict(__file__=self.activate_path))
 
 
 def verify_python_version(log_handle):
     """Ensure the current version of Python is sufficient."""
-    major, minor, micro = sys.version_info[:3]
-
-    our = StrictVersion('%d.%d.%d' % (major, minor, micro))
+    major, minor = sys.version_info[:2]
 
-    if major != MINIMUM_PYTHON_MAJOR or our < MINIMUM_PYTHON_VERSION:
-        log_handle.write('Python %s or greater (but not Python 3) is '
-            'required to build. ' % MINIMUM_PYTHON_VERSION)
-        log_handle.write('You are running Python %s.\n' % our)
-
-        if os.name in ('nt', 'ce'):
-            log_handle.write(UPGRADE_WINDOWS)
-        else:
-            log_handle.write(UPGRADE_OTHER)
-
+    if major != MINIMUM_PYTHON_MAJOR or minor < MINIMUM_PYTHON_MINOR:
+        log_handle.write('Python %d.%d or greater (but not Python 3) is '
+            'required to build. ' %
+            (MINIMUM_PYTHON_MAJOR, MINIMUM_PYTHON_MINOR))
+        log_handle.write('You are running Python %d.%d.\n' % (major, minor))
         sys.exit(1)
 
 
 if __name__ == '__main__':
     if len(sys.argv) < 4:
         print('Usage: populate_virtualenv.py /path/to/topsrcdir /path/to/topobjdir /path/to/virtualenv')
         sys.exit(1)
 
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -483,19 +483,16 @@ private:
 
     nsresult
     InitPolicies();
 
     nsresult
     InitDomainPolicy(JSContext* cx, const char* aPolicyName,
                      DomainPolicy* aDomainPolicy);
 
-    // JS strings we need to clean up on shutdown
-    static jsid sEnabledID;
-
     inline void
     ScriptSecurityPrefChanged();
 
     nsObjectHashtable* mOriginToPolicyMap;
     DomainPolicy* mDefaultPolicy;
     nsObjectHashtable* mCapabilities;
 
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -72,16 +72,28 @@ using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
 nsIIOService    *nsScriptSecurityManager::sIOService = nullptr;
 nsIStringBundle *nsScriptSecurityManager::sStrBundle = nullptr;
 JSRuntime       *nsScriptSecurityManager::sRuntime   = 0;
 bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
 
+// Lazily initialized. Use the getter below.
+static jsid sEnabledID = JSID_VOID;
+static jsid
+EnabledID()
+{
+    if (sEnabledID != JSID_VOID)
+        return sEnabledID;
+    AutoSafeJSContext cx;
+    sEnabledID = INTERNED_STRING_TO_JSID(cx, JS_InternString(cx, "enabled"));
+    return sEnabledID;
+}
+
 bool
 nsScriptSecurityManager::SubjectIsPrivileged()
 {
     JSContext *cx = GetCurrentJSContext();
     if (cx && xpc::IsUniversalXPConnectEnabled(cx))
         return true;
     bool isSystem = false;
     return NS_SUCCEEDED(SubjectPrincipalIsSystem(&isSystem)) && isSystem;
@@ -1441,17 +1453,17 @@ nsScriptSecurityManager::CheckLoadURIWit
             return NS_OK;
         }
 
         // Now check capability policies
         static const char loadURIPrefGroup[] = "checkloaduri";
         ClassInfoData nameData(nullptr, loadURIPrefGroup);
 
         SecurityLevel secLevel;
-        rv = LookupPolicy(aPrincipal, nameData, sEnabledID,
+        rv = LookupPolicy(aPrincipal, nameData, EnabledID(),
                           nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
                           nullptr, &secLevel);
         if (NS_SUCCEEDED(rv) && secLevel.level == SCRIPT_SECURITY_ALL_ACCESS)
         {
             // OK for this site!
             return NS_OK;
         }
 
@@ -1728,17 +1740,17 @@ nsScriptSecurityManager::CanExecuteScrip
     if (!*result)
         return NS_OK; // Do not run scripts
 
     //-- Check for a per-site policy
     static const char jsPrefGroupName[] = "javascript";
     ClassInfoData nameData(nullptr, jsPrefGroupName);
 
     SecurityLevel secLevel;
-    rv = LookupPolicy(aPrincipal, nameData, sEnabledID,
+    rv = LookupPolicy(aPrincipal, nameData, EnabledID(),
                       nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
                       nullptr, &secLevel);
     if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS)
     {
         *result = false;
         return rv;
     }
 
@@ -2325,24 +2337,16 @@ nsScriptSecurityManager::nsScriptSecurit
 {
     static_assert(sizeof(intptr_t) == sizeof(void*),
                   "intptr_t and void* have different lengths on this platform. "
                   "This may cause a security failure with the SecurityLevel union.");
 }
 
 nsresult nsScriptSecurityManager::Init()
 {
-    JSContext* cx = GetSafeJSContext();
-    if (!cx) return NS_ERROR_FAILURE;   // this can happen of xpt loading fails
-    
-    ::JS_BeginRequest(cx);
-    if (sEnabledID == JSID_VOID)
-        sEnabledID = INTERNED_STRING_TO_JSID(cx, ::JS_InternString(cx, "enabled"));
-    ::JS_EndRequest(cx);
-
     InitPrefs();
 
     nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIStringBundleService> bundleService =
         mozilla::services::GetStringBundleService();
     if (!bundleService)
@@ -2373,18 +2377,16 @@ nsresult nsScriptSecurityManager::Init()
 
     JS_SetTrustedPrincipals(sRuntime, system);
 
     return NS_OK;
 }
 
 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
 
-jsid nsScriptSecurityManager::sEnabledID   = JSID_VOID;
-
 nsScriptSecurityManager::~nsScriptSecurityManager(void)
 {
     Preferences::RemoveObservers(this, kObservedPrefs);
     delete mOriginToPolicyMap;
     if(mDefaultPolicy)
         mDefaultPolicy->Drop();
     delete mCapabilities;
 }
--- a/content/base/public/nsHostObjectProtocolHandler.h
+++ b/content/base/public/nsHostObjectProtocolHandler.h
@@ -4,26 +4,26 @@
 
 #ifndef nsHostObjectProtocolHandler_h
 #define nsHostObjectProtocolHandler_h
 
 #include "mozilla/Attributes.h"
 #include "nsIProtocolHandler.h"
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
+#include "nsIInputStream.h"
 
 #define BLOBURI_SCHEME "blob"
 #define MEDIASTREAMURI_SCHEME "mediastream"
 #define MEDIASOURCEURI_SCHEME "mediasource"
 #define FONTTABLEURI_SCHEME "moz-fonttable"
 
 class nsIDOMBlob;
 class nsIDOMMediaStream;
 class nsIPrincipal;
-class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 class MediaSource;
 }
 }
 
 class nsHostObjectProtocolHandler : public nsIProtocolHandler
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -388,34 +388,34 @@ Element::WrapObject(JSContext *aCx, JS::
     return obj;
   }
 
   // Make sure the style context goes away _before_ we load the binding
   // since that can destroy the relevant presshell.
   mozilla::css::URLValue *bindingURL;
   bool ok = GetBindingURL(doc, &bindingURL);
   if (!ok) {
-    dom::Throw<true>(aCx, NS_ERROR_FAILURE);
+    dom::Throw(aCx, NS_ERROR_FAILURE);
     return nullptr;
   }
 
   if (!bindingURL) {
     // No binding, nothing left to do here.
     return obj;
   }
 
   nsCOMPtr<nsIURI> uri = bindingURL->GetURI();
   nsCOMPtr<nsIPrincipal> principal = bindingURL->mOriginPrincipal;
 
   // We have a binding that must be installed.
   bool dummy;
 
   nsXBLService* xblService = nsXBLService::GetInstance();
   if (!xblService) {
-    dom::Throw<true>(aCx, NS_ERROR_NOT_AVAILABLE);
+    dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   nsRefPtr<nsXBLBinding> binding;
   xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding), &dummy);
   
   if (binding) {
     if (nsContentUtils::IsSafeToRunScript()) {
--- a/content/base/src/EventSource.cpp
+++ b/content/base/src/EventSource.cpp
@@ -84,17 +84,18 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(EventSour
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(EventSource)
   bool isBlack = tmp->IsBlack();
   if (isBlack || tmp->mWaitingForOnStopRequest) {
     if (tmp->mListenerManager) {
       tmp->mListenerManager->MarkForCC();
     }
     if (!isBlack && tmp->PreservingWrapper()) {
-      xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
+      // This marks the wrapper black.
+      tmp->GetWrapper();
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(EventSource)
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -1216,17 +1216,19 @@ FragmentOrElement::MarkUserDataHandler(v
 {
   xpc_TryUnmarkWrappedGrayObject(static_cast<nsISupports*>(aChild));
 }
 
 void
 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
 {
   JSObject* o = GetJSObjectChild(aNode);
-  xpc_UnmarkGrayObject(o);
+  if (o) {
+    JS::ExposeObjectToActiveJS(o);
+  }
 
   nsEventListenerManager* elm = aNode->GetListenerManager(false);
   if (elm) {
     elm->MarkForCC();
   }
 
   if (aNode->HasProperties()) {
     nsIDocument* ownerDoc = aNode->OwnerDoc();
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -20,16 +20,17 @@ LOCAL_INCLUDES += \
   -I$(topsrcdir)/content/xbl/src \
   -I$(topsrcdir)/content/xml/content/src \
   -I$(topsrcdir)/content/xml/document/src \
   -I$(topsrcdir)/content/xslt/src/xpath \
   -I$(topsrcdir)/content/xul/content/src \
   -I$(topsrcdir)/content/xul/document/src \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/ipc \
+  -I$(topsrcdir)/dom/workers \
   -I$(topsrcdir)/js/ipc \
   -I$(topsrcdir)/image/src \
   -I$(topsrcdir)/js/xpconnect/src \
   -I$(topsrcdir)/layout/base \
   -I$(topsrcdir)/layout/generic \
   -I$(topsrcdir)/layout/style \
   -I$(topsrcdir)/layout/svg \
   -I$(topsrcdir)/layout/xul/base/src \
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -576,17 +576,18 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket)
   bool isBlack = tmp->IsBlack();
   if (isBlack|| tmp->mKeepingAlive) {
     if (tmp->mListenerManager) {
       tmp->mListenerManager->MarkForCC();
     }
     if (!isBlack && tmp->PreservingWrapper()) {
-      xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
+      // This marks the wrapper black.
+      tmp->GetWrapper();
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(WebSocket)
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/css/Declaration.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "prprf.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsCSSParser.h"
 #include "nsStyledElement.h"
+#include "nsIURI.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 #define MISC_STR_PTR(_cont) \
   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
 
 bool
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -252,21 +252,21 @@ bool nsContentUtils::sDOMWindowDumpEnabl
 namespace {
 
 static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 static PLDHashTable sEventListenerManagersHash;
 
-class DOMEventListenerManagersHashReporter MOZ_FINAL : public MemoryReporterBase
+class DOMEventListenerManagersHashReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   DOMEventListenerManagersHashReporter()
-    : MemoryReporterBase(
+    : MemoryUniReporter(
         "explicit/dom/event-listener-managers-hash",
         KIND_HEAP,
         UNITS_BYTES,
         "Memory used by the event listener manager's hash table.")
   {}
 
 private:
   int64_t Amount()
@@ -5180,23 +5180,25 @@ nsContentUtils::GetContextForEventHandle
 
   return nullptr;
 }
 
 /* static */
 JSContext *
 nsContentUtils::GetCurrentJSContext()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   return sXPConnect->GetCurrentJSContext();
 }
 
 /* static */
 JSContext *
 nsContentUtils::GetSafeJSContext()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   return sXPConnect->GetSafeJSContext();
 }
 
 /* static */
 nsresult
 nsContentUtils::ASCIIToLower(nsAString& aStr)
 {
   PRUnichar* iter = aStr.BeginWriting();
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -33,16 +33,17 @@
 #include "nsIDocument.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsGkAtoms.h"
 #include "nsGUIEvent.h"
 #include "nsIFrame.h"
+#include "nsIURI.h"
 
 // image copy stuff
 #include "nsIImageLoadingContent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentCID.h"
 
 #include "mozilla/dom/Element.h"
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -636,27 +636,27 @@ nsDOMMemoryFile::DataOwner::sDataOwnerMu
 nsDOMMemoryFile::DataOwner::sDataOwners;
 
 /* static */ bool
 nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered;
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMMemoryFileDataOwnerMallocSizeOf)
 
 class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
-  : public nsIMemoryMultiReporter
+  : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.AssignASCII("dom-memory-file-data-owner");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCallback,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
                             nsISupports *aClosure)
   {
     typedef nsDOMMemoryFile::DataOwner DataOwner;
 
     StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
 
     if (!DataOwner::sDataOwners) {
       return NS_OK;
@@ -720,29 +720,29 @@ class nsDOMMemoryFileDataOwnerMemoryRepo
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(nsDOMMemoryFileDataOwnerMemoryReporter,
-                   nsIMemoryMultiReporter)
+                   nsIMemoryReporter)
 
 /* static */ void
 nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered()
 {
   sDataOwnerMutex.AssertCurrentThreadOwns();
   if (sMemoryReporterRegistered) {
     return;
   }
 
   nsRefPtr<nsDOMMemoryFileDataOwnerMemoryReporter> reporter = new
     nsDOMMemoryFileDataOwnerMemoryReporter();
-  NS_RegisterMemoryMultiReporter(reporter);
+  NS_RegisterMemoryReporter(reporter);
 
   sMemoryReporterRegistered = true;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // nsDOMFileList implementation
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList)
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -11321,17 +11321,17 @@ nsIDocument::WrapObject(JSContext *aCx, 
   JS::Rooted<JS::Value> winVal(aCx);
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   nsresult rv = nsContentUtils::WrapNative(aCx, obj, win,
                                            &NS_GET_IID(nsIDOMWindow),
                                            winVal.address(),
                                            getter_AddRefs(holder),
                                            false);
   if (NS_FAILED(rv)) {
-    Throw<true>(aCx, rv);
+    Throw(aCx, rv);
     return nullptr;
   }
 
   NS_NAMED_LITERAL_STRING(doc_str, "document");
 
   if (!JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(winVal),
                            reinterpret_cast<const jschar *>
                                            (doc_str.get()),
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -2468,17 +2468,17 @@ nsINode::WrapObject(JSContext *aCx, JS::
   // Event handling is possible only if (1). If (2) event handling is
   // prevented.
   // If the document has never had a script handling object, untrusted
   // scripts (3) shouldn't touch it!
   bool hasHadScriptHandlingObject = false;
   if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) &&
       !hasHadScriptHandlingObject &&
       !nsContentUtils::IsCallerChrome()) {
-    Throw<true>(aCx, NS_ERROR_UNEXPECTED);
+    Throw(aCx, NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aScope));
   if (obj && ChromeOnlyAccess() &&
       !nsContentUtils::IsSystemPrincipal(NodePrincipal()) &&
       xpc::AllowXBLScope(js::GetObjectCompartment(obj)))
   {
--- a/content/base/src/nsInProcessTabChildGlobal.h
+++ b/content/base/src/nsInProcessTabChildGlobal.h
@@ -15,22 +15,24 @@
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIClassInfo.h"
 #include "nsIDocShell.h"
 #include "nsIDOMElement.h"
 #include "nsCOMArray.h"
 #include "nsThreadUtils.h"
 #include "nsIGlobalObject.h"
+#include "nsIScriptObjectPrincipal.h"
 #include "nsWeakReference.h"
 
 class nsInProcessTabChildGlobal : public nsDOMEventTargetHelper,
                                   public nsFrameScriptExecutor,
                                   public nsIInProcessContentFrameMessageManager,
                                   public nsIGlobalObject,
+                                  public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public mozilla::dom::ipc::MessageManagerCallback
 {
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   virtual ~nsInProcessTabChildGlobal();
   NS_DECL_ISUPPORTS_INHERITED
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -3394,17 +3394,17 @@ nsObjectLoadingContent::DoNewResolve(JSC
                                      JS::Handle<jsid> aId,
                                      JS::MutableHandle<JS::Value> aValue)
 {
   // We don't resolve anything; we just try to make sure we're instantiated
 
   nsRefPtr<nsNPAPIPluginInstance> pi;
   nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
   if (NS_FAILED(rv)) {
-    return mozilla::dom::Throw<true>(aCx, rv);
+    return mozilla::dom::Throw(aCx, rv);
   }
   return true;
 }
 
 void
 nsObjectLoadingContent::GetOwnPropertyNames(JSContext* aCx,
                                             nsTArray<nsString>& /* unused */,
                                             ErrorResult& aRv)
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -62,16 +62,17 @@
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/Attributes.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsFormData.h"
 #include "nsStreamListenerWrapper.h"
+#include "xpcjsid.h"
 
 #include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Maximum size that we'll grow an ArrayBuffer instead of doubling,
 // once doubling reaches this threshold
@@ -440,17 +441,18 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttp
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
   bool isBlack = tmp->IsBlack();
   if (isBlack || tmp->mWaitingForOnStopRequest) {
     if (tmp->mListenerManager) {
       tmp->mListenerManager->MarkForCC();
     }
     if (!isBlack && tmp->PreservingWrapper()) {
-      xpc_UnmarkGrayObject(tmp->GetWrapperPreserveColor());
+      // This marks the wrapper black.
+      tmp->GetWrapper();
     }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
   return tmp->
     IsBlackAndDoesNotNeedTracing(static_cast<nsDOMEventTargetHelper*>(tmp));
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -43,16 +43,17 @@
 
 class AsyncVerifyRedirectCallbackForwarder;
 class BlobSet;
 class nsDOMFile;
 class nsFormData;
 class nsIJARChannel;
 class nsILoadGroup;
 class nsIUnicodeDecoder;
+class nsIJSID;
 
 namespace mozilla {
 
 // A helper for building up an ArrayBuffer object's data
 // before creating the ArrayBuffer itself.  Will do doubling
 // based reallocation, up to an optional maximum growth given.
 //
 // When all the data has been appended, call getArrayBuffer,
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -138,21 +138,21 @@ namespace dom {
 const Float SIGMA_MAX = 100;
 
 /* Memory reporter stuff */
 static int64_t gCanvasAzureMemoryUsed = 0;
 
 // This is KIND_OTHER because it's not always clear where in memory the pixels
 // of a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
-class Canvas2dPixelsReporter MOZ_FINAL : public MemoryReporterBase
+class Canvas2dPixelsReporter MOZ_FINAL : public MemoryUniReporter
 {
   public:
     Canvas2dPixelsReporter()
-      : MemoryReporterBase("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
 "Memory used by 2D canvases. Each canvas requires (width * height * 4) bytes.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE { return gCanvasAzureMemoryUsed; }
 };
 
 class CanvasRadialGradient : public CanvasGradient
 {
@@ -814,21 +814,20 @@ CanvasRenderingContext2D::AddDemotableCo
 void
 CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D* context)
 {
   std::vector<CanvasRenderingContext2D*>::iterator iter = std::find(DemotableContexts().begin(), DemotableContexts().end(), context);
   if (iter != DemotableContexts().end())
     DemotableContexts().erase(iter);
 }
 
-#define MIN_SKIA_GL_DIMENSION 16
-
 bool
 CheckSizeForSkiaGL(IntSize size) {
-  return size.width > MIN_SKIA_GL_DIMENSION && size.height > MIN_SKIA_GL_DIMENSION;
+  int minsize = Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128);
+  return size.width >= minsize && size.height >= minsize;
 }
 
 #endif
 
 void
 CanvasRenderingContext2D::EnsureTarget()
 {
   if (mTarget) {
--- a/content/canvas/src/ImageData.h
+++ b/content/canvas/src/ImageData.h
@@ -9,17 +9,17 @@
 
 #include "nsIDOMCanvasRenderingContext2D.h"
 
 #include "mozilla/Attributes.h"
 #include <stdint.h>
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsTraceRefcnt.h"
-#include "xpcpublic.h"
+#include "js/GCAPI.h"
 
 namespace mozilla {
 namespace dom {
 
 class ImageData MOZ_FINAL : public nsISupports
 {
 public:
   ImageData(uint32_t aWidth, uint32_t aHeight, JSObject& aData)
@@ -49,17 +49,17 @@ public:
     return mHeight;
   }
   JSObject* Data(JSContext* cx, JS::Handle<JSObject*> /* unused */) const
   {
     return GetDataObject();
   }
   JSObject* GetDataObject() const
   {
-    xpc_UnmarkGrayObject(mData);
+    JS::ExposeObjectToActiveJS(mData);
     return mData;
   }
 
   JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> scope);
 
 private:
   void HoldData();
   void DropData();
--- a/content/canvas/src/WebGL2Context.cpp
+++ b/content/canvas/src/WebGL2Context.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL2Context.h"
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
-
+#include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::gl;
 
 // -----------------------------------------------------------------------------
 // CONSTRUCTOR & DESTRUCTOR
 
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -5,17 +5,17 @@
 
 #include "WebGLContext.h"
 #include "WebGL1Context.h"
 #include "WebGLObjectModel.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
-#include "WebGLMemoryMultiReporterWrapper.h"
+#include "WebGLMemoryReporterWrapper.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLVertexArray.h"
 #include "WebGLQuery.h"
 
 #include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
@@ -174,17 +174,17 @@ WebGLContext::WebGLContext()
     mGLMaxColorAttachments = 1;
     mGLMaxDrawBuffers = 1;
     mGLMaxTransformFeedbackSeparateAttribs = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
-    WebGLMemoryMultiReporterWrapper::AddWebGLContext(this);
+    WebGLMemoryReporterWrapper::AddWebGLContext(this);
 
     mAllowRestore = true;
     mContextLossTimerRunning = false;
     mDrawSinceContextLossTimerSet = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextNotLost;
     mContextLostErrorSet = false;
     mLoseContextOnHeapMinimize = false;
@@ -208,17 +208,17 @@ WebGLContext::WebGLContext()
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     DestroyResourcesAndContext();
-    WebGLMemoryMultiReporterWrapper::RemoveWebGLContext(this);
+    WebGLMemoryReporterWrapper::RemoveWebGLContext(this);
     TerminateContextLossTimer();
     mContextRestorer = nullptr;
 }
 
 void
 WebGLContext::DestroyResourcesAndContext()
 {
     if (mMemoryPressureObserver) {
@@ -648,18 +648,18 @@ void WebGLContext::LoseOldestWebGLContex
 #endif
     MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
 
     // it's important to update the index on a new context before losing old contexts,
     // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
     // when choosing which one to lose first.
     UpdateLastUseIndex();
 
-    WebGLMemoryMultiReporterWrapper::ContextsArrayType &contexts
-      = WebGLMemoryMultiReporterWrapper::Contexts();
+    WebGLMemoryReporterWrapper::ContextsArrayType &contexts
+      = WebGLMemoryReporterWrapper::Contexts();
 
     // quick exit path, should cover a majority of cases
     if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
         return;
     }
 
     // note that here by "context" we mean "non-lost context". See the check for
     // IsContextLost() below. Indeed, the point of this function is to maybe lose
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -71,17 +71,17 @@ class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLVertexArray;
 
 namespace dom {
 class ImageData;
 
 struct WebGLContextAttributes;
 struct WebGLContextAttributesInitializer;
-template<typename> class Nullable;
+template<typename> struct Nullable;
 }
 
 using WebGLTexelConversions::WebGLTexelFormat;
 
 WebGLTexelFormat GetWebGLTexelFormat(GLenum format, GLenum type);
 
 struct WebGLContextOptions {
     // these are defaults
@@ -113,17 +113,17 @@ class WebGLContext :
     public nsIDOMWebGLRenderingContext,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference,
     public WebGLRectangleObject,
     public nsWrapperCache
 {
     friend class WebGLContextUserData;
     friend class WebGLMemoryPressureObserver;
-    friend class WebGLMemoryMultiReporterWrapper;
+    friend class WebGLMemoryReporterWrapper;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionVertexArray;
 
--- a/content/canvas/src/WebGLContextExtensions.cpp
+++ b/content/canvas/src/WebGLContextExtensions.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLExtensions.h"
 #include "GLContext.h"
 
 #include "nsString.h"
-
+#include "mozilla/Preferences.h"
 #include "AccessCheck.h"
 
 using namespace mozilla;
 using namespace mozilla::gl;
 
 // must match WebGLContext::WebGLExtensionID
 static const char *sExtensionNames[] = {
     "EXT_texture_filter_anisotropic",
--- a/content/canvas/src/WebGLContextReporter.cpp
+++ b/content/canvas/src/WebGLContextReporter.cpp
@@ -1,165 +1,165 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
-#include "WebGLMemoryMultiReporterWrapper.h"
+#include "WebGLMemoryReporterWrapper.h"
 #include "nsIMemoryReporter.h"
 
 using namespace mozilla;
 
 NS_IMPL_ISUPPORTS1(WebGLMemoryPressureObserver, nsIObserver)
 
-class WebGLMemoryMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter 
+class WebGLMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
 };
 
-NS_IMPL_ISUPPORTS1(WebGLMemoryMultiReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(WebGLMemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
-WebGLMemoryMultiReporter::GetName(nsACString &aName)
+WebGLMemoryReporter::GetName(nsACString &aName)
 {
   aName.AssignLiteral("webgl");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-WebGLMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, 
-                                         nsISupports* aClosure)
+WebGLMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
+                                    nsISupports* aClosure)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
     do {                                                                      \
       nsresult rv;                                                            \
       rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), _kind,    \
                          _units, _amount, NS_LITERAL_CSTRING(_desc),          \
                          aClosure);                                           \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
     } while (0)
 
     REPORT("webgl-texture-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetTextureMemoryUsed(),
+           WebGLMemoryReporterWrapper::GetTextureMemoryUsed(),
            "Memory used by WebGL textures.The OpenGL"
            " implementation is free to store these textures in either video"
            " memory or main memory. This measurement is only a lower bound,"
-           " actual memory usage may be higher for example if the storage" 
+           " actual memory usage may be higher for example if the storage"
            " is strided.");
-   
+
     REPORT("webgl-texture-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetTextureCount(), 
+           WebGLMemoryReporterWrapper::GetTextureCount(),
            "Number of WebGL textures.");
 
     REPORT("webgl-buffer-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetBufferMemoryUsed(), 
+           WebGLMemoryReporterWrapper::GetBufferMemoryUsed(),
            "Memory used by WebGL buffers. The OpenGL"
            " implementation is free to store these buffers in either video"
            " memory or main memory. This measurement is only a lower bound,"
            " actual memory usage may be higher for example if the storage"
            " is strided.");
 
     REPORT("explicit/webgl/buffer-cache-memory",
            nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed(),
+           WebGLMemoryReporterWrapper::GetBufferCacheMemoryUsed(),
            "Memory used by WebGL buffer caches. The WebGL"
            " implementation caches the contents of element array buffers"
            " only.This adds up with the webgl-buffer-memory value, but"
            " contrary to it, this one represents bytes on the heap,"
            " not managed by OpenGL.");
 
     REPORT("webgl-buffer-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetBufferCount(),
-           "Number of WebGL buffers."); 
-   
+           WebGLMemoryReporterWrapper::GetBufferCount(),
+           "Number of WebGL buffers.");
+
     REPORT("webgl-renderbuffer-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetRenderbufferMemoryUsed(), 
+           WebGLMemoryReporterWrapper::GetRenderbufferMemoryUsed(),
            "Memory used by WebGL renderbuffers. The OpenGL"
            " implementation is free to store these renderbuffers in either"
            " video memory or main memory. This measurement is only a lower"
            " bound, actual memory usage may be higher for example if the"
            " storage is strided.");
-   
+
     REPORT("webgl-renderbuffer-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetRenderbufferCount(),
+           WebGLMemoryReporterWrapper::GetRenderbufferCount(),
            "Number of WebGL renderbuffers.");
-  
+
     REPORT("explicit/webgl/shader",
            nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetShaderSize(), 
+           WebGLMemoryReporterWrapper::GetShaderSize(),
            "Combined size of WebGL shader ASCII sources and translation"
-           " logs cached on the heap."); 
+           " logs cached on the heap.");
 
     REPORT("webgl-shader-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetShaderCount(), 
-           "Number of WebGL shaders."); 
+           WebGLMemoryReporterWrapper::GetShaderCount(),
+           "Number of WebGL shaders.");
 
     REPORT("webgl-context-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetContextCount(), 
-           "Number of WebGL contexts."); 
+           WebGLMemoryReporterWrapper::GetContextCount(),
+           "Number of WebGL contexts.");
 
 #undef REPORT
 
     return NS_OK;
 }
 
-WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::sUniqueInstance = nullptr;
+WebGLMemoryReporterWrapper* WebGLMemoryReporterWrapper::sUniqueInstance = nullptr;
 
-WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::UniqueInstance()
+WebGLMemoryReporterWrapper* WebGLMemoryReporterWrapper::UniqueInstance()
 {
     if (!sUniqueInstance) {
-        sUniqueInstance = new WebGLMemoryMultiReporterWrapper;
+        sUniqueInstance = new WebGLMemoryReporterWrapper;
     }
-    return sUniqueInstance;     
+    return sUniqueInstance;
 }
 
-WebGLMemoryMultiReporterWrapper::WebGLMemoryMultiReporterWrapper()
-{ 
-    mReporter = new WebGLMemoryMultiReporter;   
-    NS_RegisterMemoryMultiReporter(mReporter);
+WebGLMemoryReporterWrapper::WebGLMemoryReporterWrapper()
+{
+    mReporter = new WebGLMemoryReporter;
+    NS_RegisterMemoryReporter(mReporter);
 }
 
-WebGLMemoryMultiReporterWrapper::~WebGLMemoryMultiReporterWrapper()
+WebGLMemoryReporterWrapper::~WebGLMemoryReporterWrapper()
 {
-    NS_UnregisterMemoryMultiReporter(mReporter);
+    NS_UnregisterMemoryReporter(mReporter);
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLBufferMallocSizeOf)
 
-int64_t 
-WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed() {
+int64_t
+WebGLMemoryReporterWrapper::GetBufferCacheMemoryUsed() {
     const ContextsArrayType & contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLBuffer *buffer = contexts[i]->mBuffers.getFirst();
              buffer;
              buffer = buffer->getNext())
         {
             if (buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
                 result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
         }
     }
     return result;
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLShaderMallocSizeOf)
 
-int64_t 
-WebGLMemoryMultiReporterWrapper::GetShaderSize() {
+int64_t
+WebGLMemoryReporterWrapper::GetShaderSize() {
     const ContextsArrayType & contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLShader *shader = contexts[i]->mShaders.getFirst();
              shader;
              shader = shader->getNext())
         {
             result += shader->SizeOfIncludingThis(WebGLShaderMallocSizeOf);
rename from content/canvas/src/WebGLMemoryMultiReporterWrapper.h
rename to content/canvas/src/WebGLMemoryReporterWrapper.h
--- a/content/canvas/src/WebGLMemoryMultiReporterWrapper.h
+++ b/content/canvas/src/WebGLMemoryReporterWrapper.h
@@ -1,42 +1,42 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef WEBGLMEMORYMULTIREPORTWRAPER_H_
-#define WEBGLMEMORYMULTIREPORTWRAPER_H_
+#ifndef WEBGLMEMORYREPORTERWRAPPER_H_
+#define WEBGLMEMORYREPORTERWRAPPER_H_
 
 #include "WebGLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 
 namespace mozilla {
 
-class WebGLMemoryMultiReporterWrapper
+class WebGLMemoryReporterWrapper
 {
-    WebGLMemoryMultiReporterWrapper();
-    ~WebGLMemoryMultiReporterWrapper();
-    static WebGLMemoryMultiReporterWrapper* sUniqueInstance;
+    WebGLMemoryReporterWrapper();
+    ~WebGLMemoryReporterWrapper();
+    static WebGLMemoryReporterWrapper* sUniqueInstance;
 
-    // here we store plain pointers, not RefPtrs: we don't want the 
-    // WebGLMemoryMultiReporterWrapper unique instance to keep alive all		
+    // here we store plain pointers, not RefPtrs: we don't want the
+    // WebGLMemoryReporterWrapper unique instance to keep alive all
     // WebGLContexts ever created.
     typedef nsTArray<const WebGLContext*> ContextsArrayType;
     ContextsArrayType mContexts;
 
-    nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+    nsCOMPtr<nsIMemoryReporter> mReporter;
 
-    static WebGLMemoryMultiReporterWrapper* UniqueInstance();
+    static WebGLMemoryReporterWrapper* UniqueInstance();
 
     static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; }
 
     friend class WebGLContext;
 
   public:
 
     static void AddWebGLContext(const WebGLContext* c) {
--- a/content/canvas/test/reftest/reftest.list
+++ b/content/canvas/test/reftest/reftest.list
@@ -138,17 +138,17 @@ pref(webgl.force-layers-readback,true) r
 
 
 # Check alpha behavior:
 fuzzy-if(B2G,256,83) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=1.0  wrapper.html?colors.png
 # These tests don't use wrapper.html, as there appear to be invalidation issues with black.png and async image decoding - Bug 803299
 random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.0&alphaVal=1.0  black.html
 
 fuzzy-if(B2G,256,83) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0        wrapper.html?colors.png
-fails-if(B2G)        random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0&alpha  wrapper.html?white.png
+random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0&alpha  wrapper.html?white.png
 
 fuzzy(1,65536) fuzzy-if(B2G,256,83) fuzzy-if(Android||B2G,9,65536) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=1.0  wrapper.html?half-colors.png
 
 # Test premult:
 fuzzy(1,65536) fails-if(B2G) fuzzy-if(Android,9,65536)                                random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.5&alpha          wrapper.html?colors-half-alpha.png
 fuzzy(1,65536) fails-if(B2G) fuzzy-if(Android,9,65536) fails-if(cocoaWidget||Android) random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha          wrapper.html?half-colors-half-alpha.png
 fuzzy(1,65536) fails-if(B2G) fuzzy-if(Android,9,65536)                                random-if(Android&&AndroidVersion<15)  == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=0.5&alpha&premult  wrapper.html?colors-half-alpha.png
 
--- a/content/canvas/test/webgl/non-conf-tests/Makefile.in
+++ b/content/canvas/test/webgl/non-conf-tests/Makefile.in
@@ -1,8 +1,9 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MOCHITEST_FILES = \
   test_webgl_conformance.html \
+  test_webgl_request_mismatch.html \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/non-conf-tests/test_webgl_request_mismatch.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<title>WebGL test: Mismatched 'webgl' and 'experimental-webgl' context requests</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<body>
+<canvas id="c1"></canvas>
+<canvas id="c2"></canvas>
+<canvas id="c3"></canvas>
+<canvas id="c4"></canvas>
+<script>
+
+function testContextRetrieval(canvasId, creationId, requestId) {
+  var canvas = document.getElementById(canvasId);
+  ok(canvas, 'Invalid `canvasId`: ' + canvasId);
+
+  var createdGL = canvas.getContext(creationId);
+  if (!createdGL)
+    return; // No WebGL on this machine?
+
+  var requestedGL = canvas.getContext(requestId);
+  if (creationId == requestId) {
+    ok(requestedGL, 'Request for \'' + requestId + '\' on from \'' + creationId + '\' should succeed.');
+    ok(requestedGL == createdGL, 'Request for \'' + requestId + '\' on from \'' + creationId + '\' should match.');
+  } else {
+    ok(!requestedGL, 'Request for \'' + requestId + '\' on from \'' + creationId + '\' should fail.');
+  }
+}
+
+testContextRetrieval('c1', 'experimental-webgl', 'webgl');
+testContextRetrieval('c2', 'webgl', 'experimental-webgl');
+testContextRetrieval('c3', 'experimental-webgl', 'experimental-webgl');
+testContextRetrieval('c4', 'webgl', 'webgl');
+
+</script>
+
--- a/content/events/public/EventTarget.h
+++ b/content/events/public/EventTarget.h
@@ -17,17 +17,17 @@ class nsIDOMEventListener;
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class EventListener;
 class EventHandlerNonNull;
-template <class T> class Nullable;
+template <class T> struct Nullable;
 
 // IID for the dom::EventTarget interface
 #define NS_EVENTTARGET_IID \
 { 0x0a5aed21, 0x0bab, 0x48b3, \
  { 0xbe, 0x4b, 0xd4, 0xf9, 0xd4, 0xea, 0xc7, 0xdb } }
 
 class EventTarget : public nsIDOMEventTarget,
                     public nsWrapperCache
--- a/content/events/public/nsEventStates.h
+++ b/content/events/public/nsEventStates.h
@@ -248,16 +248,18 @@ private:
 // Platform does not support plugin content (some mobile platforms)
 #define NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM NS_DEFINE_EVENT_STATE_MACRO(41)
 // Element is ltr (for :dir pseudo-class)
 #define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(42)
 // Element is rtl (for :dir pseudo-class)
 #define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(43)
 // Handler for play preview plugin
 #define NS_EVENT_STATE_TYPE_PLAY_PREVIEW NS_DEFINE_EVENT_STATE_MACRO(44)
+// Element is highlighted (devtools inspector)
+#define NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED NS_DEFINE_EVENT_STATE_MACRO(45)
 
 // Event state that is used for values that need to be parsed but do nothing.
 #define NS_EVENT_STATE_IGNORE NS_DEFINE_EVENT_STATE_MACRO(63)
 
 /**
  * NOTE: do not go over 63 without updating nsEventStates::InternalType!
  */
 
--- a/content/events/src/nsDOMDataTransfer.cpp
+++ b/content/events/src/nsDOMDataTransfer.cpp
@@ -487,18 +487,20 @@ nsDOMDataTransfer::MozGetDataAt(const ns
         nsCOMPtr<nsISupports> data;
         formatitem.mData->GetAsISupports(getter_AddRefs(data));
         // Make sure the code that is calling us is same-origin with the data.
         nsCOMPtr<EventTarget> pt = do_QueryInterface(data);
         if (pt) {
           nsresult rv = NS_OK;
           nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
           NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
-          nsIScriptObjectPrincipal* sp = c->GetGlobalObject();
-          NS_ENSURE_TRUE(sp, NS_ERROR_DOM_SECURITY_ERR);
+          nsIGlobalObject* go = c->GetGlobalObject();
+          NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR);
+          nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
+          MOZ_ASSERT(sp, "This cannot fail on the main thread.");
           nsIPrincipal* dataPrincipal = sp->GetPrincipal();
           NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
           NS_ENSURE_TRUE(principal || (principal = GetCurrentPrincipal(&rv)),
                          NS_ERROR_DOM_SECURITY_ERR);
           NS_ENSURE_SUCCESS(rv, rv);
           bool equals = false;
           NS_ENSURE_TRUE(NS_SUCCEEDED(principal->Equals(dataPrincipal, &equals)) && equals,
                          NS_ERROR_DOM_SECURITY_ERR);
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -1325,19 +1325,21 @@ void
 nsEventListenerManager::MarkForCC()
 {
   uint32_t count = mListeners.Length();
   for (uint32_t i = 0; i < count; ++i) {
     const nsListenerStruct& ls = mListeners.ElementAt(i);
     nsIJSEventListener* jsl = ls.GetJSListener();
     if (jsl) {
       if (jsl->GetHandler().HasEventHandler()) {
-        xpc_UnmarkGrayObject(jsl->GetHandler().Ptr()->Callable());
+        JS::ExposeObjectToActiveJS(jsl->GetHandler().Ptr()->Callable());
       }
-      xpc_UnmarkGrayObject(jsl->GetEventScope());
+      if (JSObject* scope = jsl->GetEventScope()) {
+        JS::ExposeObjectToActiveJS(scope);
+      }
     } else if (ls.mListenerType == eWrappedJSListener) {
       xpc_TryUnmarkWrappedGrayObject(ls.mListener.GetXPCOMCallback());
     } else if (ls.mListenerType == eWebIDLListener) {
       // Callback() unmarks gray
       ls.mListener.GetWebIDLCallback()->Callback();
     }
   }
   if (mRefCnt.IsPurple()) {
--- a/content/html/content/src/HTMLAudioElement.cpp
+++ b/content/html/content/src/HTMLAudioElement.cpp
@@ -13,16 +13,17 @@
 #include "nsIDocument.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "AudioSampleFormat.h"
 #include "AudioChannelCommon.h"
 #include <algorithm>
 #include "mozilla/Preferences.h"
+#include "nsComponentManagerUtils.h"
 
 static bool
 IsAudioAPIEnabled()
 {
   return mozilla::Preferences::GetBool("media.audio_data.enabled", true);
 }
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -775,16 +775,24 @@ nsresult
 HTMLCanvasElement::GetContext(const nsAString& aContextId,
                               nsISupports** aContext)
 {
   ErrorResult rv;
   *aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).get();
   return rv.ErrorCode();
 }
 
+static bool
+IsContextIdWebGL(const nsAString& str)
+{
+  return str.EqualsLiteral("webgl") ||
+         str.EqualsLiteral("experimental-webgl") ||
+         str.EqualsLiteral("moz-webgl");
+}
+
 already_AddRefed<nsISupports>
 HTMLCanvasElement::GetContext(JSContext* aCx,
                               const nsAString& aContextId,
                               JS::Handle<JS::Value> aContextOptions,
                               ErrorResult& rv)
 {
   if (mCurrentContextId.IsEmpty()) {
     rv = GetContextHelper(aContextId, getter_AddRefs(mCurrentContext));
@@ -806,16 +814,30 @@ HTMLCanvasElement::GetContext(JSContext*
     if (rv.Failed()) {
       rv = NS_OK; // See bug 645792
       return nullptr;
     }
     mCurrentContextId.Assign(aContextId);
   }
 
   if (!mCurrentContextId.Equals(aContextId)) {
+    if (IsContextIdWebGL(aContextId) &&
+        IsContextIdWebGL(mCurrentContextId))
+    {
+      // Warn when we get a request for a webgl context with an id that differs
+      // from the id it was created with.
+      nsCString creationId = NS_LossyConvertUTF16toASCII(mCurrentContextId);
+      nsCString requestId = NS_LossyConvertUTF16toASCII(aContextId);
+      JS_ReportWarning(aCx, "WebGL: Retrieving a WebGL context from a canvas "
+                            "via a request id ('%s') different from the id used "
+                            "to create the context ('%s') is not allowed.",
+                            requestId.get(),
+                            creationId.get());
+    }
+    
     //XXX eventually allow for more than one active context on a given canvas
     return nullptr;
   }
 
   nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
   return context.forget();
 }
 
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -8,16 +8,17 @@
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsAttrValue.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
+#include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLPropertiesCollection)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLPropertiesCollection)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
--- a/content/html/content/src/HTMLSharedElement.cpp
+++ b/content/html/content/src/HTMLSharedElement.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/dom/HTMLParamElementBinding.h"
 #include "mozilla/dom/HTMLQuoteElementBinding.h"
 
 #include "nsAttrValueInlines.h"
 #include "nsStyleConsts.h"
 #include "nsRuleData.h"
 #include "nsMappedAttributes.h"
 #include "nsContentUtils.h"
+#include "nsIURI.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Shared)
 
 namespace mozilla {
 namespace dom {
 
 extern nsAttrValue::EnumTable kListTypeTable[];
 
--- a/content/html/content/src/nsClientRect.h
+++ b/content/html/content/src/nsClientRect.h
@@ -4,23 +4,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSCLIENTRECT_H_
 #define NSCLIENTRECT_H_
 
 #include "nsIDOMClientRect.h"
 #include "nsIDOMClientRectList.h"
 #include "nsTArray.h"
-#include "nsRect.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 
+struct nsRect;
+
 class nsClientRect MOZ_FINAL : public nsIDOMClientRect
                              , public nsWrapperCache
 {
 public:
   nsClientRect(nsISupports* aParent)
     : mParent(aParent), mX(0.0), mY(0.0), mWidth(0.0), mHeight(0.0)
   {
     SetIsDOMBinding();
--- a/content/html/document/src/HTMLAllCollection.cpp
+++ b/content/html/document/src/HTMLAllCollection.cpp
@@ -62,13 +62,14 @@ HTMLAllCollection::GetObject(JSContext* 
       return nullptr;
     }
 
     // Make the JSObject hold a reference to the document.
     JS_SetPrivate(mObject, ToSupports(mDocument));
     NS_ADDREF(mDocument);
   }
 
-  return xpc_UnmarkGrayObject(mObject);
+  JS::ExposeObjectToActiveJS(mObject);
+  return mObject;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -15,16 +15,17 @@
 #include "nsStyleConsts.h"
 #include "nsIDocument.h"
 #include "nsEventStates.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
+#include "nsIURI.h"
 
 #include "mozilla/dom/ElementBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 // nsISupports methods:
rename from content/media/omx/MP3FrameParser.cpp
rename to content/media/MP3FrameParser.cpp
--- a/content/media/omx/MP3FrameParser.cpp
+++ b/content/media/MP3FrameParser.cpp
@@ -27,17 +27,17 @@ public:
     mLength(aLength),
     mSize(0)
   {
     MOZ_ASSERT(mBuffer || !mLength);
   }
 
   nsresult Parse();
 
-  int64_t GetMP3Offset() const {
+  int64_t Length() const {
     return ID3_HEADER_LENGTH + mSize;
   }
 
 private:
   const uint8_t* mBuffer;
   uint32_t       mLength;
   uint32_t       mSize;
 };
@@ -79,24 +79,21 @@ public:
   };
 
   MP3Buffer(const uint8_t* aBuffer, uint32_t aLength)
   : mBuffer(aBuffer),
     mLength(aLength),
     mDurationUs(0),
     mNumFrames(0),
     mBitRateSum(0),
-    mFrameSizeSum(0),
-    mTrailing(0)
+    mFrameSizeSum(0)
   {
     MOZ_ASSERT(mBuffer || !mLength);
   }
 
-  static const uint8_t* FindNextHeader(const uint8_t* aBuffer, uint32_t aLength);
-
   nsresult Parse();
 
   int64_t GetDuration() const {
     return mDurationUs;
   }
 
   int64_t GetNumberOfFrames() const {
     return mNumFrames;
@@ -105,20 +102,16 @@ public:
   int64_t GetBitRateSum() const {
     return mBitRateSum;
   }
 
   int64_t GetFrameSizeSum() const {
     return mFrameSizeSum;
   }
 
-  int64_t GetTrailing() const {
-    return mTrailing;
-  }
-
 private:
 
   enum MP3FrameHeaderField {
     MP3_HDR_FIELD_SYNC,
     MP3_HDR_FIELD_VERSION,
     MP3_HDR_FIELD_LAYER,
     MP3_HDR_FIELD_BITRATE,
     MP3_HDR_FIELD_SAMPLERATE,
@@ -133,19 +126,19 @@ private:
   };
 
   static uint32_t ExtractBits(uint32_t aValue, uint32_t aOffset,
                               uint32_t aBits);
   static uint32_t ExtractFrameHeaderField(uint32_t aHeader,
                                           enum MP3FrameHeaderField aField);
   static uint32_t ExtractFrameHeader(const uint8_t* aBuffer);
   static nsresult DecodeFrameHeader(const uint8_t* aBuffer,
-                                          size_t* aFrameSize,
-                                          uint32_t* aBitRate,
-                                          uint64_t* aDuration);
+                                    uint32_t* aFrameSize,
+                                    uint32_t* aBitRate,
+                                    uint64_t* aDuration);
 
   static const uint16_t sBitRate[16];
   static const uint16_t sSampleRate[4];
 
   const uint8_t* mBuffer;
   uint32_t       mLength;
 
   // The duration of this parsers data in milliseconds.
@@ -154,19 +147,16 @@ private:
   // The number of frames in the range.
   int64_t mNumFrames;
 
   // The sum of all frame's bit rates.
   int64_t mBitRateSum;
 
   // The sum of all frame's sizes in byte.
   int32_t mFrameSizeSum;
-
-  // The number of trailing bytes.
-  int32_t mTrailing;
 };
 
 const uint16_t MP3Buffer::sBitRate[16] = {
   0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0
 };
 
 const uint16_t MP3Buffer::sSampleRate[4] = {
   44100, 48000, 32000, 0
@@ -209,34 +199,16 @@ uint32_t MP3Buffer::ExtractFrameHeader(c
   //  else
   //    return 0;
   //
   return (frameSync == uint32_t(MP3_HDR_CONST_FRAMESYNC)) *
          (version == uint32_t(MP3_HDR_CONST_VERSION)) *
          (layer == uint32_t(MP3_HDR_CONST_LAYER)) * !!bitRate * !!sampleRate * header;
 }
 
-const uint8_t* MP3Buffer::FindNextHeader(const uint8_t* aBuffer, uint32_t aLength)
-{
-  MOZ_ASSERT(aBuffer || !aLength);
-
-  // Find MP3's frame-sync marker while there are at least 4 bytes
-  // left to contain the MP3 frame header
-
-  while (aLength >= MP3_HEADER_LENGTH) {
-    if (ExtractFrameHeader(aBuffer)) {
-      break;
-    }
-    ++aBuffer;
-    --aLength;
-  }
-
-  return aBuffer;
-}
-
 nsresult MP3Buffer::DecodeFrameHeader(const uint8_t* aBuffer,
                                       uint32_t* aFrameSize,
                                       uint32_t* aBitRate,
                                       uint64_t* aDuration)
 {
   uint32_t header = ExtractFrameHeader(aBuffer);
 
   if (!header) {
@@ -263,213 +235,196 @@ nsresult MP3Buffer::DecodeFrameHeader(co
 
 nsresult MP3Buffer::Parse()
 {
   // We walk over the newly arrived data and sum up the
   // bit rates, sizes, durations, etc. of the contained
   // MP3 frames.
 
   const uint8_t* buffer = mBuffer;
-  uint32_t       length = mLength;
+  uint32_t length = mLength;
 
   while (length >= MP3_HEADER_LENGTH) {
 
     uint32_t frameSize;
     uint32_t bitRate;
     uint64_t duration;
 
     nsresult rv = DecodeFrameHeader(buffer, &frameSize, &bitRate, &duration);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
 
     mBitRateSum += bitRate;
     mDurationUs += duration;
     ++mNumFrames;
 
     mFrameSizeSum += frameSize;
 
     if (frameSize <= length) {
       length -= frameSize;
     } else {
       length = 0;
     }
 
     buffer += frameSize;
   }
 
-  mTrailing = length;
-
   return NS_OK;
 }
 
+// Some MP3's have large ID3v2 tags, up to 150KB, so we allow lots of
+// skipped bytes to be read, just in case, before we give up and assume