Merge m-c to b2g-inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 06 Sep 2013 18:01:09 -0400
changeset 158900 312d21ee124afad4f57c32e0e35eccb74afac9a9
parent 158899 b917d0b96b6fcd5a74b11e7f6b14e6e33e579b23 (current diff)
parent 158875 bdbdeda69f0534a1a255a510eb1dcd939de55130 (diff)
child 158901 d3c0933d379d3a19dffa3d55c65b970544870c3a
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [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 m-c to b2g-inbound.
browser/base/content/test/social/browser_social_markButton.js
browser/base/content/test/social/social_mark_image.png
content/canvas/src/CanvasRenderingContext2D.cpp
gfx/layers/ipc/LayerTransaction.ipdlh
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/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
widget/windows/winrt/FrameworkViewGfx.cpp
--- 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/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/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/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/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 ] });
   },
 
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)
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/locales/all-locales
+++ b/browser/locales/all-locales
@@ -1,88 +1,40 @@
-af
-ak
 ar
-as
 ast
-be
-bg
-bn-BD
-bn-IN
-br
-bs
-ca
 cs
-cy
-da
 de
-el
 en-GB
-en-ZA
 eo
 es-AR
 es-CL
 es-ES
 es-MX
-et
-eu
 fa
-fi
 fr
 fy-NL
-ga-IE
-gd
 gl
-gu-IN
 he
-hi-IN
-hr
 hu
-hy-AM
 id
-is
 it
 ja
 ja-JP-mac
-ka
 kk
-km
-kn
 ko
-ku
-lg
 lt
 lv
-mai
-mk
-ml
-mn
-mr
 nb-NO
 nl
 nn-NO
-nso
 oc
-or
-pa-IN
 pl
 pt-BR
 pt-PT
-rm
-ro
 ru
-si
 sk
-sl
-son
-sq
-sr
 sv-SE
-ta
-ta-LK
-te
 th
 tr
 uk
 vi
 zh-CN
 zh-TW
-zu
--- 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
@@ -425,21 +425,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/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/configure.in
+++ b/configure.in
@@ -6478,27 +6478,29 @@ dnl ====================================
 dnl = Content process sandboxing
 dnl ========================================================
 if test -n "$gonkdir"; then
     MOZ_CONTENT_SANDBOX=1
 fi
 
 MOZ_ARG_ENABLE_BOOL(content-sandbox,
 [  --enable-content-sandbox        Enable sandboxing support for content-processes],
-    MOZ_CONTENT_SANDBOX=1)
+    MOZ_CONTENT_SANDBOX=1,
+    MOZ_CONTENT_SANDBOX=)
 
 if test -n "$MOZ_CONTENT_SANDBOX"; then
     AC_DEFINE(MOZ_CONTENT_SANDBOX)
 fi
 
 AC_SUBST(MOZ_CONTENT_SANDBOX)
 
 MOZ_ARG_ENABLE_BOOL(content-sandbox-reporter,
 [ --enable-content-sandbox-reporter        Enable syscall reporter to troubleshoot syscalls denied by the content-processes sandbox],
-    MOZ_CONTENT_SANDBOX_REPORTER=1)
+    MOZ_CONTENT_SANDBOX_REPORTER=1,
+    MOZ_CONTENT_SANDBOX_REPORTER=)
 
 if test -n "$MOZ_CONTENT_SANDBOX_REPORTER"; then
     AC_DEFINE(MOZ_CONTENT_SANDBOX_REPORTER)
 fi
 
 AC_SUBST(MOZ_CONTENT_SANDBOX_REPORTER)
 
 dnl ========================================================
--- a/content/base/crashtests/308120-1.xul
+++ b/content/base/crashtests/308120-1.xul
@@ -1,3 +1,3 @@
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="var button=document.getElementsByTagName('button')[0]; try { button.appendChild(document.getAnonymousNodes(button)[0]); } catch(e) { }">
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="var button=document.getElementsByTagName('button')[0]; try { button.appendChild(SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(button))[0]); } catch(e) { }">
   <button/>
 </window>
--- a/content/base/crashtests/330925-1.xhtml
+++ b/content/base/crashtests/330925-1.xhtml
@@ -5,17 +5,17 @@
 <script>
 
 function init()
 {
   var foopy = document.getElementById("foopy");
   var emb = document.getElementById("emb");
 
   try {  
-    foopy.appendChild(document.getAnonymousNodes(emb)[0]);
+    foopy.appendChild(SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(emb))[0]);
     emb.parentNode.removeChild(emb);
     foopy.parentNode.removeChild(foopy);
   } catch (e) {
   }
   
   document.documentElement.removeAttribute("class");
 }
 
--- a/content/base/crashtests/401993-1.html
+++ b/content/base/crashtests/401993-1.html
@@ -6,17 +6,17 @@ function s()
 {
   var x = document.getElementById("x");
   x.style.MozBinding = "url(401993-1.xml#foo)";
   
   setTimeout(boom, 0);
   
   function boom()
   {
-    var nodes = document.getAnonymousNodes(x);
+    var nodes = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(x));
     if (!nodes) {
       setTimeout(boom, 10);
       return;
     }
 
     var newSpan = document.createElement("span");
     newSpan.contentEditable = "true";
     nodes[0].appendChild(newSpan);
--- a/content/base/test/test_base.xhtml
+++ b/content/base/test/test_base.xhtml
@@ -35,17 +35,17 @@ addLoadEvent(function() {
   }
   is(expected.length, 0, "found all expected nodes");
 
   var svgExpected =
     ["http://mochi.test:8888/tests/content/base/test/file_base_xbl.xml",
      "http://mochi.test:8888/tests/content/base/test/file_base_xbl.xml",
      "http://mochi.test:8888/tests/content/base/test/file_base_xbl.xml#shesellsseashellsbytheseashore",
      ];
-  node = document.getAnonymousNodes(document.getElementById("bound"))[0];
+  node = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById("bound")))[0];
   while(node) {
     is(node.baseURI, svgExpected.shift(), "node base");
     node = node.firstChild;
   }
   is(svgExpected.length, 0, "found all expected nodes");
 
   SimpleTest.finish();
 });
--- a/content/base/test/test_bug330925.xhtml
+++ b/content/base/test/test_bug330925.xhtml
@@ -41,30 +41,31 @@ xbl textnode2
 
 // We have to wait until onload because XBL doesn't bind immediately.
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(init);
 
 function init()
 {
   var t = document.getElementById("t");
+  var d = SpecialPowers.wrap(document);
 
-  is(document.getBindingParent(document.getAnonymousNodes(t)[0]), 
+  is(SpecialPowers.unwrap(d.getBindingParent(d.getAnonymousNodes(t)[0])),
      t,
      "Wrong binding parent for anonymous node");
      
-  is(document.getBindingParent(document.getAnonymousNodes(t)[1].childNodes[0]), 
+  is(SpecialPowers.unwrap(d.getBindingParent(d.getAnonymousNodes(t)[1].childNodes[0])),
      t,
      "Wrong binding parent for child of anonymous node");
      
-  is(document.getBindingParent(t), 
+  is(d.getBindingParent(t), 
      null,
      "Non-anonymous node should not have a binding parent");
      
-  is(document.getBindingParent(document.documentElement), 
+  is(d.getBindingParent(document.documentElement), 
      null,
      "Document element should not have a binding parent");
      
   SimpleTest.finish();
 }
 
 ]]>
 </script>
--- a/content/base/test/test_bug372086.html
+++ b/content/base/test/test_bug372086.html
@@ -37,34 +37,34 @@ function runTest() {
   range.setStart(c, 1);
   range.setEnd(c, 3);
   is(range.startContainer, c, "Unexpected start container");
   is(range.startOffset, 1, "Unexpected start offset");
   is(range.endContainer, c, "Unexpected end container");
   is(range.endOffset, 3, "Unexpected end offset");
   is(range.toString(), "bc", "Unexpected range serialization");
 
-  var anon = document.getAnonymousNodes($("d"))[0];
+  var anon = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes($("d")))[0];
   // Should collapse the range, because can't determine order
   range.setEnd(anon, 2);
   is(range.startContainer, anon, "Unexpected collapsed start container");
   is(range.startOffset, 2, "Unexpected collapsed start offset");
   is(range.endContainer, anon, "Unexpected collapsed end container");
   is(range.endOffset, 2, "Unexpected collapsed end offset");
   is(range.toString(), "", "Unexpected collapsed range serialization");
 
   range.setEnd(a, 2);
   range.setStart(a, 0);
   is(range.startContainer, a, "Unexpected start container after");
   is(range.startOffset, 0, "Unexpected start offset after");
   is(range.endContainer, a, "Unexpected end container after");
   is(range.endOffset, 2, "Unexpected end offset after");
   is(range.toString(), "de", "Unexpected range serialization after");
 
-  anon = document.getAnonymousNodes($("d"))[2];
+  anon = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes($("d")))[2];
   // Collapses because one endpoint is anonymous from point of view of
   // the other.  
   range.setStart(anon, 1);
   is(range.startContainer, anon, "Unexpected newly collapsed start container");
   is(range.startOffset, 1, "Unexpected newly collapsed start offset");
   is(range.endContainer, anon, "Unexpected newly collapsed end container");
   is(range.endOffset, 1, "Unexpected newly collapsed end offset");
   is(range.toString(), "", "Unexpected collapsed range serialization");
--- a/content/base/test/test_bug444030.xhtml
+++ b/content/base/test/test_bug444030.xhtml
@@ -21,17 +21,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 <![CDATA[
 
 /** Test for Bug 444030 **/
 
 function doTest() {
-  var anonTextNode = document.getAnonymousNodes(document.getElementById("boundElement"))[0];
+  var anonTextNode = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById("boundElement")))[0];
   var hadException = false;
   try {
     var wholeText = anonTextNode.wholeText;
   } catch(e) {
     hadException = true;
   }
   ok(hadException,
      "Should have got an exception when using .wholeText with a text node child of the binding parent");
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -9,18 +9,18 @@
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "gfxPattern.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x8b8da863, 0xd151, 0x4014, \
-  { 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
+{ 0x9a6a5bdf, 0x1261, 0x4057, \
+  { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
 
 class gfxContext;
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -60,16 +60,19 @@ public:
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
   // Render the canvas at the origin of the given gfxContext
   NS_IMETHOD Render(gfxContext *ctx,
                     gfxPattern::GraphicsFilter aFilter,
                     uint32_t aFlags = RenderFlagPremultAlpha) = 0;
 
+  // Creates an image buffer. Returns null on failure.
+  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
+
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *aMimeType,
                             const PRUnichar *aEncoderOptions,
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -43,17 +43,17 @@
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIXPConnect.h"
 #include "nsDisplayList.h"
 
 #include "nsTArray.h"
 
-#include "imgIEncoder.h"
+#include "ImageEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
 #include "gfxBlur.h"
 #include "gfxUtils.h"
@@ -1041,81 +1041,81 @@ CanvasRenderingContext2D::Render(gfxCont
       NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
 
       gfxUtils::UnpremultiplyImageSurface(gis);
   }
 
   return rv;
 }
 
+void
+CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
+                                         int32_t* aFormat)
+{
+  *aImageBuffer = nullptr;
+  *aFormat = 0;
+
+  nsRefPtr<gfxASurface> surface;
+  nsresult rv = GetThebesSurface(getter_AddRefs(surface));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  static const fallible_t fallible = fallible_t();
+  uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+  if (!imageBuffer) {
+    return;
+  }
+
+  nsRefPtr<gfxImageSurface> imgsurf =
+    new gfxImageSurface(imageBuffer,
+                        gfxIntSize(mWidth, mHeight),
+                        mWidth * 4,
+                        gfxASurface::ImageFormatARGB32);
+
+  if (!imgsurf || imgsurf->CairoStatus()) {
+    delete[] imageBuffer;
+    return;
+  }
+
+  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+  if (!ctx || ctx->HasError()) {
+    delete[] imageBuffer;
+    return;
+  }
+
+  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+  ctx->SetSource(surface, gfxPoint(0, 0));
+  ctx->Paint();
+
+  *aImageBuffer = imageBuffer;
+  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+}
+
 NS_IMETHODIMP
 CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
                                          const PRUnichar *aEncoderOptions,
                                          nsIInputStream **aStream)
 {
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxASurface> surface;
-
-  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+  uint8_t* imageBuffer = nullptr;
+  int32_t format = 0;
+  GetImageBuffer(&imageBuffer, &format);
+  if (!imageBuffer) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv;
-  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
-  static const fallible_t fallible = fallible_t();
-  nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
-
-  if (!conid) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  strcpy(conid, encoderPrefix);
-  strcat(conid, aMimeType);
-
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+  nsCString enccid("@mozilla.org/image/encoder;2?type=");
+  enccid += aMimeType;
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
   if (!encoder) {
     return NS_ERROR_FAILURE;
   }
 
-  nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
-  if (!imageBuffer) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsRefPtr<gfxImageSurface> imgsurf =
-    new gfxImageSurface(imageBuffer.get(),
-                        gfxIntSize(mWidth, mHeight),
-                        mWidth * 4,
-                        gfxASurface::ImageFormatARGB32);
-
-  if (!imgsurf || imgsurf->CairoStatus()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
-
-  if (!ctx || ctx->HasError()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-  ctx->SetSource(surface, gfxPoint(0, 0));
-  ctx->Paint();
-
-  rv = encoder->InitFromData(imageBuffer.get(),
-                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
-                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                              nsDependentString(aEncoderOptions));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return CallQueryInterface(encoder, aStream);
+  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
+                                      encoder, aEncoderOptions, aStream);
 }
 
 SurfaceFormat
 CanvasRenderingContext2D::GetSurfaceFormat() const
 {
   return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
 }
 
@@ -3744,16 +3744,19 @@ CanvasRenderingContext2D::PutImageData_e
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
 {
   EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
 
   nsRefPtr<gfxASurface> thebesSurface =
       gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
 
   if (!thebesSurface) {
     return NS_ERROR_FAILURE;
   }
 
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -15,16 +15,17 @@
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CanvasUtils.h"
 #include "gfxFont.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
+#include "imgIEncoder.h"
 
 class nsXULElement;
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 }
 
@@ -443,16 +444,18 @@ public:
       mDSPathBuilder->BezierTo(transform * aCP1,
                                 transform * aCP2,
                                 transform * aCP3);
     }
   }
 
   friend class CanvasRenderingContext2DUserData;
 
+  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
+
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                  unsigned char *aData, uint32_t aDataLen,
                                  bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/ImageEncoder.cpp
@@ -0,0 +1,296 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageEncoder.h"
+
+#include "mozilla/dom/CanvasRenderingContext2D.h"
+
+namespace mozilla {
+namespace dom {
+
+class EncodingCompleteEvent : public nsRunnable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  EncodingCompleteEvent(JSContext* aJSContext,
+                        nsIThread* aEncoderThread,
+                        nsIFileCallback* aCallback)
+    : mImgSize(0)
+    , mType()
+    , mImgData(nullptr)
+    , mJSContext(aJSContext)
+    , mEncoderThread(aEncoderThread)
+    , mCallback(aCallback)
+  {}
+  virtual ~EncodingCompleteEvent() {}
+
+  NS_IMETHOD Run()
+  {
+    nsRefPtr<nsDOMMemoryFile> blob =
+      new nsDOMMemoryFile(mImgData, mImgSize, mType);
+
+    if (mJSContext) {
+      JS_updateMallocCounter(mJSContext, mImgSize);
+    }
+    nsresult rv = mCallback->Receive(blob);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mEncoderThread->Shutdown();
+    return rv;
+  }
+
+  void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
+  {
+    mImgData = aImgData;
+    mImgSize = aImgSize;
+    mType = aType;
+  }
+
+private:
+  uint64_t mImgSize;
+  nsAutoString mType;
+  void* mImgData;
+  JSContext* mJSContext;
+  nsCOMPtr<nsIThread> mEncoderThread;
+  nsCOMPtr<nsIFileCallback> mCallback;
+};
+
+NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
+
+class EncodingRunnable : public nsRunnable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  EncodingRunnable(const nsAString& aType,
+                   const nsAString& aOptions,
+                   uint8_t* aImageBuffer,
+                   imgIEncoder* aEncoder,
+                   nsIThread* aOriginThread,
+                   EncodingCompleteEvent* aEncodingCompleteEvent,
+                   int32_t aFormat,
+                   const nsIntSize aSize,
+                   bool aUsingCustomOptions)
+    : mType(aType)
+    , mOptions(aOptions)
+    , mImageBuffer(aImageBuffer)
+    , mEncoder(aEncoder)
+    , mOriginThread(aOriginThread)
+    , mEncodingCompleteEvent(aEncodingCompleteEvent)
+    , mFormat(aFormat)
+    , mSize(aSize)
+    , mUsingCustomOptions(aUsingCustomOptions)
+  {}
+  virtual ~EncodingRunnable() {}
+
+  NS_IMETHOD Run()
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    nsresult rv = ImageEncoder::ExtractDataInternal(mType,
+                                                    mOptions,
+                                                    mImageBuffer,
+                                                    mFormat,
+                                                    mSize,
+                                                    nullptr,
+                                                    getter_AddRefs(stream),
+                                                    mEncoder);
+
+    // If there are unrecognized custom parse options, we should fall back to
+    // the default values for the encoder without any options at all.
+    if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
+      rv = ImageEncoder::ExtractDataInternal(mType,
+                                             EmptyString(),
+                                             mImageBuffer,
+                                             mFormat,
+                                             mSize,
+                                             nullptr,
+                                             getter_AddRefs(stream),
+                                             mEncoder);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint64_t imgSize;
+    rv = stream->Available(&imgSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+
+    void* imgData = nullptr;
+    rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
+    rv = mOriginThread->Dispatch(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return rv;
+  }
+
+private:
+  nsAutoString mType;
+  nsAutoString mOptions;
+  nsAutoArrayPtr<uint8_t> mImageBuffer;
+  nsCOMPtr<imgIEncoder> mEncoder;
+  nsCOMPtr<nsIThread> mOriginThread;
+  nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
+  int32_t mFormat;
+  const nsIntSize mSize;
+  bool mUsingCustomOptions;
+};
+
+NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
+
+/* static */
+nsresult
+ImageEncoder::ExtractData(nsAString& aType,
+                          const nsAString& aOptions,
+                          const nsIntSize aSize,
+                          nsICanvasRenderingContextInternal* aContext,
+                          nsIInputStream** aStream)
+{
+  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
+  if (!encoder) {
+    return NS_IMAGELIB_ERROR_NO_ENCODER;
+  }
+
+  return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
+                             aStream, encoder);
+}
+
+/* static */
+nsresult
+ImageEncoder::ExtractDataAsync(nsAString& aType,
+                               const nsAString& aOptions,
+                               bool aUsingCustomOptions,
+                               uint8_t* aImageBuffer,
+                               int32_t aFormat,
+                               const nsIntSize aSize,
+                               nsICanvasRenderingContextInternal* aContext,
+                               JSContext* aJSContext,
+                               nsIFileCallback* aCallback)
+{
+  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
+  if (!encoder) {
+    return NS_IMAGELIB_ERROR_NO_ENCODER;
+  }
+
+  nsCOMPtr<nsIThread> encoderThread;
+  nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<EncodingCompleteEvent> completeEvent =
+    new EncodingCompleteEvent(aJSContext, encoderThread, aCallback);
+
+  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
+                                                     aOptions,
+                                                     aImageBuffer,
+                                                     encoder,
+                                                     NS_GetCurrentThread(),
+                                                     completeEvent,
+                                                     aFormat,
+                                                     aSize,
+                                                     aUsingCustomOptions);
+  return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
+}
+
+/*static*/ nsresult
+ImageEncoder::GetInputStream(int32_t aWidth,
+                             int32_t aHeight,
+                             uint8_t* aImageBuffer,
+                             int32_t aFormat,
+                             imgIEncoder* aEncoder,
+                             const PRUnichar* aEncoderOptions,
+                             nsIInputStream** aStream)
+{
+  nsresult rv =
+    aEncoder->InitFromData(aImageBuffer,
+                           aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
+                           aFormat,
+                           nsDependentString(aEncoderOptions));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CallQueryInterface(aEncoder, aStream);
+}
+
+/* static */
+nsresult
+ImageEncoder::ExtractDataInternal(const nsAString& aType,
+                                  const nsAString& aOptions,
+                                  uint8_t* aImageBuffer,
+                                  int32_t aFormat,
+                                  const nsIntSize aSize,
+                                  nsICanvasRenderingContextInternal* aContext,
+                                  nsIInputStream** aStream,
+                                  imgIEncoder* aEncoder)
+{
+  nsCOMPtr<nsIInputStream> imgStream;
+
+  // get image bytes
+  nsresult rv;
+  if (aImageBuffer) {
+    rv = ImageEncoder::GetInputStream(
+      aSize.width,
+      aSize.height,
+      aImageBuffer,
+      aFormat,
+      aEncoder,
+      nsPromiseFlatString(aOptions).get(),
+      getter_AddRefs(imgStream));
+  } else if (aContext) {
+    NS_ConvertUTF16toUTF8 encoderType(aType);
+    rv = aContext->GetInputStream(encoderType.get(),
+                                  nsPromiseFlatString(aOptions).get(),
+                                  getter_AddRefs(imgStream));
+  } else {
+    // no context, so we have to encode an empty image
+    // note that if we didn't have a current context, the spec says we're
+    // supposed to just return transparent black pixels of the canvas
+    // dimensions.
+    nsRefPtr<gfxImageSurface> emptyCanvas =
+      new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
+                          gfxASurface::ImageFormatARGB32);
+    if (emptyCanvas->CairoStatus()) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    rv = aEncoder->InitFromData(emptyCanvas->Data(),
+                                aSize.width * aSize.height * 4,
+                                aSize.width,
+                                aSize.height,
+                                aSize.width * 4,
+                                imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                                aOptions);
+    if (NS_SUCCEEDED(rv)) {
+      imgStream = do_QueryInterface(aEncoder);
+    }
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  imgStream.forget(aStream);
+  return rv;
+}
+
+/* static */
+already_AddRefed<imgIEncoder>
+ImageEncoder::GetImageEncoder(nsAString& aType)
+{
+  // Get an image encoder for the media type.
+  nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
+  NS_ConvertUTF16toUTF8 encoderType(aType);
+  encoderCID += encoderType;
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
+
+  if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
+    // Unable to create an encoder instance of the specified type. Falling back
+    // to PNG.
+    aType.AssignLiteral("image/png");
+    nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
+    encoder = do_CreateInstance(PNGEncoderCID.get());
+  }
+
+  return encoder.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/ImageEncoder.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ImageEncoder_h
+#define ImageEncoder_h
+
+#include "imgIEncoder.h"
+#include "nsDOMFile.h"
+#include "nsError.h"
+#include "nsIDOMHTMLCanvasElement.h"
+#include "nsLayoutUtils.h"
+#include "nsNetUtil.h"
+#include "nsSize.h"
+
+class nsICanvasRenderingContextInternal;
+
+namespace mozilla {
+namespace dom {
+
+class EncodingRunnable;
+
+class ImageEncoder
+{
+public:
+  // Extracts data synchronously and gives you a stream containing the image
+  // represented by aContext. aType may change to "image/png" if we had to fall
+  // back to a PNG encoder. A return value of NS_OK implies successful data
+  // extraction. If there are any unrecognized custom parse options in
+  // aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
+  // error it is usual to call this function again without any options at all.
+  static nsresult ExtractData(nsAString& aType,
+                              const nsAString& aOptions,
+                              const nsIntSize aSize,
+                              nsICanvasRenderingContextInternal* aContext,
+                              nsIInputStream** aStream);
+
+  // Extracts data asynchronously. aType may change to "image/png" if we had to
+  // fall back to a PNG encoder. aOptions are the options to be passed to the
+  // encoder and aUsingCustomOptions specifies whether custom parse options were
+  // used (i.e. by using -moz-parse-options). If there are any unrecognized
+  // custom parse options, we fall back to the default values for the encoder
+  // without any options at all. A return value of NS_OK only implies
+  // successful dispatching of the extraction step to the encoding thread.
+  static nsresult ExtractDataAsync(nsAString& aType,
+                                   const nsAString& aOptions,
+                                   bool aUsingCustomOptions,
+                                   uint8_t* aImageBuffer,
+                                   int32_t aFormat,
+                                   const nsIntSize aSize,
+                                   nsICanvasRenderingContextInternal* aContext,
+                                   JSContext* aJSContext,
+                                   nsIFileCallback* aCallback);
+
+  // Gives you a stream containing the image represented by aImageBuffer.
+  // The format is given in aFormat, for example
+  // imgIEncoder::INPUT_FORMAT_HOSTARGB.
+  static nsresult GetInputStream(int32_t aWidth,
+                                 int32_t aHeight,
+                                 uint8_t* aImageBuffer,
+                                 int32_t aFormat,
+                                 imgIEncoder* aEncoder,
+                                 const PRUnichar* aEncoderOptions,
+                                 nsIInputStream** aStream);
+
+private:
+  // When called asynchronously, aContext is null.
+  static nsresult
+  ExtractDataInternal(const nsAString& aType,
+                      const nsAString& aOptions,
+                      uint8_t* aImageBuffer,
+                      int32_t aFormat,
+                      const nsIntSize aSize,
+                      nsICanvasRenderingContextInternal* aContext,
+                      nsIInputStream** aStream,
+                      imgIEncoder* aEncoder);
+
+  // Creates and returns an encoder instance of the type specified in aType.
+  // aType may change to "image/png" if no instance of the original type could
+  // be created and we had to fall back to a PNG encoder. A return value of
+  // NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
+  // undefined in this case.
+  static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
+
+  friend class EncodingRunnable;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // ImageEncoder_h
\ No newline at end of file
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -17,10 +17,11 @@ CXXFLAGS	+= $(MOZ_CAIRO_CFLAGS) $(MOZ_PI
 INCLUDES	+= \
 		-I$(srcdir)/../../../layout/xul/base/src \
 		-I$(srcdir)/../../../layout/style \
 		-I$(srcdir)/../../../layout/generic \
 		-I$(srcdir)/../../base/src \
 		-I$(srcdir)/../../html/content/src \
 		-I$(srcdir)/../../../js/xpconnect/src \
 		-I$(srcdir)/../../../dom/base \
+		-I$(srcdir)/../../../image/src \
 		-I$(topsrcdir)/content/xul/content/src \
 		$(NULL)
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -22,17 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsIGfxInfo.h"
 #include "nsIWidget.h"
 
 #include "nsIVariant.h"
 
-#include "imgIEncoder.h"
+#include "ImageEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
 #include "nsDisplayList.h"
 
@@ -716,67 +716,89 @@ void WebGLContext::LoseOldestWebGLContex
     } else if (numContexts > kMaxWebGLContexts) {
         GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
                         kMaxWebGLContexts);
         MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
         const_cast<WebGLContext*>(oldestContext)->LoseContext();
     }
 }
 
+void
+WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
+{
+    *aImageBuffer = nullptr;
+    *aFormat = 0;
+
+    nsRefPtr<gfxImageSurface> imgsurf =
+        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                            gfxASurface::ImageFormatARGB32);
+
+    if (!imgsurf || imgsurf->CairoStatus()) {
+        return;
+    }
+
+    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+    if (!ctx || ctx->HasError()) {
+        return;
+    }
+
+    // Use Render() to make sure that appropriate y-flip gets applied
+    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
+    nsresult rv = Render(ctx, gfxPattern::FILTER_NEAREST, flags);
+    if (NS_FAILED(rv)) {
+        return;
+    }
+
+    int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+    if (!mOptions.premultipliedAlpha) {
+        // We need to convert to INPUT_FORMAT_RGBA, otherwise
+        // we are automatically considered premult, and unpremult'd.
+        // Yes, it is THAT silly.
+        // Except for different lossy conversions by color,
+        // we could probably just change the label, and not change the data.
+        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
+        format = imgIEncoder::INPUT_FORMAT_RGBA;
+    }
+
+    static const fallible_t fallible = fallible_t();
+    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+    if (!imageBuffer) {
+        return;
+    }
+    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
+
+    *aImageBuffer = imageBuffer;
+    *aFormat = format;
+}
+
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const PRUnichar* aEncoderOptions,
                              nsIInputStream **aStream)
 {
     NS_ASSERTION(gl, "GetInputStream on invalid context?");
     if (!gl)
         return NS_ERROR_FAILURE;
 
-    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                                                         gfxASurface::ImageFormatARGB32);
-    if (surf->CairoStatus() != 0)
+    uint8_t* imageBuffer = nullptr;
+    int32_t format = 0;
+    GetImageBuffer(&imageBuffer, &format);
+    if (!imageBuffer) {
         return NS_ERROR_FAILURE;
-
-    nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
-    // Use Render() to make sure that appropriate y-flip gets applied
-    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
-    nsresult rv = Render(tmpcx, gfxPattern::FILTER_NEAREST, flags);
-    if (NS_FAILED(rv))
-        return rv;
-
-    const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
-    nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
-
-    strcpy(conid, encoderPrefix);
-    strcat(conid, aMimeType);
-
-    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
-    if (!encoder)
-        return NS_ERROR_FAILURE;
-
-    int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
-    if (!mOptions.premultipliedAlpha) {
-        // We need to convert to INPUT_FORMAT_RGBA, otherwise
-        // we are automatically considered premult, and unpremult'd.
-        // Yes, it is THAT silly.
-        // Except for different lossy conversions by color,
-        // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(surf);
-        format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
-    rv = encoder->InitFromData(surf->Data(),
-                               mWidth * mHeight * 4,
-                               mWidth, mHeight,
-                               surf->Stride(),
-                               format,
-                               nsDependentString(aEncoderOptions));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCString enccid("@mozilla.org/image/encoder;2?type=");
+    enccid += aMimeType;
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
+    if (!encoder) {
+        return NS_ERROR_FAILURE;
+    }
 
-    return CallQueryInterface(encoder, aStream);
+    return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
+                                        encoder, aEncoderOptions, aStream);
 }
 
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -155,16 +155,17 @@ public:
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset() MOZ_OVERRIDE
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Render(gfxContext *ctx,
                       gfxPattern::GraphicsFilter f,
                       uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
+    virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream) MOZ_OVERRIDE;
     NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE
         { return nullptr; }
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -17,16 +17,17 @@ EXPORTS.mozilla.dom += [
 
 CPP_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageData.cpp',
+    'ImageEncoder.cpp',
 ]
 
 if CONFIG['MOZ_WEBGL']:
     CPP_SOURCES += [
         'WebGLActiveInfo.cpp',
         'WebGLBuffer.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
--- a/content/canvas/test/test_mozGetAsFile.html
+++ b/content/canvas/test/test_mozGetAsFile.html
@@ -2,49 +2,50 @@
 <title>Canvas test: mozGetAsFile</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="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-var gCompares = 0;
-
-function compareAsync(file, canvas, type)
+function compareAsync(file, canvas, type, callback)
 {
-  ++gCompares;
-
   var reader = new FileReader();
   reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      if (--gCompares == 0) {
-        SimpleTest.finish();
-      }
+      callback(canvas);
     };
   reader.readAsDataURL(file);
 }
 
+function test1(canvas)
+{
+  var pngfile = canvas.mozGetAsFile("foo.png");
+  is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
+  compareAsync(pngfile, canvas, "image/png", test2);
+  is(pngfile.name, "foo.png", "File name should be what we passed in");
+}
+
+function test2(canvas)
+{
+  var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
+  is(jpegfile.type, "image/jpeg",
+     "When a valid type is specified that should be returned");
+  compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
+  is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
+}
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
-
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-var pngfile = canvas.mozGetAsFile("foo.png");
-is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
-compareAsync(pngfile, canvas, "image/png");
-is(pngfile.name, "foo.png", "File name should be what we passed in");
-
-var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
-is(jpegfile.type, "image/jpeg",
-   "When a valid type is specified that should be returned");
-compareAsync(jpegfile, canvas, "image/jpeg");
-is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
+test1(canvas);
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/canvas/test/test_toBlob.html
+++ b/content/canvas/test/test_toBlob.html
@@ -1,42 +1,48 @@
 <!DOCTYPE HTML>
-<title>Canvas test: mozGetAsFile</title>
+<title>Canvas test: toBlob</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="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-var gCompares = 2;
-
-function BlobListener(type, canvas, file)
+function BlobListener(type, canvas, callback, file)
 {
   is(file.type, type,
      "When a valid type is specified that should be returned");
   var reader = new FileReader();
-  reader.onload = 
+  reader.onload =
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
- "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      if (--gCompares == 0) {
-        SimpleTest.finish();
-      }
+  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
+      callback(canvas);
     };
   reader.readAsDataURL(file);
 }
 
+function test1(canvas)
+{
+  canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
+}
+
+function test2(canvas)
+{
+  canvas.toBlob(
+    BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
+    "image/jpeg");
+}
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
-
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
-canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
+test1(canvas);
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/html/content/crashtests/465466-1.xhtml
+++ b/content/html/content/crashtests/465466-1.xhtml
@@ -6,17 +6,17 @@
 </binding></bindings>
 
 
 <script type="text/javascript">
 
 function boom()
 {
   var f = document.getElementById("f");
-  var anon = document.getAnonymousNodes(f)[0];
+  var anon = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(f))[0];
   document.body.removeChild(f);
   anon.appendChild(document.createElement("label"));
 }
 
 </script>
 </head>
 
 <body onload="boom();"><form id="f" style="-moz-binding: url(#u);"><label></label></form></body>
--- a/content/html/content/public/HTMLCanvasElement.h
+++ b/content/html/content/public/HTMLCanvasElement.h
@@ -222,20 +222,19 @@ protected:
   nsIntSize GetWidthHeight();
 
   nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
   nsresult ParseParams(JSContext* aCx,
                        const nsAString& aType,
                        const JS::Value& aEncoderOptions,
                        nsAString& aParams,
                        bool* usingCustomParseOptions);
-  nsresult ExtractData(const nsAString& aType,
+  nsresult ExtractData(nsAString& aType,
                        const nsAString& aOptions,
-                       nsIInputStream** aStream,
-                       bool& aFellBackToPNG);
+                       nsIInputStream** aStream);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLCanvasElement.h"
 
-#include "Layers.h"
-#include "imgIEncoder.h"
+#include "ImageEncoder.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "Layers.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
@@ -40,38 +40,16 @@ using namespace mozilla::layers;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
 
 namespace {
 
 typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
 HTMLImageOrCanvasOrVideoElement;
 
-class ToBlobRunnable : public nsRunnable
-{
-public:
-  ToBlobRunnable(nsIFileCallback* aCallback,
-                 nsIDOMBlob* aBlob)
-    : mCallback(aCallback),
-      mBlob(aBlob)
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  }
-
-  NS_IMETHOD Run()
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    mCallback->Receive(mBlob);
-    return NS_OK;
-  }
-private:
-  nsCOMPtr<nsIFileCallback> mCallback;
-  nsCOMPtr<nsIDOMBlob> mBlob;
-};
-
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 class HTMLCanvasPrintState : public nsIDOMMozCanvasPrintState
 {
 public:
@@ -362,20 +340,20 @@ HTMLCanvasElement::ToDataURL(const nsASt
 NS_IMETHODIMP
 HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
                                     const nsAString& aType)
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_ERROR_FAILURE;
 
   nsresult rv;
-  bool fellBackToPNG = false;
   nsCOMPtr<nsIInputStream> inputData;
 
-  rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
+  nsAutoString type(aType);
+  rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIThread> mainThread;
   rv = NS_GetMainThread(getter_AddRefs(mainThread));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -405,78 +383,25 @@ HTMLCanvasElement::GetMozPrintCallback()
 NS_IMETHODIMP
 HTMLCanvasElement::GetMozPrintCallback(nsIPrintCallback** aCallback)
 {
   NS_IF_ADDREF(*aCallback = GetMozPrintCallback());
   return NS_OK;
 }
 
 nsresult
-HTMLCanvasElement::ExtractData(const nsAString& aType,
+HTMLCanvasElement::ExtractData(nsAString& aType,
                                const nsAString& aOptions,
-                               nsIInputStream** aStream,
-                               bool& aFellBackToPNG)
+                               nsIInputStream** aStream)
 {
-  // note that if we don't have a current context, the spec says we're
-  // supposed to just return transparent black pixels of the canvas
-  // dimensions.
-  nsRefPtr<gfxImageSurface> emptyCanvas;
-  nsIntSize size = GetWidthHeight();
-  if (!mCurrentContext) {
-    emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
-    if (emptyCanvas->CairoStatus()) {
-      return NS_ERROR_INVALID_ARG;
-    }
-  }
-
-  nsresult rv;
-
-  // get image bytes
-  nsCOMPtr<nsIInputStream> imgStream;
-  NS_ConvertUTF16toUTF8 encoderType(aType);
-
- try_again:
-  if (mCurrentContext) {
-    rv = mCurrentContext->GetInputStream(encoderType.get(),
-                                         nsPromiseFlatString(aOptions).get(),
-                                         getter_AddRefs(imgStream));
-  } else {
-    // no context, so we have to encode the empty image we created above
-    nsCString enccid("@mozilla.org/image/encoder;2?type=");
-    enccid += encoderType;
-
-    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
-    if (NS_SUCCEEDED(rv) && encoder) {
-      rv = encoder->InitFromData(emptyCanvas->Data(),
-                                 size.width * size.height * 4,
-                                 size.width,
-                                 size.height,
-                                 size.width * 4,
-                                 imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                                 aOptions);
-      if (NS_SUCCEEDED(rv)) {
-        imgStream = do_QueryInterface(encoder);
-      }
-    } else {
-      rv = NS_ERROR_FAILURE;
-    }
-  }
-
-  if (NS_FAILED(rv) && !aFellBackToPNG) {
-    // Try image/png instead.
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    aFellBackToPNG = true;
-    encoderType.AssignLiteral("image/png");
-    goto try_again;
-  }
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  imgStream.forget(aStream);
-  return NS_OK;
+  return ImageEncoder::ExtractData(aType,
+                                   aOptions,
+                                   GetSize(),
+                                   mCurrentContext,
+                                   aStream);
 }
 
 nsresult
 HTMLCanvasElement::ParseParams(JSContext* aCx,
                                const nsAString& aType,
                                const JS::Value& aEncoderOptions,
                                nsAString& aParams,
                                bool* usingCustomParseOptions)
@@ -517,18 +442,16 @@ HTMLCanvasElement::ParseParams(JSContext
 }
 
 nsresult
 HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
                                  const nsAString& aMimeType,
                                  const JS::Value& aEncoderOptions,
                                  nsAString& aDataURL)
 {
-  bool fallbackToPNG = false;
-
   nsIntSize size = GetWidthHeight();
   if (size.height == 0 || size.width == 0) {
     aDataURL = NS_LITERAL_STRING("data:,");
     return NS_OK;
   }
 
   nsAutoString type;
   nsresult rv = nsContentUtils::ASCIIToLower(aMimeType, type);
@@ -539,43 +462,37 @@ HTMLCanvasElement::ToDataURLImpl(JSConte
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsCOMPtr<nsIInputStream> stream;
-  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
+  rv = ExtractData(type, params, getter_AddRefs(stream));
 
   // If there are unrecognized custom parse options, we should fall back to
   // the default values for the encoder without any options at all.
   if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
-    fallbackToPNG = false;
-    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
+    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // build data URL string
-  if (fallbackToPNG)
-    aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
-  else
-    aDataURL = NS_LITERAL_STRING("data:") + type +
-      NS_LITERAL_STRING(";base64,");
+  aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
 
   uint64_t count;
   rv = stream->Available(&count);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
 }
 
-// XXXkhuey the encoding should be off the main thread, but we're lazy.
 NS_IMETHODIMP
 HTMLCanvasElement::ToBlob(nsIFileCallback* aCallback,
                           const nsAString& aType,
                           const JS::Value& aEncoderOptions,
                           JSContext* aCx)
 {
   // do a trust check if this is a write-only canvas
   if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
@@ -594,53 +511,34 @@ HTMLCanvasElement::ToBlob(nsIFileCallbac
 
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  bool fallbackToPNG = false;
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  nsCOMPtr<nsIThread> currentThread = NS_GetCurrentThread();
 
-  nsCOMPtr<nsIInputStream> stream;
-  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
-  // If there are unrecognized custom parse options, we should fall back to
-  // the default values for the encoder without any options at all.
-  if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
-    fallbackToPNG = false;
-    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
-  }
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (fallbackToPNG) {
-    type.AssignLiteral("image/png");
+  uint8_t* imageBuffer = nullptr;
+  int32_t format = 0;
+  if (mCurrentContext) {
+    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
   }
 
-  uint64_t imgSize;
-  rv = stream->Available(&imgSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-
-  void* imgData = nullptr;
-  rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // The DOMFile takes ownership of the buffer
-  nsRefPtr<nsDOMMemoryFile> blob =
-    new nsDOMMemoryFile(imgData, imgSize, type);
-
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  if (cx) {
-    JS_updateMallocCounter(cx, imgSize);
-  }
-
-  nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
-  return NS_DispatchToCurrentThread(runnable);
+  return ImageEncoder::ExtractDataAsync(type,
+                                        params,
+                                        usingCustomParseOptions,
+                                        imageBuffer,
+                                        format,
+                                        GetSize(),
+                                        mCurrentContext,
+                                        cx,
+                                        aCallback);
 }
 
 already_AddRefed<nsIDOMFile>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
                                 ErrorResult& aRv)
 {
   nsCOMPtr<nsIDOMFile> file;
@@ -664,28 +562,21 @@ HTMLCanvasElement::MozGetAsFile(const ns
   return MozGetAsFileImpl(aName, aType, aResult);
 }
 
 nsresult
 HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
                                     const nsAString& aType,
                                     nsIDOMFile** aResult)
 {
-  bool fallbackToPNG = false;
-
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
-                            fallbackToPNG);
+  nsAutoString type(aType);
+  nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsAutoString type(aType);
-  if (fallbackToPNG) {
-    type.AssignLiteral("image/png");
-  }
-
   uint64_t imgSize;
   rv = stream->Available(&imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   void* imgData = nullptr;
   rv = NS_ReadInputStreamToBuffer(stream, &imgData, (uint32_t)imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -17,12 +17,13 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../layout/tables \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir)/../../../../editor/txmgr/src \
 		-I$(srcdir)/../../../../netwerk/base/src \
+		-I$(srcdir)/../../../../content/canvas/src \
 		-I$(srcdir) \
 		-I$(topsrcdir)/xpcom/ds \
 		-I$(topsrcdir)/content/media/ \
 		$(NULL)
--- a/content/xbl/crashtests/472260-1.xhtml
+++ b/content/xbl/crashtests/472260-1.xhtml
@@ -7,17 +7,17 @@
 </bindings>
 
 <script type="text/javascript">
 // <![CDATA[
 
 function boom()
 {
   var bo = document.getElementById("bo");
-  var anon = document.getAnonymousNodes(bo)[0];
+  var anon = SpecialPowers.wrap(document).getAnonymousNodes(bo)[0];
 
   bo.style.MozBinding = "url(#bar)";
 
   var fr = document.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
   fr.setAttribute("src", "javascript:void 0;"); 
   anon.appendChild(fr);
   
   document.documentElement.removeAttribute("class");
--- a/content/xbl/test/file_bug379959_cross.html
+++ b/content/xbl/test/file_bug379959_cross.html
@@ -11,15 +11,15 @@
   -moz-binding: url(http://example.com/tests/content/xbl/test/file_bug379959_xbl.xml#xbltest);
 }
 </style>
 <body>
 <div id="div1"></div>
 <div id="div2"></div>
 <script>
 onload = function() {
-  nodes = document.getAnonymousNodes(document.getElementById('div1'));
+  nodes = SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById('div1'));
   parent.postMessage(nodes ? nodes.length : 0, "http://mochi.test:8888");
-  nodes = document.getAnonymousNodes(document.getElementById('div2'));
+  nodes = SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById('div2'));
   parent.postMessage(nodes ? nodes.length : 0, "http://mochi.test:8888");
 }
 </script>
 </html>
--- a/content/xbl/test/file_bug379959_data.html
+++ b/content/xbl/test/file_bug379959_data.html
@@ -6,13 +6,13 @@
   color: green;
   -moz-binding: url(data:text/xml;charset=utf-8,%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3Cbindings%20id%3D%22xbltestBindings%22%20xmlns%3D%22http%3A//www.mozilla.org/xbl%22%3E%0A%20%20%3Cbinding%20id%3D%22xbltest%22%3E%3Ccontent%3EPASS%3C/content%3E%3C/binding%3E%0A%3C/bindings%3E%0A);
 }
 </style>
 <body>
 <div id="d"></div>
 <script>
 onload = function() {
-  nodes = document.getAnonymousNodes(document.getElementById('d'));
+  nodes = SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById('d'));
   parent.postMessage(nodes ? nodes.length : 0, "http://mochi.test:8888");
 }
 </script>
 </html>
--- a/content/xbl/test/file_bug591198_inner.html
+++ b/content/xbl/test/file_bug591198_inner.html
@@ -17,17 +17,17 @@ function sendResults() {
 
   ps = document.getElementsByTagName('span');
   for (var i = 0; i < ps.length; i++) {
     res.widths.push(ps[i].offsetWidth);
   }
   
   try {
     res.anonChildCount =
-      document.getAnonymousNodes(document.getElementById('b')).length;
+      SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById('b')).length;
   }
   catch (ex) {}
 
   parent.postMessage(JSON.stringify(res), "*");
 }
     </script>
   </head>
   <body onload="sendResults();">
--- a/content/xbl/test/test_bug378866.xhtml
+++ b/content/xbl/test/test_bug378866.xhtml
@@ -28,17 +28,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 <![CDATA[
 
 /** Test for Bug 378866 **/
 
 function runTest() {
-  var anon = document.getAnonymousNodes(document.getElementById('grandparent'));
+  var anon = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(document.getElementById('grandparent')));
   var child = document.getElementById('child');
   var insertionPoint = anon[0].childNodes[0];
   insertionPoint.parentNode.removeChild(insertionPoint);
   child.appendChild(insertionPoint);
 
   var e = document.createEvent("Event");
   e.initEvent("foo", true, true);
   child.dispatchEvent(e);
--- a/content/xbl/test/test_bug468210.xhtml
+++ b/content/xbl/test/test_bug468210.xhtml
@@ -27,17 +27,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <![CDATA[
 
 /** Test for Bug 468210 **/
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var div = $("d");
   var n = document.anchors.length;
   is(n, 1, "Unexpected number of anchors");
-  var anon = document.getAnonymousNodes(div)[0];
+  var anon = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(div))[0];
   is(anon instanceof HTMLSpanElement, true, "Unexpected node");
   is(anon.parentNode, div, "Unexpected parent");
   document.body.appendChild(div);
   is(anon.parentNode, null, "Parent should have become null");
   // An attr set to test notifications
   anon.setAttribute("h", "i");
 });
 addLoadEvent(SimpleTest.finish);
--- a/content/xslt/tests/mochitest/test_bug319374.xhtml
+++ b/content/xslt/tests/mochitest/test_bug319374.xhtml
@@ -26,45 +26,48 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 319374 **/
 
   function testChangesInAnonymousTree() {
     // Test 1: Make sure that modifying anonymous content doesn't 
     //         cause non-anonymous XPath result to throw exceptions..
     var counter = 0;
     var error = null;
+    function getAnonymousNodes(e) {
+      return SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(e));
+    }
     try {
       var xp = new XPathEvaluator();
       var result = xp.evaluate("*",
                                document.getElementById('content'),
                                null,
                                SpecialPowers.Ci.nsIDOMXPathResult.
                                  UNORDERED_NODE_ITERATOR_TYPE,
                                null);
       var res = null;
       while (res = result.iterateNext()) {
         ++counter; 
-        var anon = document.getAnonymousNodes(res);
+        var anon = getAnonymousNodes(res);
         anon[0].removeChild(anon[0].firstChild); // Removing a child node
         anon[0].removeAttribute("attr1"); // Removing an attribute
         anon[1].firstChild.data = "anon text changed" // Modifying text data
       }
     } catch (e) {
       error = e;
     }
     ok(!error, error);
     ok(counter == 3, "XPathEvaluator should have found 3 elements.")
 
     // Test 2: If the context node is in anonymous content, changing some
     //         other anonymous tree shouldn't cause XPath result to throw.
     var anonAttr1 =
-      document.getAnonymousNodes(document.getElementById('content').
+      getAnonymousNodes(document.getElementById('content').
         firstChild)[0].getAttributeNode('attr');
     var anonAttr2 =
-      document.getAnonymousNodes(document.getElementById('content').
+      getAnonymousNodes(document.getElementById('content').
         lastChild)[0].getAttributeNode('attr');
     var resultAttr = null;
     try {
       var xp2 = xp.evaluate(".",
                             anonAttr1,
                             null,
                             SpecialPowers.Ci.nsIDOMXPathResult.
                               UNORDERED_NODE_ITERATOR_TYPE,
--- a/content/xul/content/crashtests/326864-1.xul
+++ b/content/xul/content/crashtests/326864-1.xul
@@ -1,23 +1,27 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
 
 <script>
 
+function getAnonymousNodes(e) {
+  return SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(e));
+}
+
 function init()
 {
   var tt = document.getElementById("textbox");
-  var hb = document.getAnonymousNodes(tt)[0]; // hbox
-  var men = document.getAnonymousNodes(hb)[1]; // menupopup
+  var hb = getAnonymousNodes(tt)[0]; // hbox
+  var men = getAnonymousNodes(hb)[1]; // menupopup
   var menitem = men.childNodes[0]; // menuitem
-  var hb2 = document.getAnonymousNodes(menitem)[1]; // hbox
+  var hb2 = getAnonymousNodes(menitem)[1]; // hbox
   var label2 = hb2.childNodes[0]; // label
 
   men.menu = null;
   label2.click();
 }
 
 window.addEventListener("load", init, false);
 
--- a/content/xul/content/crashtests/384740-1.xul
+++ b/content/xul/content/crashtests/384740-1.xul
@@ -7,17 +7,17 @@
 <script>
 
 function boom()
 {
   var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
   var scrollbar = document.createElementNS(XUL_NS, 'scrollbar');
   document.documentElement.appendChild(scrollbar);
-  var sbb = document.getAnonymousNodes(scrollbar)[0];
+  var sbb = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(scrollbar))[0];
   var action = document.createElementNS(XUL_NS, 'action');
   action.setAttribute('datasources', "");
   sbb.appendChild(action);
 }
 
 </script>
 
 </window>
--- a/dom/base/crashtests/404869-1.xul
+++ b/dom/base/crashtests/404869-1.xul
@@ -5,17 +5,17 @@
   <binding id="empty"><content></content></binding>
 </bindings>
 
 <script type="text/javascript">
 
 function boom()
 {
   var menupopup = document.getElementById("menupopup");
-  var x = document.getAnonymousNodes(menupopup)[0];
+  var x = SpecialPowers.wrap(document).getAnonymousNodes(menupopup)[0];
   menupopup.style.MozBinding = "url('#empty')";
   for (var ppp in x) {
   }
 }
 
 </script>
 
 <menupopup id="menupopup"/>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -21,16 +21,18 @@
 #include "nsIXPConnect.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XPCQuickStubs.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "prprf.h"
 
+#include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/DOMErrorBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 
 namespace mozilla {
 namespace dom {
@@ -181,16 +183,51 @@ ErrorResult::ReportJSException(JSContext
     JS_SetPendingException(cx, mJSException);
   }
   // If JS_WrapValue failed, not much we can do about it...  No matter
   // what, go ahead and unroot mJSException.
   JS_RemoveValueRoot(cx, &mJSException);
 }
 
 void
+ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
+{
+  MOZ_ASSERT(!mMightHaveUnreportedJSException,
+             "Why didn't you tell us you planned to handle JS exceptions?");
+
+  dom::DOMError* domError;
+  nsresult rv = UNWRAP_OBJECT(DOMError, aCx, &mJSException.toObject(),
+                              domError);
+  if (NS_FAILED(rv)) {
+    // Unwrapping really shouldn't fail here, if mExceptionHandling is set to
+    // eRethrowContentExceptions then the CallSetup destructor only stores an
+    // exception if it unwraps to DOMError. If we reach this then either
+    // mExceptionHandling wasn't set to eRethrowContentExceptions and we
+    // shouldn't be calling ReportJSExceptionFromJSImplementation or something
+    // went really wrong.
+    NS_RUNTIMEABORT("We stored a non-DOMError exception!");
+  }
+
+  nsString message;
+  domError->GetMessage(message);
+
+  JSErrorReport errorReport;
+  memset(&errorReport, 0, sizeof(JSErrorReport));
+  errorReport.errorNumber = JSMSG_USER_DEFINED_ERROR;
+  errorReport.ucmessage = message.get();
+  errorReport.exnType = JSEXN_ERR;
+  JS_ThrowReportedError(aCx, nullptr, &errorReport);
+  JS_RemoveValueRoot(aCx, &mJSException);
+  
+  // We no longer have a useful exception but we do want to signal that an error
+  // occured.
+  mResult = NS_ERROR_FAILURE;
+}
+
+void
 ErrorResult::StealJSException(JSContext* cx,
                               JS::MutableHandle<JS::Value> value)
 {
   MOZ_ASSERT(!mMightHaveUnreportedJSException,
              "Must call WouldReportJSException unconditionally in all codepaths that might call StealJSException");
   MOZ_ASSERT(IsJSException(), "No exception to steal");
 
   value.set(mJSException);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -84,24 +84,29 @@ Throw(JSContext* cx, nsresult rv)
   }
   return false;
 }
 
 template<bool mainThread>
 inline bool
 ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
                              const char* ifaceName,
-                             const char* memberName)
+                             const char* memberName,
+                             bool reportJSContentExceptions = false)
 {
   if (rv.IsTypeError()) {
     rv.ReportTypeError(cx);
     return false;
   }
   if (rv.IsJSException()) {
-    rv.ReportJSException(cx);
+    if (reportJSContentExceptions) {
+      rv.ReportJSExceptionFromJSImplementation(cx);
+    } else {
+      rv.ReportJSException(cx);
+    }
     return false;
   }
   if (rv.IsNotEnoughArgsError()) {
     rv.ReportNotEnoughArgsError(cx, ifaceName, memberName);
   }
   return Throw<mainThread>(cx, rv.ErrorCode());
 }
 
@@ -178,18 +183,18 @@ UnwrapDOMObjectToISupports(JSObject* aOb
 inline bool
 IsDOMObject(JSObject* obj)
 {
   js::Class* clasp = js::GetObjectClass(obj);
   return IsDOMClass(clasp) || IsDOMProxy(obj, clasp);
 }
 
 #define UNWRAP_OBJECT(Interface, cx, obj, value)                             \
-  UnwrapObject<prototypes::id::Interface,                                    \
-               mozilla::dom::Interface##Binding::NativeType>(cx, obj, value)
+  mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
+    mozilla::dom::Interface##Binding::NativeType>(cx, obj, value)
 
 // Some callers don't want to set an exception when unwrapping fails
 // (for example, overload resolution uses unwrapping to tell what sort
 // of thing it's looking at).
 // U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
 template <prototypes::ID PrototypeID, class T, typename U>
 MOZ_ALWAYS_INLINE nsresult
 UnwrapObject(JSContext* cx, JSObject* obj, U& value)
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "mozilla/dom/CallbackObject.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/DOMErrorBinding.h"
 #include "jsfriendapi.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIScriptSecurityManager.h"
@@ -35,18 +38,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallback)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
                                      ErrorResult& aRv,
-                                     ExceptionHandling aExceptionHandling)
+                                     ExceptionHandling aExceptionHandling,
+                                     JSCompartment* aCompartment)
   : mCx(nullptr)
+  , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
 {
   // We need to produce a useful JSContext here.  Ideally one that the callback
   // is in some sense associated with, so that we can sort of treat it as a
   // "script entry point".  Though once we actually have script entry points,
   // we'll need to do the script entry point bits once we have an actual
   // callable.
@@ -118,35 +123,65 @@ CallbackObject::CallSetup::CallSetup(JS:
 
   // Enter the compartment of our callback, so we can actually work with it.
   mAc.construct(cx, aCallback);
 
   // And now we're ready to go.
   mCx = cx;
 
   // Make sure the JS engine doesn't report exceptions we want to re-throw
-  if (mExceptionHandling == eRethrowExceptions) {
+  if (mExceptionHandling == eRethrowContentExceptions ||
+      mExceptionHandling == eRethrowExceptions) {
     mSavedJSContextOptions = JS_GetOptions(cx);
     JS_SetOptions(cx, mSavedJSContextOptions | JSOPTION_DONT_REPORT_UNCAUGHT);
   }
 }
 
+bool
+CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
+{
+  if (mExceptionHandling == eRethrowExceptions) {
+    return true;
+  }
+
+  MOZ_ASSERT(mExceptionHandling == eRethrowContentExceptions);
+
+  // For eRethrowContentExceptions we only want to throw an exception if the
+  // object that was thrown is a DOMError object in the caller compartment
+  // (which we stored in mCompartment).
+
+  if (!aException.isObject()) {
+    return false;
+  }
+
+  JS::Rooted<JSObject*> obj(mCx, &aException.toObject());
+  obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false);
+  if (js::GetObjectCompartment(obj) != mCompartment) {
+    return false;
+  }
+
+  DOMError* domError;
+  return NS_SUCCEEDED(UNWRAP_OBJECT(DOMError, mCx, obj, domError));
+}
+
 CallbackObject::CallSetup::~CallSetup()
 {
   // First things first: if we have a JSContext, report any pending
   // errors on it, unless we were told to re-throw them.
   if (mCx) {
     bool dealtWithPendingException = false;
-    if (mExceptionHandling == eRethrowExceptions) {
+    if (mExceptionHandling == eRethrowContentExceptions ||
+        mExceptionHandling == eRethrowExceptions) {
       // Restore the old context options
       JS_SetOptions(mCx, mSavedJSContextOptions);
       mErrorResult.MightThrowJSException();
       if (JS_IsExceptionPending(mCx)) {
         JS::Rooted<JS::Value> exn(mCx);
-        if (JS_GetPendingException(mCx, exn.address())) {
+        if (JS_GetPendingException(mCx, exn.address()) &&
+            ShouldRethrowException(exn)) {
           mErrorResult.ThrowJSException(mCx, exn);
           JS_ClearPendingException(mCx);
           dealtWithPendingException = true;
         }
       }
     }
 
     if (!dealtWithPendingException) {
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -73,17 +73,23 @@ public:
   JS::Handle<JSObject*> CallbackPreserveColor() const
   {
     // Calling fromMarkedLocation() is safe because we trace our mCallback, and
     // because the value of mCallback cannot change after if has been set.
     return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
   }
 
   enum ExceptionHandling {
+    // Report any exception and don't throw it to the caller code.
     eReportExceptions,
+    // Throw an exception to the caller code if the thrown exception is a
+    // binding object for a DOMError from the caller's scope, otherwise report
+    // it.
+    eRethrowContentExceptions,
+    // Throw any exception to the caller code.
     eRethrowExceptions
   };
 
 protected:
   explicit CallbackObject(CallbackObject* aCallbackObject)
   {
     Init(aCallbackObject->mCallback);
   }
@@ -113,32 +119,41 @@ protected:
   {
     /**
      * A class that performs whatever setup we need to safely make a
      * call while this class is on the stack, After the constructor
      * returns, the call is safe to make if GetContext() returns
      * non-null.
      */
   public:
+    // If aExceptionHandling == eRethrowContentExceptions then aCompartment
+    // needs to be set to the caller's compartment.
     CallSetup(JS::Handle<JSObject*> aCallable, ErrorResult& aRv,
-              ExceptionHandling aExceptionHandling);
+              ExceptionHandling aExceptionHandling,
+              JSCompartment* aCompartment = nullptr);
     ~CallSetup();
 
     JSContext* GetContext() const
     {
       return mCx;
     }
 
   private:
     // We better not get copy-constructed
     CallSetup(const CallSetup&) MOZ_DELETE;
 
+    bool ShouldRethrowException(JS::Handle<JS::Value> aException);
+
     // Members which can go away whenever
     JSContext* mCx;
 
+    // Caller's compartment. This will only have a sensible value if
+    // mExceptionHandling == eRethrowContentExceptions.
+    JSCompartment* mCompartment;
+
     // And now members whose construction/destruction order we need to control.
 
     // Put our nsAutoMicrotask first, so it gets destroyed after everything else
     // is gone
     nsAutoMicroTask mMt;
 
     nsCxPusher mCxPusher;
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4802,25 +4802,36 @@ if (global.Failed()) {
         # For JS-implemented interfaces we do not want to base the
         # needsCx decision on the types involved, just on our extended
         # attributes.
         needsCx = needCx(returnType, arguments, self.extendedAttributes,
                          descriptor, not descriptor.interface.isJSImplemented())
         if needsCx and not (static and descriptor.workers):
             argsPre.append("cx")
 
-        needsUnwrap = isConstructor
-        if needScopeObject(returnType, arguments, self.extendedAttributes,
-                           descriptor, descriptor.wrapperCache,
-                           not descriptor.interface.isJSImplemented()):
+        needsUnwrap = False
+        if isConstructor:
+            needsUnwrap = True
+            needsUnwrappedVar = False
+            unwrappedVar = "obj"
+        elif descriptor.interface.isJSImplemented():
+            needsUnwrap = True
+            needsUnwrappedVar = True
+            argsPre.append("js::GetObjectCompartment(unwrappedObj.empty() ? obj : unwrappedObj.ref())")
+        elif needScopeObject(returnType, arguments, self.extendedAttributes,
+                             descriptor, descriptor.wrapperCache, True):
+            needsUnwrap = True
+            needsUnwrappedVar = True
+            argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()")
+
+        if needsUnwrap and needsUnwrappedVar:
             # We cannot assign into obj because it's a Handle, not a
             # MutableHandle, so we need a separate Rooted.
             cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;"))
-            argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()")
-            needsUnwrap = True
+            unwrappedVar = "unwrappedObj.ref()"
 
         if idlNode.isMethod() and idlNode.isLegacycaller():
             # If we can have legacycaller with identifier, we can't
             # just use the idlNode to determine whether we're
             # generating code for the legacycaller or not.
             assert idlNode.isIdentifierLess()
             # Pass in our thisVal
             argsPre.append("args.thisv()")
@@ -4839,27 +4850,27 @@ if (global.Failed()) {
                                              invalidEnumValueFatal=not setter,
                                              allowTreatNonCallableAsNull=setter,
                                              lenientFloatCode=lenientFloatCode) for
                          i in range(argConversionStartsAt, self.argCount)])
 
         if needsUnwrap:
             # Something depends on having the unwrapped object, so unwrap it now.
             xraySteps = []
-            if not isConstructor:
+            if needsUnwrappedVar:
                 xraySteps.append(
                     CGGeneric("unwrappedObj.construct(cx, obj);"))
 
             # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
             # not null.
             xraySteps.append(
                 CGGeneric(string.Template("""${obj} = js::CheckedUnwrap(${obj});
 if (!${obj}) {
   return false;
-}""").substitute({ 'obj' : 'obj' if isConstructor else 'unwrappedObj.ref()' })))
+}""").substitute({ 'obj' : unwrappedVar })))
             if isConstructor:
                 # If we're called via an xray, we need to enter the underlying
                 # object's compartment and then wrap up all of our arguments into
                 # that compartment as needed.  This is all happening after we've
                 # already done the conversions from JS values to WebIDL (C++)
                 # values, so we only need to worry about cases where there are 'any'
                 # or 'object' types, or other things that we represent as actual
                 # JSAPI types, present.  Effectively, we're emulating a
@@ -4910,20 +4921,24 @@ if (!${obj}) {
         except MethodNotCreatorError, err:
             assert not isCreator
             raise TypeError("%s being returned from non-creator method or property %s.%s" %
                             (err.typename,
                              self.descriptor.interface.identifier.name,
                              self.idlNode.identifier.name))
 
     def getErrorReport(self):
-        return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
+        jsImplemented = ""
+        if self.descriptor.interface.isJSImplemented():
+            jsImplemented = ", true"
+        return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s"%s);'
                          % (toStringBool(not self.descriptor.workers),
                             self.descriptor.interface.identifier.name,
-                            self.idlNode.identifier.name))
+                            self.idlNode.identifier.name,
+                            jsImplemented))
 
     def define(self):
         return (self.cgRoot.define() + "\n" + self.wrap_return_value())
 
 class CGSwitch(CGList):
     """
     A class to generate code for a switch statement.
 
@@ -9504,49 +9519,78 @@ class CGExampleRoot(CGThing):
 
     def define(self):
         return self.root.define()
 
 
 def jsImplName(name):
     return name + "JSImpl"
 
-class CGJSImplMethod(CGNativeMember):
+class CGJSImplMember(CGNativeMember):
+    """
+    Base class for generating code for the members of the implementation class
+    for a JS-implemented WebIDL interface.
+    """
+    def __init__(self, descriptorProvider, member, name, signature,
+                 extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True,
+                 visibility="public", jsObjectsArePtr=False,
+                 variadicIsSequence=False):
+        CGNativeMember.__init__(self, descriptorProvider, member, name,
+                                signature, extendedAttrs, breakAfter=breakAfter,
+                                passJSBitsAsNeeded=passJSBitsAsNeeded,
+                                visibility=visibility,
+                                jsObjectsArePtr=jsObjectsArePtr,
+                                variadicIsSequence=variadicIsSequence)
+        self.body = self.getImpl()
+
+    def getArgs(self, returnType, argList):
+        args = CGNativeMember.getArgs(self, returnType, argList)
+        args.insert(0, Argument("JSCompartment*", "aCompartment"))
+        return args
+
+class CGJSImplMethod(CGJSImplMember):
+    """
+    Class for generating code for the methods for a JS-implemented WebIDL
+    interface.
+    """
     def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
-        CGNativeMember.__init__(self, descriptor, method,
+        self.signature = signature
+        self.descriptor = descriptor
+        self.isConstructor = isConstructor
+        CGJSImplMember.__init__(self, descriptor, method,
                                 CGSpecializedMethod.makeNativeName(descriptor,
                                                                    method),
                                 signature,
                                 descriptor.getExtendedAttributes(method),
                                 breakAfter=breakAfter,
                                 variadicIsSequence=True,
                                 passJSBitsAsNeeded=False)
-        self.signature = signature
-        self.descriptor = descriptor
-        if isConstructor:
-            self.body = self.getConstructorImpl()
-        else:
-            self.body = self.getImpl()
+
+    def getArgs(self, returnType, argList):
+        if self.isConstructor:
+            # Skip the JSCompartment bits for constructors; it's handled
+            # manually in getImpl.
+            return CGNativeMember.getArgs(self, returnType, argList)
+        return CGJSImplMember.getArgs(self, returnType, argList)
 
     def getImpl(self):
-        callbackArgs = [arg.name for arg in self.getArgs(self.signature[0], self.signature[1])]
-        return 'return mImpl->%s(%s);' % (self.name, ", ".join(callbackArgs))
-
-    def getConstructorImpl(self):
+        args = self.getArgs(self.signature[0], self.signature[1])
+        if not self.isConstructor:
+            return 'return mImpl->%s(%s);' % (self.name, ", ".join(arg.name for arg in args))
+
         assert self.descriptor.interface.isJSImplemented()
         if self.name != 'Constructor':
             raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
         if len(self.signature[1]) != 0:
-            args = self.getArgs(self.signature[0], self.signature[1])
             # The first two arguments to the constructor implementation are not
             # arguments to the WebIDL constructor, so don't pass them to __Init()
             assert args[0].argType == 'const GlobalObject&'
             assert args[1].argType == 'JSContext*'
-            args = args[2:]
-            constructorArgs = [arg.name for arg in args]
+            constructorArgs = [arg.name for arg in args[2:]]
+            constructorArgs.insert(0, "js::GetObjectCompartment(scopeObj)")
             initCall = """
 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
 JS::Rooted<JS::Value> wrappedVal(cx);
 if (!WrapNewBindingObject(cx, scopeObj, impl, &wrappedVal)) {
   MOZ_ASSERT(JS_IsExceptionPending(cx));
   aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -9583,42 +9627,48 @@ def genConstructorBody(descriptor, initC
 
 # We're always fallible
 def callbackGetterName(attr):
     return "Get" + MakeNativeName(attr.identifier.name)
 
 def callbackSetterName(attr):
     return "Set" + MakeNativeName(attr.identifier.name)
 
-class CGJSImplGetter(CGNativeMember):
+class CGJSImplGetter(CGJSImplMember):
+    """
+    Class for generating code for the getters of attributes for a JS-implemented
+    WebIDL interface.
+    """
     def __init__(self, descriptor, attr):
-        CGNativeMember.__init__(self, descriptor, attr,
+        CGJSImplMember.__init__(self, descriptor, attr,
                                 CGSpecializedGetter.makeNativeName(descriptor,
                                                                    attr),
                                 (attr.type, []),
                                 descriptor.getExtendedAttributes(attr,
                                                                  getter=True),
                                 passJSBitsAsNeeded=False)
-        self.body = self.getImpl()
 
     def getImpl(self):
         callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
         return 'return mImpl->%s(%s);' % (callbackGetterName(self.member), ", ".join(callbackArgs))
 
-class CGJSImplSetter(CGNativeMember):
+class CGJSImplSetter(CGJSImplMember):
+    """
+    Class for generating code for the setters of attributes for a JS-implemented
+    WebIDL interface.
+    """
     def __init__(self, descriptor, attr):
-        CGNativeMember.__init__(self, descriptor, attr,
+        CGJSImplMember.__init__(self, descriptor, attr,
                                 CGSpecializedSetter.makeNativeName(descriptor,
                                                                    attr),
                                 (BuiltinTypes[IDLBuiltinType.Types.void],
                                  [FakeArgument(attr.type, attr)]),
                                 descriptor.getExtendedAttributes(attr,
                                                                  setter=True),
                                 passJSBitsAsNeeded=False)
-        self.body = self.getImpl()
 
     def getImpl(self):
         callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
                                                          [FakeArgument(self.member.type, self.member)])]
         return 'mImpl->%s(%s);' % (callbackSetterName(self.member), ", ".join(callbackArgs))
 
 class CGJSImplClass(CGBindingImplClass):
     def __init__(self, descriptor):
@@ -9924,21 +9974,23 @@ class FakeMember():
         # Claim to be a [Creator] so we can avoid the "mark this
         # resultNotAddRefed" comments CGNativeMember codegen would
         # otherwise stick in.
         if name == "Creator":
             return True
         return None
 
 class CallbackMember(CGNativeMember):
-    def __init__(self, sig, name, descriptorProvider, needThisHandling):
+    def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
         """
         needThisHandling is True if we need to be able to accept a specified
         thisObj, False otherwise.
         """
+        assert not rethrowContentException or not needThisHandling
+
         self.retvalType = sig[0]
         self.originalSig = sig
         args = sig[1]
         self.argCount = len(args)
         if self.argCount > 0:
             # Check for variadic arguments
             lastArg = args[self.argCount-1]
             if lastArg.variadic:
@@ -9946,16 +9998,17 @@ class CallbackMember(CGNativeMember):
                     "(%d - 1) + %s.Length()" % (self.argCount,
                                                 lastArg.identifier.name))
             else:
                 self.argCountStr = "%d" % self.argCount
         self.needThisHandling = needThisHandling
         # If needThisHandling, we generate ourselves as private and the caller
         # will handle generating public versions that handle the "this" stuff.
         visibility = "private" if needThisHandling else "public"
+        self.rethrowContentException = rethrowContentException
         # We don't care, for callback codegen, whether our original member was
         # a method or attribute or whatnot.  Just always pass FakeMember()
         # here.
         CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
                                 name, (self.retvalType, args),
                                 extendedAttrs={},
                                 passJSBitsAsNeeded=False,
                                 visibility=visibility,
@@ -10104,34 +10157,47 @@ class CallbackMember(CGNativeMember):
             default = " " + default
         return default
 
     def getArgs(self, returnType, argList):
         args = CGNativeMember.getArgs(self, returnType, argList)
         if not self.needThisHandling:
             # Since we don't need this handling, we're the actual method that
             # will be called, so we need an aRethrowExceptions argument.
-            return args + [Argument("ExceptionHandling", "aExceptionHandling",
-                                    "eReportExceptions")]
+            if self.rethrowContentException:
+                args.insert(0, Argument("JSCompartment*", "aCompartment"))
+            else:
+                args.append(Argument("ExceptionHandling", "aExceptionHandling",
+                                     "eReportExceptions"))
+            return args
         # We want to allow the caller to pass in a "this" object, as
         # well as a JSContext.
         return [Argument("JSContext*", "cx"),
                 Argument("JS::Handle<JSObject*>", "aThisObj")] + args
 
     def getCallSetup(self):
         if self.needThisHandling:
             # It's been done for us already
             return ""
+        callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
+        if self.rethrowContentException:
+            # getArgs doesn't add the aExceptionHandling argument but does add
+            # aCompartment for us.
+            callSetup += ", eRethrowContentExceptions, aCompartment"
+        else:
+            callSetup += ", aExceptionHandling"
+        callSetup += ");"
         return string.Template(
-            "CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n"
+            "${callSetup}\n"
             "JSContext* cx = s.GetContext();\n"
             "if (!cx) {\n"
             "  aRv.Throw(NS_ERROR_UNEXPECTED);\n"
             "  return${errorReturn};\n"
             "}\n").substitute({
+                "callSetup": callSetup,
                 "errorReturn" : self.getDefaultRetval(),
                 })
 
     def getArgcDecl(self):
         return CGGeneric("unsigned argc = %s;" % self.argCountStr);
 
     @staticmethod
     def ensureASCIIName(idlObject):
@@ -10144,19 +10210,19 @@ class CallbackMember(CGNativeMember):
         if re.match('"', idlObject.identifier.name):
             raise SyntaxError("Callback %s name '%s' contains "
                               "double-quote character.  We can't handle "
                               "that.  %s" %
                               (type, idlObject.identifier.name,
                                idlObject.location))
 
 class CallbackMethod(CallbackMember):
-    def __init__(self, sig, name, descriptorProvider, needThisHandling):
+    def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
         CallbackMember.__init__(self, sig, name, descriptorProvider,
-                                needThisHandling)
+                                needThisHandling, rethrowContentException)
     def getRvalDecl(self):
         return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
 
     def getCall(self):
         replacements = {
             "errorReturn" : self.getDefaultRetval(),
             "thisObj": self.getThisObj(),
             "getCallable": self.getCallableDecl()
@@ -10184,20 +10250,20 @@ class CallCallback(CallbackMethod):
 
     def getCallableDecl(self):
         return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
 
 class CallbackOperationBase(CallbackMethod):
     """
     Common class for implementing various callback operations.
     """
-    def __init__(self, signature, jsName, nativeName, descriptor, singleOperation):
+    def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
         self.singleOperation = singleOperation
         self.methodName = jsName
-        CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation)
+        CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
 
     def getThisObj(self):
         if not self.singleOperation:
             return "mCallback"
         # This relies on getCallableDecl declaring a boolean
         # isCallable in the case when we're a single-operation
         # interface.
         return "isCallable ? aThisObj.get() : mCallback"
@@ -10227,27 +10293,29 @@ class CallbackOperation(CallbackOperatio
     """
     Codegen actual WebIDL operations on callback interfaces.
     """
     def __init__(self, method, signature, descriptor):
         self.ensureASCIIName(method)
         jsName = method.identifier.name
         CallbackOperationBase.__init__(self, signature,
                                        jsName, MakeNativeName(jsName),
-                                       descriptor, descriptor.interface.isSingleOperationInterface())
+                                       descriptor, descriptor.interface.isSingleOperationInterface(),
+                                       rethrowContentException=descriptor.interface.isJSImplemented())
 
 class CallbackGetter(CallbackMember):
     def __init__(self, attr, descriptor):
         self.ensureASCIIName(attr)
         self.attrName = attr.identifier.name
         CallbackMember.__init__(self,
                                 (attr.type, []),
                                 callbackGetterName(attr),
                                 descriptor,
-                                needThisHandling=False)
+                                needThisHandling=False,
+                                rethrowContentException=descriptor.interface.isJSImplemented())
 
     def getRvalDecl(self):
         return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
 
     def getCall(self):
         replacements = {
             "errorReturn" : self.getDefaultRetval(),
             "attrName": self.attrName
@@ -10262,17 +10330,18 @@ class CallbackSetter(CallbackMember):
     def __init__(self, attr, descriptor):
         self.ensureASCIIName(attr)
         self.attrName = attr.identifier.name
         CallbackMember.__init__(self,
                                 (BuiltinTypes[IDLBuiltinType.Types.void],
                                  [FakeArgument(attr.type, attr)]),
                                 callbackSetterName(attr),
                                 descriptor,
-                                needThisHandling=False)
+                                needThisHandling=False,
+                                rethrowContentException=descriptor.interface.isJSImplemented())
 
     def getRvalDecl(self):
         # We don't need an rval
         return ""
 
     def getCall(self):
         replacements = {
             "errorReturn" : self.getDefaultRetval(),
@@ -10291,17 +10360,17 @@ class CallbackSetter(CallbackMember):
 
 class CGJSImplInitOperation(CallbackOperationBase):
     """
     Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
     """
     def __init__(self, sig, descriptor):
         assert sig in descriptor.interface.ctor().signatures()
         CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
-                                       "__init", "__Init", descriptor, False)
+                                       "__init", "__Init", descriptor, False, True)
 
 class GlobalGenRoots():
     """
     Roots for global codegen.
 
     To generate code, call the method associated with the target, and then
     call the appropriate define/declare method.
     """
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -65,21 +65,24 @@ public:
   void ThrowTypeError(const dom::ErrNum errorNumber, ...);
   void ReportTypeError(JSContext* cx);
   void ClearMessage();
   bool IsTypeError() const { return ErrorCode() == NS_ERROR_TYPE_ERR; }
 
   // Facilities for throwing a preexisting JS exception value via this
   // ErrorResult.  The contract is that any code which might end up calling
   // ThrowJSException() must call MightThrowJSException() even if no exception
-  // is being thrown.  Code that would call ReportJSException or
+  // is being thrown.  Code that would call ReportJSException* or
   // StealJSException as needed must first call WouldReportJSException even if
   // this ErrorResult has not failed.
   void ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn);
   void ReportJSException(JSContext* cx);
+  // Used to implement throwing exceptions from the JS implementation of
+  // bindings to callers of the binding.
+  void ReportJSExceptionFromJSImplementation(JSContext* aCx);
   bool IsJSException() const { return ErrorCode() == NS_ERROR_DOM_JS_EXCEPTION; }
 
   void ThrowNotEnoughArgsError() { mResult = NS_ERROR_XPC_NOT_ENOUGH_ARGS; }
   void ReportNotEnoughArgsError(JSContext* cx,
                                 const char* ifaceName,
                                 const char* memberName);
   bool IsNotEnoughArgsError() const { return ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS; }
 
--- a/dom/indexedDB/test/head.js
+++ b/dom/indexedDB/test/head.js
@@ -55,17 +55,17 @@ function triggerSecondaryCommand(popup, 
 {
   info("triggering secondary command, " + index);
   let notifications = popup.childNodes;
   ok(notifications.length > 0, "at least one notification displayed");
   let notification = notifications[0];
 
   // Cancel the arrow panel slide-in transition (bug 767133) such that
   // it won't interfere with us interacting with the dropdown.
-  document.getAnonymousNodes(popup)[0].style.transition = "none";
+  SpecialPowers.wrap(document).getAnonymousNodes(popup)[0].style.transition = "none";
 
   notification.button.focus();
 
   popup.addEventListener("popupshown", function () {
     popup.removeEventListener("popupshown", arguments.callee, false);
 
     // Press down until the desired command is selected
     for (let i = 0; i <= index; i++)
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -354,40 +354,38 @@ RTCPeerConnection.prototype = {
    *                   { url:"turn:turn.example.org",
    *                     username:"jib", credential:"mypass"} ] }
    *
    * WebIDL normalizes structure for us, so we test well-formed stun/turn urls,
    * but not validity of servers themselves, before passing along to C++.
    * ErrorMsg is passed in to detail which array-entry failed, if any.
    */
   _mustValidateRTCConfiguration: function(rtcConfig, errorMsg) {
+    var errorCtor = this._win.DOMError;
     function nicerNewURI(uriStr, errorMsg) {
       let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
       try {
         return ios.newURI(uriStr, null, null);
       } catch (e if (e.result == Cr.NS_ERROR_MALFORMED_URI)) {
-        throw new Components.Exception(errorMsg + " - malformed URI: " + uriStr,
-                                       Cr.NS_ERROR_MALFORMED_URI);
+        throw new errorCtor("", errorMsg + " - malformed URI: " + uriStr);
       }
     }
     function mustValidateServer(server) {
       let url = nicerNewURI(server.url, errorMsg);
       if (url.scheme in { turn:1, turns:1 }) {
         if (!server.username) {
-          throw new Components.Exception(errorMsg + " - missing username: " +
-                                         server.url, Cr.NS_ERROR_MALFORMED_URI);
+          throw new errorCtor("", errorMsg + " - missing username: " + server.url);
         }
         if (!server.credential) {
-          throw new Components.Exception(errorMsg + " - missing credential: " +
-                                         server.url, Cr.NS_ERROR_MALFORMED_URI);
+          throw new errorCtor("", errorMsg + " - missing credential: " +
+                              server.url);
         }
       }
       else if (!(url.scheme in { stun:1, stuns:1 })) {
-        throw new Components.Exception(errorMsg + " - improper scheme: " + url.scheme,
-                                       Cr.NS_ERROR_MALFORMED_URI);
+        throw new errorCtor("", errorMsg + " - improper scheme: " + url.scheme);
       }
     }
     if (rtcConfig.iceServers) {
       let len = rtcConfig.iceServers.length;
       for (let i=0; i < len; i++) {
         mustValidateServer (rtcConfig.iceServers[i], errorMsg);
       }
     }
--- a/dom/payment/Payment.js
+++ b/dom/payment/Payment.js
@@ -11,24 +11,22 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 
 const PAYMENTCONTENTHELPER_CID =
   Components.ID("{a920adc0-c36e-4fd0-8de0-aac1ac6ebbd0}");
 
 const PAYMENT_IPC_MSG_NAMES = ["Payment:Success",
                                "Payment:Failed"];
 
+const PREF_DEBUG = "dom.payment.debug";
+
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
-function debug (s) {
-  //dump("-*- PaymentContentHelper: " + s + "\n");
-};
-
 function PaymentContentHelper() {
 };
 
 PaymentContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINavigatorPayment,
                                          Ci.nsIDOMGlobalPropertyInitializer,
@@ -47,18 +45,20 @@ PaymentContentHelper.prototype = {
   pay: function pay(aJwts) {
     let request = this.createRequest();
     let requestId = this.getRequestId(request);
 
     let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIWebNavigation)
                    .QueryInterface(Ci.nsIDocShell);
     if (!docShell.isActive) {
-      debug("The caller application is a background app. No request " +
-            "will be sent");
+      if (this._debug) {
+        this.LOG("The caller application is a background app. No request " +
+                  "will be sent");
+      }
       let runnable = {
         run: function run() {
           Services.DOMRequest.fireError(request, "BACKGROUND_APP");
         }
       }
       Services.tm.currentThread.dispatch(runnable,
                                          Ci.nsIThread.DISPATCH_NORMAL);
       return request;
@@ -75,34 +75,52 @@ PaymentContentHelper.prototype = {
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer
 
   init: function(aWindow) {
     this._window = aWindow;
     this.initDOMRequestHelper(aWindow, PAYMENT_IPC_MSG_NAMES);
+
+    try {
+      this._debug =
+        Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
+        && Services.prefs.getBoolPref(PREF_DEBUG);
+    } catch(e) {
+      this._debug = false;
+    }
+
     return this.pay.bind(this);
   },
 
   // nsIFrameMessageListener
 
   receiveMessage: function receiveMessage(aMessage) {
     let name = aMessage.name;
     let msg = aMessage.json;
-    debug("Received message '" + name + "': " + JSON.stringify(msg));
+    if (this._debug) {
+      this.LOG("Received message '" + name + "': " + JSON.stringify(msg));
+    }
     let requestId = msg.requestId;
     let request = this.takeRequest(requestId);
     if (!request) {
       return;
     }
     switch (name) {
       case "Payment:Success":
         Services.DOMRequest.fireSuccess(request, msg.result);
         break;
       case "Payment:Failed":
         Services.DOMRequest.fireError(request, msg.errorMsg);
         break;
     }
+  },
+
+  LOG: function LOG(s) {
+    if (!this._debug) {
+      return;
+    }
+    dump("-*- PaymentContentHelper: " + s + "\n");
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentContentHelper]);
--- a/dom/payment/Payment.jsm
+++ b/dom/payment/Payment.jsm
@@ -12,29 +12,26 @@ Cu.import("resource://gre/modules/Servic
 this.EXPORTED_SYMBOLS = [];
 
 const PAYMENT_IPC_MSG_NAMES = ["Payment:Pay",
                                "Payment:Success",
                                "Payment:Failed"];
 
 const PREF_PAYMENTPROVIDERS_BRANCH = "dom.payment.provider.";
 const PREF_PAYMENT_BRANCH = "dom.payment.";
+const PREF_DEBUG = "dom.payment.debug";
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "prefService",
                                    "@mozilla.org/preferences-service;1",
                                    "nsIPrefService");
 
-function debug (s) {
-  //dump("-*- PaymentManager: " + s + "\n");
-};
-
 let PaymentManager =  {
   init: function init() {
     // Payment providers data are stored as a preference.
     this.registeredProviders = null;
 
     this.messageManagers = {};
 
     // The dom.payment.skipHTTPSCheck pref is supposed to be used only during
@@ -48,25 +45,35 @@ let PaymentManager =  {
       }
     } catch(e) {}
 
     for each (let msgname in PAYMENT_IPC_MSG_NAMES) {
       ppmm.addMessageListener(msgname, this);
     }
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
+
+    try {
+      this._debug =
+        Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
+        && Services.prefs.getBoolPref(PREF_DEBUG);
+    } catch(e) {
+      this._debug = false;
+    }
   },
 
   /**
    * Process a message from the content process.
    */
   receiveMessage: function receiveMessage(aMessage) {
     let name = aMessage.name;
     let msg = aMessage.json;
-    debug("Received '" + name + "' message from content process");
+    if (this._debug) {
+      this.LOG("Received '" + name + "' message from content process");
+    }
 
     switch (name) {
       case "Payment:Pay": {
         // First of all, we register the payment providers.
         if (!this.registeredProviders) {
           this.registeredProviders = {};
           this.registerPaymentProviders();
         }
@@ -107,43 +114,50 @@ let PaymentManager =  {
 
         // After getting the list of valid payment requests, we ask the user
         // for confirmation before sending any request to any payment provider.
         // If there is more than one choice, we also let the user select the one
         // that he prefers.
         let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                    .createInstance(Ci.nsIPaymentUIGlue);
         if (!glue) {
-          debug("Could not create nsIPaymentUIGlue instance");
+          if (this._debug) {
+            this.LOG("Could not create nsIPaymentUIGlue instance");
+          }
           this.paymentFailed(requestId,
                              "INTERNAL_ERROR_CREATE_PAYMENT_GLUE_FAILED");
           return;
         }
 
         let confirmPaymentSuccessCb = function successCb(aRequestId,
                                                          aResult) {
           // Get the appropriate payment provider data based on user's choice.
           let selectedProvider = this.registeredProviders[aResult];
           if (!selectedProvider || !selectedProvider.uri) {
-            debug("Could not retrieve a valid provider based on user's " +
-                  "selection");
+            if (this._debug) {
+              this.LOG("Could not retrieve a valid provider based on user's " +
+                        "selection");
+            }
             this.paymentFailed(aRequestId,
                                "INTERNAL_ERROR_NO_VALID_SELECTED_PROVIDER");
             return;
           }
 
           let jwt;
           for (let i in paymentRequests) {
             if (paymentRequests[i].type == aResult) {
               jwt = paymentRequests[i].jwt;
               break;
             }
           }
           if (!jwt) {
-            debug("The selected request has no JWT information associated");
+            if (this._debug) {
+              this.LOG("The selected request has no JWT information " +
+                        "associated");
+            }
             this.paymentFailed(aRequestId,
                                "INTERNAL_ERROR_NO_JWT_ASSOCIATED_TO_REQUEST");
             return;
           }
 
           this.showPaymentFlow(aRequestId, selectedProvider, jwt);
         };
 
@@ -201,20 +215,24 @@ let PaymentManager =  {
           continue;
         }
         this.registeredProviders[type] = {
           name: branch.getCharPref("name"),
           uri: branch.getCharPref("uri"),
           description: branch.getCharPref("description"),
           requestMethod: branch.getCharPref("requestMethod")
         };
-        debug("Registered Payment Providers: " +
-              JSON.stringify(this.registeredProviders[type]));
+        if (this._debug) {
+          this.LOG("Registered Payment Providers: " +
+                    JSON.stringify(this.registeredProviders[type]));
+        }
       } catch (ex) {
-        debug("An error ocurred registering a payment provider. " + ex);
+        if (this._debug) {
+          this.LOG("An error ocurred registering a payment provider. " + ex);
+        }
       }
     }
   },
 
   /**
    * Helper for sending a Payment:Failed message to the parent process.
    */
   paymentFailed: function paymentFailed(aRequestId, aErrorMsg) {
@@ -237,33 +255,37 @@ let PaymentManager =  {
 
     // First thing, we check that the jwt type is an allowed type and has a
     // payment provider flow information associated.
 
     // A jwt string consists in three parts separated by period ('.'): header,
     // payload and signature.
     let segments = aJwt.split('.');
     if (segments.length !== 3) {
-      debug("Error getting payment provider's uri. " +
-            "Not enough or too many segments");
+      if (this._debug) {
+        this.LOG("Error getting payment provider's uri. " +
+                  "Not enough or too many segments");
+      }
       this.paymentFailed(aRequestId,
                          "PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT");
       return true;
     }
 
     let payloadObject;
     try {
       // We only care about the payload segment, which contains the jwt type
       // that should match with any of the stored payment provider's data and
       // the payment request information to be shown to the user.
       // Before decoding the JWT string we need to normalize it to be compliant
       // with RFC 4648.
       segments[1] = segments[1].replace("-", "+", "g").replace("_", "/", "g");
       let payload = atob(segments[1]);
-      debug("Payload " + payload);
+      if (this._debug) {
+        this.LOG("Payload " + payload);
+      }
       if (!payload.length) {
         this.paymentFailed(aRequestId, "PAY_REQUEST_ERROR_EMPTY_PAYLOAD");
         return true;
       }
       payloadObject = JSON.parse(payload);
       if (!payloadObject) {
         this.paymentFailed(aRequestId,
                            "PAY_REQUEST_ERROR_ERROR_PARSING_JWT_PAYLOAD");
@@ -290,31 +312,35 @@ let PaymentManager =  {
     // Once we got the jwt 'typ' value we look for a match within the payment
     // providers stored preferences. If the jwt 'typ' is not recognized as one
     // of the allowed values for registered payment providers, we skip the jwt
     // validation but we don't fire any error. This way developers might have
     // a default set of well formed JWTs that might be used in different B2G
     // devices with a different set of allowed payment providers.
     let provider = this.registeredProviders[payloadObject.typ];
     if (!provider) {
-      debug("Not registered payment provider for jwt type: " +
-            payloadObject.typ);
+      if (this._debug) {
+        this.LOG("Not registered payment provider for jwt type: " +
+                  payloadObject.typ);
+      }
       return false;
     }
 
     if (!provider.uri || !provider.name) {
       this.paymentFailed(aRequestId,
                          "INTERNAL_ERROR_WRONG_REGISTERED_PAY_PROVIDER");
       return true;
     }
 
     // We only allow https for payment providers uris.
     if (this.checkHttps && !/^https/.exec(provider.uri.toLowerCase())) {
       // We should never get this far.
-      debug("Payment provider uris must be https: " + provider.uri);
+      if (this._debug) {
+        this.LOG("Payment provider uris must be https: " + provider.uri);
+      }
       this.paymentFailed(aRequestId,
                          "INTERNAL_ERROR_NON_HTTPS_PROVIDER_URI");
       return true;
     }
 
     let pldRequest = payloadObject.request;
     let request = Cc["@mozilla.org/payment/request-info;1"]
                   .createInstance(Ci.nsIDOMPaymentRequestInfo);
@@ -336,17 +362,19 @@ let PaymentManager =  {
                           .createInstance(Ci.nsIPaymentFlowInfo);
     paymentFlowInfo.uri = aPaymentProvider.uri;
     paymentFlowInfo.requestMethod = aPaymentProvider.requestMethod;
     paymentFlowInfo.jwt = aJwt;
 
     let glue = Cc["@mozilla.org/payment/ui-glue;1"]
                .createInstance(Ci.nsIPaymentUIGlue);
     if (!glue) {
-      debug("Could not create nsIPaymentUIGlue instance");
+      if (this._debug) {
+        this.LOG("Could not create nsIPaymentUIGlue instance");
+      }
       this.paymentFailed(aRequestId,
                          "INTERNAL_ERROR_CREATE_PAYMENT_GLUE_FAILED");
       return false;
     }
     glue.showPaymentFlow(aRequestId,
                          paymentFlowInfo,
                          this.paymentFailed.bind(this));
   },
@@ -359,11 +387,18 @@ let PaymentManager =  {
         ppmm.removeMessageListener(msgname, this);
       }
       this.registeredProviders = null;
       this.messageManagers = null;
 
       Services.obs.removeObserver(this, "xpcom-shutdown");
     }
   },
+
+  LOG: function LOG(s) {
+    if (!this._debug) {
+      return;
+    }
+    dump("-*- PaymentManager: " + s + "\n");
+  }
 };
 
 PaymentManager.init();
--- a/dom/tests/mochitest/chrome/window_focus.xul
+++ b/dom/tests/mochitest/chrome/window_focus.xul
@@ -1122,19 +1122,26 @@ function otherWindowFocused(otherWindow)
   is(gEvents, "", "when lowered no events fired");
 
   var other = otherWindow.document.getElementById("other");
   other.focus();
   is(fm.focusedElement, other, "focus method in second window");
 
   otherWindow.close();
 
+  getById("n2").focus();
+
   // next, check modal dialogs
-  getById("n2").focus();
-  var nextWindow = window.openDialog("focus_window2.xul", "_blank", "chrome,modal", modalWindowOpened);
+  // XXXndeakin Bug 621399 - modal dialog test sometimes fails on Windows 8 so disable it.
+  if (navigator.userAgent.indexOf("Windows NT 6.2") >= 0) {
+    window.open("focus_frameset.html", "_blank", "width=400,height=400,toolbar=no");
+  }
+  else {
+    window.openDialog("focus_window2.xul", "_blank", "chrome,modal", modalWindowOpened);
+  }
 }
 
 function modalWindowOpened(modalWindow)
 {
   var elem = modalWindow.document.getElementById("other");
   if (gPartialTabbing)
     elem.focus();
   else
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -272,21 +272,24 @@ partial interface Document {
   //(Not implemented)Element?  find(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
   //(Not implemented)NodeList  findAll(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
 };
 
 //  Mozilla extensions of various sorts
 partial interface Document {
   // nsIDOMDocumentXBL.  Wish we could make these [ChromeOnly], but
   // that would likely break bindings running with the page principal.
+  [Func="IsChromeOrXBL"]
   NodeList? getAnonymousNodes(Element elt);
+  [Func="IsChromeOrXBL"]
   Element? getAnonymousElementByAttribute(Element elt, DOMString attrName,
                                           DOMString attrValue);
+  [Func="IsChromeOrXBL"]
   Element? getBindingParent(Node node);
-  [Throws]
+  [Throws, Func="IsChromeOrXBL"]
   void loadBindingDocument(DOMString documentURL);
 
   // nsIDOMDocumentTouch
   // XXXbz I can't find the sane spec for this stuff, so just cribbing
   // from our xpidl for now.
   [Creator, Func="nsGenericHTMLElement::TouchEventsEnabled"]
   Touch createTouch(optional Window? view = null,
                     optional EventTarget? target = null,
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -21,17 +21,17 @@
 #include "mozilla/Telemetry.h"          // for Accumulate
 #include "mozilla/TelemetryHistogramEnums.h"
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite
-#include "mozilla/layers/LayerTransaction.h"  // for TransformFunction, etc
+#include "mozilla/layers/LayersMessages.h"  // for TransformFunction, etc
 #include "nsAString.h"
 #include "nsCSSValue.h"                 // for nsCSSValue::Array, etc
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsStyleStruct.h"              // for nsTimingFunction, etc
 
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 
--- a/gfx/layers/client/ClientCanvasLayer.h
+++ b/gfx/layers/client/ClientCanvasLayer.h
@@ -7,17 +7,17 @@
 #define GFX_CLIENTCANVASLAYER_H
 
 #include "mozilla/layers/CanvasClient.h"  // for CanvasClient, etc
 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
 #include "CopyableCanvasLayer.h"        // for CopyableCanvasLayer
 #include "Layers.h"                     // for CanvasLayer, etc
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for RefPtr
-#include "mozilla/layers/LayerTransaction.h"  // for CanvasLayerAttributes, etc
+#include "mozilla/layers/LayersMessages.h"  // for CanvasLayerAttributes, etc
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 
 using namespace mozilla::gfx;
 
--- a/gfx/layers/client/ClientColorLayer.cpp
+++ b/gfx/layers/client/ClientColorLayer.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
 #include "Layers.h"                     // for ColorLayer, etc
-#include "mozilla/layers/LayerTransaction.h"  // for ColorLayerAttributes, etc
+#include "mozilla/layers/LayersMessages.h"  // for ColorLayerAttributes, etc
 #include "mozilla/mozalloc.h"           // for operator new
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 
--- a/gfx/layers/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -6,17 +6,17 @@
 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
 #include "ImageContainer.h"             // for AutoLockImage, etc
 #include "ImageLayers.h"                // for ImageLayer
 #include "gfxASurface.h"                // for gfxASurface
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ImageClient.h"  // for ImageClient, etc
-#include "mozilla/layers/LayerTransaction.h"  // for ImageLayerAttributes, etc
+#include "mozilla/layers/LayersMessages.h"  // for ImageLayerAttributes, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr, getter_AddRefs, etc
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -12,17 +12,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Hal.h"
 #include "mozilla/dom/ScreenOrientation.h"  // for ScreenOrientation
 #include "mozilla/dom/TabChild.h"       // for TabChild
 #include "mozilla/hal_sandbox/PHal.h"   // for ScreenConfiguration
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
 #include "mozilla/layers/ContentClient.h"  // for ContentClientRemote
 #include "mozilla/layers/ISurfaceAllocator.h"
-#include "mozilla/layers/LayerTransaction.h"  // for EditReply, etc
+#include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/PLayerChild.h"  // for PLayerChild
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "nsAString.h"
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsTArray.h"                   // for AutoInfallibleTArray
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #ifdef MOZ_WIDGET_ANDROID
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -8,17 +8,17 @@
 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
 #include "gfx3DMatrix.h"                // for gfx3DMatrix
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxPoint.h"                   // for gfxSize
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Rect.h"           // for Rect, RectTyped
-#include "mozilla/layers/LayerTransaction.h"
+#include "mozilla/layers/LayersMessages.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsRect.h"                     // for nsIntRect
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 
 namespace mozilla {
 namespace layers {
 
 
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -15,17 +15,17 @@
 #include "ipc/ShadowLayers.h"           // for ShadowLayerForwarder
 #include "mozilla/Util.h"               // for ArrayLength
 #include "mozilla/gfx/2D.h"             // for DrawTarget, Factory
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"
 #include "mozilla/layers/LayerManagerComposite.h"
-#include "mozilla/layers/LayerTransaction.h"  // for ThebesBufferData
+#include "mozilla/layers/LayersMessages.h"  // for ThebesBufferData
 #include "mozilla/layers/LayersTypes.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION, NS_WARNING, etc
 #include "nsISupportsImpl.h"            // for gfxContext::Release, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "prenv.h"                      // for PR_GetEnv
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -9,17 +9,17 @@
 #include "Units.h"                      // for LayerPoint, etc
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerManagerComposite
 #include "gfx3DMatrix.h"                // for gfx3DMatrix
 #include "mozilla/Attributes.h"         // for MOZ_DELETE, MOZ_FINAL, etc
 #include "mozilla/RefPtr.h"             // for RefCounted
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/dom/ScreenOrientation.h"  // for ScreenOrientation
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
-#include "mozilla/layers/LayerTransaction.h"  // for TargetConfig
+#include "mozilla/layers/LayersMessages.h"  // for TargetConfig
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"            // for LayerManager::AddRef, etc
 #include "nsTraceRefcnt.h"              // for MOZ_COUNT_CTOR, etc
 
 namespace mozilla {
 namespace layers {
 
 class AsyncPanZoomController;
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -6,17 +6,17 @@
 #include "mozilla/layers/ContentHost.h"
 #include "LayersLogging.h"              // for AppendToString
 #include "gfx2DGlue.h"                  // for ContentForFormat
 #include "gfxPoint.h"                   // for gfxIntSize
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
-#include "mozilla/layers/LayerTransaction.h"  // for ThebesBufferData
+#include "mozilla/layers/LayersMessages.h"  // for ThebesBufferData
 #include "nsAString.h"
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
 class gfxImageSurface;
 
 namespace mozilla {
 namespace gfx {
--- a/gfx/layers/ipc/CompositableTransactionParent.h
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -6,17 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
 #define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
 
 #include <vector>                       // for vector
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
-#include "mozilla/layers/LayerTransaction.h"  // for EditReply, etc
+#include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 
 namespace mozilla {
 namespace layers {
 
 typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
 
 // Since PCompositble has two potential manager protocols, we can't just call
 // the Manager() method usually generated when there's one manager protocol,
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -22,17 +22,17 @@
 #include "base/platform_thread.h"       // for PlatformThreadId
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Attributes.h"         // for MOZ_OVERRIDE
 #include "mozilla/Monitor.h"            // for Monitor
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/GeckoContentController.h"
-#include "mozilla/layers/LayerTransaction.h"  // for TargetConfig
+#include "mozilla/layers/LayersMessages.h"  // for TargetConfig
 #include "mozilla/layers/PCompositorParent.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"
 #include "nsSize.h"                     // for nsIntSize
 
 class CancelableTask;
 class MessageLoop;
 class gfxContext;
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -20,17 +20,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Monitor.h"            // for Monitor, MonitorAutoLock
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitor, etc
 #include "mozilla/ipc/AsyncChannel.h"   // for AsyncChannel, etc
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
-#include "mozilla/layers/LayerTransaction.h"  // for CompositableOperation
+#include "mozilla/layers/LayersMessages.h"  // for CompositableOperation
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"            // for ImageContainer::AddRef, etc
 #include "nsTArray.h"                   // for nsAutoTArray, nsTArray, etc
 #include "nsTArrayForwardDeclare.h"     // for AutoInfallibleTArray
 #include "nsThreadUtils.h"              // for NS_IsMainThread
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -13,17 +13,17 @@
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxPoint.h"                   // for gfxIntSize
 #include "mozilla/ipc/AsyncChannel.h"   // for AsyncChannel, etc
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/CompositorParent.h"  // for CompositorParent
 #include "mozilla/layers/LayerManagerComposite.h"
-#include "mozilla/layers/LayerTransaction.h"  // for EditReply
+#include "mozilla/layers/LayersMessages.h"  // for EditReply
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PImageBridgeParent.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for ImageBridgeParent::Release, etc
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -18,17 +18,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BasePoint3D.h"    // for BasePoint3D
 #include "mozilla/layers/CanvasLayerComposite.h"
 #include "mozilla/layers/ColorLayerComposite.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/ContainerLayerComposite.h"
 #include "mozilla/layers/ImageLayerComposite.h"
 #include "mozilla/layers/LayerManagerComposite.h"
-#include "mozilla/layers/LayerTransaction.h"  // for EditReply, etc
+#include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
 #include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PLayerParent.h"  // for PLayerParent
 #include "mozilla/layers/ThebesLayerComposite.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
rename from gfx/layers/ipc/LayerTransaction.ipdlh
rename to gfx/layers/ipc/LayersMessages.ipdlh
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
-include LayerTransaction;
+include LayersMessages;
 include protocol PGrallocBuffer;
 include protocol PCompositable;
 
 include "mozilla/layers/CompositorTypes.h";
 include "mozilla/GfxMessageUtils.h";
 
 using ImageHandle;
 using mozilla::layers::TextureInfo;
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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 LayersSurfaces;
-include LayerTransaction;
+include LayersMessages;
 include protocol PCompositable;
 include protocol PCompositor;
 include protocol PGrallocBuffer;
 include protocol PLayer;
 include protocol PRenderFrame;
 
 include "mozilla/WidgetUtils.h";
 include "mozilla/dom/ScreenOrientation.h";
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -15,17 +15,17 @@
 #include "RenderTrace.h"                // for RenderTraceScope
 #include "ShadowLayerChild.h"           // for ShadowLayerChild
 #include "gfxImageSurface.h"            // for gfxImageSurface
 #include "gfxPlatform.h"                // for gfxImageFormat, gfxPlatform
 #include "gfxSharedImageSurface.h"      // for gfxSharedImageSurface
 #include "ipc/IPCMessageUtils.h"        // for gfxContentType, null_t
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient, etc
-#include "mozilla/layers/LayerTransaction.h"  // for Edit, etc
+#include "mozilla/layers/LayersMessages.h"  // for Edit, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "ShadowLayerUtils.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr, getter_AddRefs, etc
 #include "nsDebug.h"                    // for NS_ABORT_IF_FALSE, etc
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -258,17 +258,17 @@ CPP_SOURCES += [
     'ThebesLayerComposite.cpp',
     'ThebesLayerOGL.cpp',
     'TiledContentClient.cpp',
     'TiledContentHost.cpp',
     'YCbCrImageDataSerializer.cpp',
 ]
 
 IPDL_SOURCES = [
-    'ipc/LayerTransaction.ipdlh',
+    'ipc/LayersMessages.ipdlh',
     'ipc/LayersSurfaces.ipdlh',
     'ipc/PCompositable.ipdl',
     'ipc/PCompositor.ipdl',
     'ipc/PGrallocBuffer.ipdl',
     'ipc/PImageBridge.ipdl',
     'ipc/PLayer.ipdl',
     'ipc/PLayerTransaction.ipdl',
 ]
--- a/image/test/mochitest/test_animSVGImage.html
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -49,64 +49,56 @@ function takeReferenceSnapshot() {
 
   // Re-hide reference div, and take another snapshot to be sure it's gone
   referenceDiv.style.display = "none";
   let blankSnapshot2 = snapshotWindow(window, false);
   ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
      "reference div should disappear when it becomes display:none");
 }
 
-function myOnStopFrame(aRequest) {
+function myOnStopFrame() {
   gOnStopFrameCounter++;
   ok(true, "myOnStopFrame called");
   let currentSnapshot = snapshotWindow(window, false);
   if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
     // SUCCESS!
     ok(true, "Animated image looks correct, " +
              "at call #" + gOnStopFrameCounter + " to onStopFrame");
     cleanUpAndFinish();
   }
-  setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
+  else
+    setTimeout(myOnStopFrame, 1);
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "Animated image still doesn't look correct, " +
             "after call #" + gOnStopFrameCounter + " to onStopFrame");
   cleanUpAndFinish();
 }
 
 function cleanUpAndFinish() {
   // On the off chance that failTest and myOnStopFrame are triggered
   // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
   if (gIsTestFinished) {
     return;
   }
-  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
-  imgLoadingContent.removeObserver(gMyDecoderObserver);
   SimpleTest.finish();
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
-  // Create, customize & attach decoder observer
-  observer = new ImageDecoderObserverStub();
-  observer.frameComplete = myOnStopFrame;
-  gMyDecoderObserver =
-    Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
-      .createScriptedObserver(observer);
-  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
-  imgLoadingContent.addObserver(gMyDecoderObserver);
-
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
   clearImageCache();
 
+  setTimeout(myOnStopFrame, 1);
+
   // kick off image-loading! myOnStopFrame handles the rest.
   gImg.setAttribute("src", "lime-anim-100x100.svg");
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
 
--- a/js/src/TraceLogging.cpp
+++ b/js/src/TraceLogging.cpp
@@ -60,17 +60,17 @@ rdtsc(void)
     result = upper;
     result = result<<32;
     result = result|lower;
 
     return(result);
 }
 #endif
 
-const char* const TraceLogging::type_name[] = {
+const char* const TraceLogging::typeName[] = {
     "1,s",  // start script
     "0,s",  // stop script
     "1,c",  // start ion compilation
     "0,c",  // stop ion compilation
     "1,r",  // start regexp JIT execution
     "0,r",  // stop regexp JIT execution
     "1,G",  // start major GC
     "0,G",  // stop major GC
@@ -81,53 +81,55 @@ const char* const TraceLogging::type_nam
     "1,pl", // start lazy parsing
     "0,pl", // stop lazy parsing
     "1,pf", // start Function parsing
     "0,pf", // stop Function parsing
     "e,i",  // engine interpreter
     "e,b",  // engine baseline
     "e,o"   // engine ionmonkey
 };
-TraceLogging* TraceLogging::_defaultLogger = NULL;
+TraceLogging* TraceLogging::loggers[] = {NULL, NULL};
+bool TraceLogging::atexitSet = false;
+uint64_t TraceLogging::startupTime = 0;
 
-TraceLogging::TraceLogging()
-  : startupTime(rdtsc()),
-    loggingTime(0),
+TraceLogging::TraceLogging(Logger id)
+  : loggingTime(0),
     nextTextId(1),
     entries(NULL),
     curEntry(0),
     numEntries(1000000),
     fileno(0),
-    out(NULL)
+    out(NULL),
+    id(id)
 {
     textMap.init();
 }
 
 TraceLogging::~TraceLogging()
 {
-    if (out != NULL) {
+    if (out) {
         fclose(out);
         out = NULL;
     }
 
-    if (entries != NULL) {
+    if (entries) {
         flush();
         free(entries);
         entries = NULL;
     }
 }
 
 void
 TraceLogging::grow()
 {
     Entry* nentries = (Entry*) realloc(entries, numEntries*2*sizeof(Entry));
 
     // Allocating a bigger array failed.
     // Keep using the current storage, but remove all entries by flushing them.
-    if (nentries == NULL) {
+    if (!nentries) {
         flush();
         return;
     }
 
     entries = nentries;
     numEntries *= 2;
 }
 
@@ -190,86 +192,98 @@ TraceLogging::log(const char* log)
 {
     this->log(INFO, log, 0);
 }
 
 void
 TraceLogging::flush()
 {
     // Open the logging file, when not opened yet.
-    if (out == NULL)
-        out = fopen(TRACE_LOG_DIR "tracelogging.log", "w");
+    if (!out) {
+        switch(id) {
+          case DEFAULT:
+            out = fopen(TRACE_LOG_DIR "tracelogging.log", "w");
+            break;
+          case ION_BACKGROUND_COMPILER:
+            out = fopen(TRACE_LOG_DIR "tracelogging-compile.log", "w");
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("Bad trigger");
+            return;
+        }
+    }
 
     // Print all log entries into the file
     for (unsigned int i = 0; i < curEntry; i++) {
         Entry entry = entries[i];
         int written;
         if (entry.type() == INFO) {
             written = fprintf(out, "I,%s\n", entry.text());
         } else {
             if (entry.textId() > 0) {
                 if (entry.text()) {
                     written = fprintf(out, "%llu,%s,%s,%d\n",
                                       (unsigned long long)entry.tick(),
-                                      type_name[entry.type()],
+                                      typeName[entry.type()],
                                       entry.text(),
                                       entry.lineno());
                 } else {
                     written = fprintf(out, "%llu,%s,%d,%d\n",
                                       (unsigned long long)entry.tick(),
-                                      type_name[entry.type()],
+                                      typeName[entry.type()],
                                       entry.textId(),
                                       entry.lineno());
                 }
             } else {
                 written = fprintf(out, "%llu,%s\n",
                                   (unsigned long long)entry.tick(),
-                                  type_name[entry.type()]);
+                                  typeName[entry.type()]);
             }
         }
 
         // A logging file can only be 2GB of length (fwrite limit).
-        // When we exceed this limit, the writing will fail.
-        // In that case try creating a extra file to write the log entries.
         if (written < 0) {
+            fprintf(stderr, "Writing tracelog to disk failed,");
+            fprintf(stderr, "probably because the file would've exceeded the maximum size of 2GB");
             fclose(out);
-            if (fileno >= 9999)
-                exit(-1);
-
-            char filename[21 + sizeof(TRACE_LOG_DIR)];
-            sprintf (filename, TRACE_LOG_DIR "tracelogging-%d.log", ++fileno);
-            out = fopen(filename, "w");
-            i--; // Try to print message again
-            continue;
+            exit(-1);
         }
 
         if (entries[i].text() != NULL) {
             free(entries[i].text());
             entries[i].text_ = NULL;
         }
     }
     curEntry = 0;
 }
 
 TraceLogging*
-TraceLogging::defaultLogger()
+TraceLogging::getLogger(Logger id)
 {
-    if (_defaultLogger == NULL) {
-        _defaultLogger = new TraceLogging();
-        atexit (releaseDefaultLogger);
+    if (!loggers[id]) {
+        loggers[id] = new TraceLogging(id);
+        if (!atexitSet) {
+            startupTime = rdtsc();
+            atexit (releaseLoggers);
+            atexitSet = true;
+        }
     }
-    return _defaultLogger;
+
+    return loggers[id];
 }
 
 void
-TraceLogging::releaseDefaultLogger()
+TraceLogging::releaseLoggers()
 {
-    if (_defaultLogger != NULL) {
-        delete _defaultLogger;
-        _defaultLogger = NULL;
+    for (size_t i = 0; i < LAST_LOGGER; i++) {
+        if (!loggers[i])
+            continue;
+
+        delete loggers[i];
+        loggers[i] = NULL;
     }
 }
 
 /* Helper functions for asm calls */
 void
 js::TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script)
 {
     logger->log(type, script);
--- a/js/src/TraceLogging.h
+++ b/js/src/TraceLogging.h
@@ -41,16 +41,22 @@ class TraceLogging
         PARSER_COMPILE_LAZY_STOP,
         PARSER_COMPILE_FUNCTION_START,
         PARSER_COMPILE_FUNCTION_STOP,
         INFO_ENGINE_INTERPRETER,
         INFO_ENGINE_BASELINE,
         INFO_ENGINE_IONMONKEY,
         INFO
     };
+    enum Logger {
+        DEFAULT,
+        ION_BACKGROUND_COMPILER,
+
+        LAST_LOGGER
+    };
 
   private:
     struct Entry {
         uint64_t tick_;
         char* text_;
         uint32_t textId_;
         uint32_t lineno_;
         uint8_t type_;
@@ -69,40 +75,45 @@ class TraceLogging
         Type type() const { return (Type) type_; }
     };
 
     typedef HashMap<const char *,
                         uint32_t,
                         PointerHasher<const char *, 3>,
                         SystemAllocPolicy> TextHashMap;
 
-    uint64_t startupTime;
     uint64_t loggingTime;
     TextHashMap textMap;
     uint32_t nextTextId;
     Entry *entries;
     unsigned int curEntry;
     unsigned int numEntries;
     int fileno;
     FILE *out;
+    Logger id;
 
-    static const char * const type_name[];
-    static TraceLogging* _defaultLogger;
+    static bool atexitSet;
+    static const char * const typeName[];
+    static TraceLogging* loggers[];
+    static uint64_t startupTime;
   public:
-    TraceLogging();
+    TraceLogging(Logger id);
     ~TraceLogging();
 
     void log(Type type, const char* text = NULL, unsigned int number = 0);
     void log(Type type, const JS::CompileOptions &options);
     void log(Type type, JSScript* script);
     void log(const char* log);
     void flush();
 
-    static TraceLogging* defaultLogger();
-    static void releaseDefaultLogger();
+    static TraceLogging* getLogger(Logger id);
+    static TraceLogging* defaultLogger() {
+        return getLogger(DEFAULT);
+    }
+    static void releaseLoggers();
 
   private:
     void grow();
 };
 
 /* Helpers functions for asm calls */
 void TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script);
 void TraceLog(TraceLogging* logger, const char* log);
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -1491,37 +1491,47 @@ public:
     void movw_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         spew("movw       %s, %s0x%x(%s)",
              nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
+    void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("movw       %s, %d(%s,%s,%d)",
+             nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
+    }
+
+#if !WTF_CPU_X86_64
+    void movw_rm(RegisterID src, const void* addr)
+    {
+        spew("movw       %s, %p",
+             nameIReg(2, src), addr);
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
+    }
+#endif
+
     void movl_rm(RegisterID src, int offset, RegisterID base)
     {
         spew("movl       %s, %s0x%x(%s)",
              nameIReg(4,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
     }
 
     void movl_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
-    void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("movw       %s, %d(%s,%s,%d)",
-             nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
-        m_formatter.prefix(PRE_OPERAND_SIZE);
-        m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
-    }
-
     void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movl       %s, %d(%s,%s,%d)",
              nameIReg(4, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
     }
 
     void movl_mEAX(const void* addr)
@@ -1559,16 +1569,28 @@ public:
 
     void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movl       %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movl       %p, %s",
+             addr, nameIReg(4, dst));
+        if (dst == X86Registers::eax)
+            movl_mEAX(addr);
+        else
+            m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
+    }
+#endif
+
     void movl_i32r(int imm, RegisterID dst)
     {
         spew("movl       $0x%x, %s",
              imm, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_EAXIv, dst);
         m_formatter.immediate32(imm);
     }
 
@@ -1775,26 +1797,16 @@ public:
         spew("movl       %s, %p",
              nameIReg(4, src), addr);
         if (src == X86Registers::eax)
             movl_EAXm(addr);
         else
             m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
     }
 
-    void movl_mr(const void* addr, RegisterID dst)
-    {
-        spew("movl       %p, %s",
-             addr, nameIReg(4, dst));
-        if (dst == X86Registers::eax)
-            movl_mEAX(addr);
-        else
-            m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
-    }
-
     void movl_i32m(int imm, const void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr);
         m_formatter.immediate32(imm);
     }
 #endif
 
@@ -1814,16 +1826,25 @@ public:
 
     void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movb       %s, %d(%s,%s,%d)",
              nameIReg(1, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movb_rm(RegisterID src, const void* addr)
+    {
+        spew("movb       %s, %p",
+             nameIReg(1, src), addr);
+        m_formatter.oneByteOp(OP_MOV_EbGv, src, addr);
+    }
+#endif
+
     void movzbl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movzbl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset);
     }
 
     void movzbl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1835,16 +1856,25 @@ public:
 
     void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movzbl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movzbl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movzbl     %p, %s",
+             addr, nameIReg(dst));
+        m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, addr);
+    }
+#endif
+
     void movsbl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movsbl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset);
     }
 
     void movsbl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1856,16 +1886,25 @@ public:
 
     void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movsbl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movsbl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movsbl     %p, %s",
+             addr, nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, addr);
+    }
+#endif
+
     void movzwl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movzwl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset);
     }
 
     void movzwl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1877,16 +1916,25 @@ public:
 
     void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movzwl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movzwl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movzwl     %p, %s",
+             addr, nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, addr);
+    }
+#endif
+
     void movswl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movswl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset);
     }
 
     void movswl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1898,16 +1946,25 @@ public:
 
     void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movswl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movswl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movswl     %p, %s",
+             addr, nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, addr);
+    }
+#endif
+
     void movzbl_rr(RegisterID src, RegisterID dst)
     {
         spew("movzbl     %s, %s",
              nameIReg(1,src), nameIReg(4,dst));
         m_formatter.twoByteOp8_movx(OP2_MOVZX_GvEb, dst, src);
     }
 
     void leal_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
@@ -2329,32 +2386,52 @@ public:
     void movss_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
     {
         spew("movss      %s, %s0x%x(%s)",
              nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movss_rm(XMMRegisterID src, const void* addr)
+    {
+        spew("movss      %s, %p",
+             nameFPReg(src), addr);
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, addr);
+    }
+#endif
+
     void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void movss_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movss_mr(const void* addr, XMMRegisterID dst)
+    {
+        spew("movss      %p, %s",
+             addr, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, addr);
+    }
+#endif
+
     void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movsd      %s, %d(%s,%s,%d)",
              nameFPReg(src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -222,37 +222,37 @@ StructTypeRepresentation::init(JSContext
     uint32_t totalSize = 0;
 
     for (size_t i = 0; i < ids.length(); i++) {
         TypeRepresentation *fieldTypeRepr = fromOwnerObject(typeReprOwners[i]);
 
         uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment());
         if (alignedSize < totalSize) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                                 JSMSG_BINARYDATA_TOO_BIG);
+                                 JSMSG_TYPEDOBJECT_TOO_BIG);
             return false;
         }
 
         new(fields() + i) StructField(i, ids[i], fieldTypeRepr, alignedSize);
         alignment_ = js::Max(alignment_, fieldTypeRepr->alignment());
 
         uint32_t incrementedSize = alignedSize + fieldTypeRepr->size();
         if (incrementedSize < alignedSize) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                                 JSMSG_BINARYDATA_TOO_BIG);
+                                 JSMSG_TYPEDOBJECT_TOO_BIG);
             return false;
         }
 
         totalSize = incrementedSize;
     }
 
     uint32_t alignedSize = alignTo(totalSize, alignment_);
     if (alignedSize < totalSize) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_TOO_BIG);
+                             JSMSG_TYPEDOBJECT_TOO_BIG);
         return false;
     }
 
     size_ = alignedSize;
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -322,17 +322,17 @@ ArrayTypeRepresentation::Create(JSContex
     JSCompartment *comp = cx->compartment();
 
     // Overly conservative, since we are using `size_t` to represent
     // size, but `SafeMul` operators on `int32_t` types. Still, it
     // should be good enough for now.
     int32_t temp;
     if (!SafeMul(element->size(), length, &temp)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_TOO_BIG);
+                             JSMSG_TYPEDOBJECT_TOO_BIG);
         return NULL;
     }
 
     ArrayTypeRepresentation sample(element, length);
     TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
     if (p)
         return (*p)->ownerObject();
 
rename from js/src/builtin/BinaryData.cpp
rename to js/src/builtin/TypedObject.cpp
--- a/js/src/builtin/BinaryData.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "builtin/BinaryData.h"
+#include "builtin/TypedObject.h"
 
 #include "jscompartment.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsutil.h"
 
 #include "builtin/TypeRepresentation.h"
 #include "gc/Marking.h"
@@ -284,17 +284,17 @@ TypeEquivalent(JSContext *cx, unsigned i
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "Type.equivalent", "1", "s");
         return false;
     }
 
     RootedObject otherObj(cx, ToObjectIfObject(args[0]));
     if (!otherObj || !IsBinaryType(otherObj)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BINARYDATA_NOT_TYPE_OBJECT);
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPEDOBJECT_NOT_TYPE_OBJECT);
         return false;
     }
 
     TypeRepresentation *thisRepr = typeRepresentation(thisObj);
     TypeRepresentation *otherRepr = typeRepresentation(otherObj);
     args.rval().setBoolean(thisRepr == otherRepr);
     return true;
 }
@@ -779,17 +779,17 @@ ArraySubarray(JSContext *cx, unsigned in
         JS_ReportErrorNumber(cx, js_GetErrorMessage,
                              NULL, JSMSG_MORE_ARGS_NEEDED,
                              "subarray()", "0", "s");
         return false;
     }
 
     if (!args[0].isInt32()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, "1");
+                             JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "1");
         return false;
     }
 
     RootedObject thisObj(cx, &args.thisv().toObject());
     if (!IsBinaryArray(cx, thisObj)) {
         ReportCannotConvertTo(cx, thisObj, "binary array");
         return false;
     }
@@ -799,17 +799,17 @@ ArraySubarray(JSContext *cx, unsigned in
     size_t length = typeRepr->length();
 
     int32_t begin = args[0].toInt32();
     int32_t end = length;
 
     if (args.length() >= 2) {
         if (!args[1].isInt32()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                    JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, "2");
+                    JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "2");
             return false;
         }
 
         end = args[1].toInt32();
     }
 
     if (begin < 0)
         begin = length + begin;
@@ -987,39 +987,39 @@ ArrayType::construct(JSContext *cx, unsi
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (argc != 2 ||
         !args[0].isObject() ||
         !args[1].isNumber() ||
         args[1].toNumber() < 0)
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS);
+                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
     RootedObject arrayTypeGlobal(cx, &args.callee());
     RootedObject elementType(cx, &args[0].toObject());
 
     if (!IsBinaryType(elementType)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS);
+                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
     if (!args[1].isInt32()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS);
+                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
     int32_t length = args[1].toInt32();
     if (length < 0) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                             JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS);
+                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
     RootedObject obj(cx, create(cx, arrayTypeGlobal, elementType, length));
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
@@ -1264,17 +1264,17 @@ StructType::construct(JSContext *cx, uns
         RootedObject obj(cx, create(cx, structTypeGlobal, fields));
         if (!obj)
             return false;
         args.rval().setObject(*obj);
         return true;
     }
 
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
-                         JSMSG_BINARYDATA_STRUCTTYPE_BAD_ARGS);
+                         JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
     return false;
 }
 
 bool
 StructType::toSource(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -1593,17 +1593,17 @@ DefineNumericClass(JSContext *cx,
     if (!JS_DefineFunction(cx, numFun, "toSource",
                            NumericTypeToString<type>, 0, 0))
         return false;
 
     return true;
 }
 
 JSObject *
-js_InitBinaryDataClasses(JSContext *cx, HandleObject obj)
+js_InitTypedObjectClasses(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->is<GlobalObject>());
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
 
     if (!InitType(cx, obj))
         return NULL;
 
 #define BINARYDATA_NUMERIC_DEFINE(constant_, type_, name_)                    \
@@ -1743,17 +1743,17 @@ BinaryBlock::createNull(JSContext *cx, H
     obj->initFixedSlot(SLOT_BLOCKREFOWNER, owner);
 
     // Tag the type object for this instance with the type
     // representation, if that has not been done already.
     if (cx->typeInferenceEnabled()) {
         RootedTypeObject typeObj(cx, obj->getType(cx));
         if (typeObj) {
             TypeRepresentation *typeRepr = typeRepresentation(type);
-            if (!typeObj->addBinaryDataAddendum(cx, typeRepr))
+            if (!typeObj->addTypedObjectAddendum(cx, typeRepr))
                 return NULL;
         }
     }
 
     return obj;
 }
 
 /*static*/ JSObject *
@@ -2137,17 +2137,17 @@ BinaryBlock::obj_setElement(JSContext *c
       case ScalarTypeRepresentation::Struct:
         break;
 
       case ScalarTypeRepresentation::Array: {
         ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray();
 
         if (index >= arrayTypeRepr->length()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                                 NULL, JSMSG_BINARYDATA_BINARYARRAY_BAD_INDEX);
+                                 NULL, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
             return false;
         }
 
         size_t offset = arrayTypeRepr->element()->size() * index;
         uint8_t *mem = BlockMem(obj) + offset;
         return ConvertAndCopyTo(cx, arrayTypeRepr->element(), vp, mem);
       }
     }
rename from js/src/builtin/BinaryData.h
rename to js/src/builtin/TypedObject.h
--- a/js/src/builtin/BinaryData.h
+++ b/js/src/builtin/TypedObject.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef builtin_BinaryData_h
-#define builtin_BinaryData_h
+#ifndef builtin_TypedObject_h
+#define builtin_TypedObject_h
 
 #include "jsobj.h"
 
 #include "builtin/TypeRepresentation.h"
 
 namespace js {
 
 // Slots common to all type descriptors:
@@ -223,9 +223,9 @@ class BinaryBlock
 
     // user-accessible constructor (`new TypeDescriptor(...)`)
     static bool construct(JSContext *cx, unsigned int argc, jsval *vp);
 };
 
 
 } // namespace js
 
-#endif /* builtin_BinaryData_h */
+#endif /* builtin_TypedObject_h */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1130,18 +1130,18 @@ ScanTypeObject(GCMarker *gcmarker, types
 
     if (type->addendum) {
         switch (type->addendum->kind) {
           case types::TypeObjectAddendum::NewScript:
             PushMarkStack(gcmarker, type->newScript()->fun);
             PushMarkStack(gcmarker, type->newScript()->shape.get());
             break;
 
-          case types::TypeObjectAddendum::BinaryData:
-            PushMarkStack(gcmarker, type->binaryData()->typeRepr->ownerObject());
+          case types::TypeObjectAddendum::TypedObject:
+            PushMarkStack(gcmarker, type->typedObject()->typeRepr->ownerObject());
             break;
         }
     }
 
     if (type->interpretedFunction)
         PushMarkStack(gcmarker, type->interpretedFunction);
 }
 
@@ -1163,18 +1163,18 @@ gc::MarkChildren(JSTracer *trc, types::T
 
     if (type->addendum) {
         switch (type->addendum->kind) {
           case types::TypeObjectAddendum::NewScript:
             MarkObject(trc, &type->newScript()->fun, "type_new_function");
             MarkShape(trc, &type->newScript()->shape, "type_new_shape");
             break;
 
-          case types::TypeObjectAddendum::BinaryData:
-            type->binaryData()->typeRepr->mark(trc);
+          case types::TypeObjectAddendum::TypedObject:
+            type->typedObject()->typeRepr->mark(trc);
             break;
         }
     }
 
     if (type->interpretedFunction)
         MarkObject(trc, &type->interpretedFunction, "type_function");
 }
 
rename from js/src/jit-test/tests/binary-data/fuzz1.js
rename to js/src/jit-test/tests/TypedObject/fuzz1.js
rename from js/src/jit-test/tests/binary-data/fuzz10.js
rename to js/src/jit-test/tests/TypedObject/fuzz10.js
rename from js/src/jit-test/tests/binary-data/fuzz11.js
rename to js/src/jit-test/tests/TypedObject/fuzz11.js
rename from js/src/jit-test/tests/binary-data/fuzz2.js
rename to js/src/jit-test/tests/TypedObject/fuzz2.js
rename from js/src/jit-test/tests/binary-data/fuzz3.js
rename to js/src/jit-test/tests/TypedObject/fuzz3.js
rename from js/src/jit-test/tests/binary-data/fuzz4.js
rename to js/src/jit-test/tests/TypedObject/fuzz4.js
rename from js/src/jit-test/tests/binary-data/fuzz5.js
rename to js/src/jit-test/tests/TypedObject/fuzz5.js
rename from js/src/jit-test/tests/binary-data/fuzz6.js
rename to js/src/jit-test/tests/TypedObject/fuzz6.js
rename from js/src/jit-test/tests/binary-data/fuzz7.js
rename to js/src/jit-test/tests/TypedObject/fuzz7.js
rename from js/src/jit-test/tests/binary-data/fuzz8.js
rename to js/src/jit-test/tests/TypedObject/fuzz8.js
rename from js/src/jit-test/tests/binary-data/fuzz9.js
rename to js/src/jit-test/tests/TypedObject/fuzz9.js
rename from js/src/jit-test/tests/binary-data/jit-prefix.js
rename to js/src/jit-test/tests/TypedObject/jit-prefix.js
rename from js/src/jit-test/tests/binary-data/jit-read-float64.js
rename to js/src/jit-test/tests/TypedObject/jit-read-float64.js
rename from js/src/jit-test/tests/binary-data/jit-read-int.js
rename to js/src/jit-test/tests/TypedObject/jit-read-int.js
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz1.js
@@ -0,0 +1,8 @@
+if (!this.hasOwnProperty("Type"))
+  exit(0);
+
+function eval() {
+    yield(undefined)
+}
+new(StructType)
+(eval())
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz10.js
@@ -0,0 +1,8 @@
+// |jit-test| error:Error
+
+if (!this.hasOwnProperty("Type"))
+  throw new Error("type too large");
+
+var AA = new ArrayType(new ArrayType(uint8, (2147483647)), 5);
+var aa = new AA();
+var aa0 = aa[0];
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz11.js
@@ -0,0 +1,13 @@
+// |jit-test| error:Error
+
+if (!this.hasOwnProperty("Type"))
+  throw new Error("type too large");
+
+var A = new ArrayType(uint8, (2147483647));
+var S = new StructType({a: A,
+                        b: A,
+                        c: A,
+                        d: A,
+                        e: A});
+var aa = new S();
+var aa0 = aa.a;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz2.js
@@ -0,0 +1,4 @@
+if (!this.hasOwnProperty("Type"))
+  exit(0);
+
+new StructType([])
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz3.js
@@ -0,0 +1,7 @@
+// |jit-test| error:RangeError;
+
+if (!this.hasOwnProperty("Type"))
+  throw new RangeError();
+
+this.__proto__ =  Proxy.create({});
+new StructType;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz4.js
@@ -0,0 +1,10 @@
+// |jit-test| error:Error;
+
+if (!this.hasOwnProperty("Type"))
+  throw new Error();
+
+var A = new ArrayType(uint8, 10);
+var a = new A();
+a.forEach(function(val, i) {
+  assertEq(arguments[5], a);
+});
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz5.js
@@ -0,0 +1,6 @@
+if (!this.hasOwnProperty("Type"))
+  exit(0);
+
+var Color = new StructType({r: uint8, g: uint8, b: uint8});
+var white2 = new Color({r: 255, toString: null, b: 253});
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz6.js
@@ -0,0 +1,6 @@
+// |jit-test| error:TypeError
+
+if (!this.hasOwnProperty("Type"))
+  throw new TypeError();
+
+new StructType(RegExp);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz7.js
@@ -0,0 +1,4 @@
+if (!this.hasOwnProperty("Type"))
+  exit(0);
+
+new StructType(RegExp());
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz8.js
@@ -0,0 +1,6 @@
+// |jit-test| error:Error
+
+if (!this.hasOwnProperty("Type"))
+  throw new Error();
+
+new ArrayType(uint8, .0000000009);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/fuzz9.js
@@ -0,0 +1,10 @@
+// |jit-test| error: TypeError
+
+if (!this.hasOwnProperty("Type"))
+  throw new TypeError();
+
+var Vec3 = new ArrayType(float32, 3);
+var Sprite = new ArrayType(Vec3, 3);
+var mario = new Sprite();
+mario[/\u00ee[]/] = new Vec3([1, 0, 0]);
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/jit-prefix.js
@@ -0,0 +1,25 @@
+var PointType2 = new StructType({x: float64,
+                                 y: float64});
+
+var PointType3 = new StructType({x: float64,
+                                 y: float64,
+                                 z: float64});
+
+function xPlusY(p) {
+  return p.x + p.y;
+}
+
+function foo() {
+  var N = 30000;
+  var points = [];
+  for (var i = 0; i < N; i++) {
+    var s;
+    if ((i % 2) == 0 || true)
+      s = xPlusY(new PointType2({x: i, y: i+1}));
+    else
+      s = xPlusY(new PointType3({x: i, y: i+1, z: i+2}));
+    assertEq(s, i + i + 1);
+  }
+}
+
+foo();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/jit-read-float64.js
@@ -0,0 +1,14 @@
+var PointType = new StructType({x: float64,
+                                y: float64,
+                                z: float64});
+
+function foo() {
+  for (var i = 0; i < 30000; i += 3) {
+    var pt = new PointType({x: i, y: i+1, z: i+2});
+    var sum = pt.x + pt.y + pt.z;
+    print(pt.x, pt.y, pt.z);
+    assertEq(sum, 3*i + 3);
+  }
+}
+
+foo();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObjects/jit-read-int.js
@@ -0,0 +1,13 @@
+var PointType = new StructType({x: uint32,
+                                y: uint32,
+                                z: uint32});
+
+function foo() {
+  for (var i = 0; i < 30000; i += 3) {
+    var pt = new PointType({x: i, y: i+1, z: i+2});
+    var sum = pt.x + pt.y + pt.z;
+    assertEq(sum, 3*i + 3);
+  }
+}
+
+foo();
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -160,10 +160,288 @@ var code = asmCompile('glob', 'imp', 'b'
 asmLink(code, this, null, new ArrayBuffer(4096));
 
 asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[12] = i } return f'), this, null, BUF_64KB)(11);
 assertEq(new Int32Array(BUF_64KB)[12], 11);
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[12]|0 } return f'), this, null, BUF_64KB)(), 11);
 new Float64Array(BUF_64KB)[0] = 3.5;
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +-f64[0] } return f'), this, null, BUF_64KB)(), -3.5);
 
+// Test constant loads.
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[1] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[0], 0);
+assertEq(new Uint8Array(buf)[1], 255);
+assertEq(new Uint8Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[4095] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[4094], 0);
+assertEq(new Uint8Array(buf)[4095], 255);
+assertEq(new Uint8Array(buf)[4096], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[4096] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[4095], 0);
+assertEq(new Uint8Array(buf)[4096], 255);
+assertEq(new Uint8Array(buf)[4097], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[8192] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[258048] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[258047], 0);
+assertEq(new Uint8Array(buf)[258048], 255);
+assertEq(new Uint8Array(buf)[258049], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[1] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[0], 0);
+assertEq(new Int8Array(buf)[1], -1);
+assertEq(new Int8Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[4095] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[4094], 0);
+assertEq(new Int8Array(buf)[4095], -1);
+assertEq(new Int8Array(buf)[4096], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[4096] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[4095], 0);
+assertEq(new Int8Array(buf)[4096], -1);
+assertEq(new Int8Array(buf)[4097], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[8192] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[258048] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[258047], 0);
+assertEq(new Int8Array(buf)[258048], -1);
+assertEq(new Int8Array(buf)[258049], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[1] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[0], 0);
+assertEq(new Uint16Array(buf)[1], 65535);
+assertEq(new Uint16Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[2047] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[2046], 0);
+assertEq(new Uint16Array(buf)[2047], 65535);
+assertEq(new Uint16Array(buf)[2048], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[2048] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[2047], 0);
+assertEq(new Uint16Array(buf)[2048], 65535);
+assertEq(new Uint16Array(buf)[2049], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[4096] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[126976] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[126975], 0);
+assertEq(new Uint16Array(buf)[126976], 65535);
+assertEq(new Uint16Array(buf)[126977], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[1] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[0], 0);
+assertEq(new Int16Array(buf)[1], -1);
+assertEq(new Int16Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[2047] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[2046], 0);
+assertEq(new Int16Array(buf)[2047], -1);
+assertEq(new Int16Array(buf)[2048], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[2048] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[2047], 0);
+assertEq(new Int16Array(buf)[2048], -1);
+assertEq(new Int16Array(buf)[2049], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[4096] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[126976] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[126975], 0);
+assertEq(new Int16Array(buf)[126976], -1);
+assertEq(new Int16Array(buf)[126977], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[0], 0);
+assertEq(new Uint32Array(buf)[1], 4294967295);
+assertEq(new Uint32Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1023] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[1022], 0);
+assertEq(new Uint32Array(buf)[1023], 4294967295);
+assertEq(new Uint32Array(buf)[1024], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1024] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[1023], 0);
+assertEq(new Uint32Array(buf)[1024], 4294967295);
+assertEq(new Uint32Array(buf)[1025], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[2048] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[61440] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[61439], 0);
+assertEq(new Uint32Array(buf)[61440], 4294967295);
+assertEq(new Uint32Array(buf)[61441], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[0], 0);
+assertEq(new Int32Array(buf)[1], -1);
+assertEq(new Int32Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1023] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1022], 0);
+assertEq(new Int32Array(buf)[1023], -1);
+assertEq(new Int32Array(buf)[124], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1024] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1023], 0);
+assertEq(new Int32Array(buf)[1024], -1);
+assertEq(new Int32Array(buf)[1025], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[2048] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[61440] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[61439], 0);
+assertEq(new Int32Array(buf)[61440], -1);
+assertEq(new Int32Array(buf)[61441], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1] = -1.0 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[0], 0);
+assertEq(new Float32Array(buf)[1], -1.0);
+assertEq(new Int32Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1023] = -1.0 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1022], 0);
+assertEq(new Float32Array(buf)[1023], -1.0);
+assertEq(new Int32Array(buf)[124], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1024] = -1.0 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1023], 0);
+assertEq(new Float32Array(buf)[1024], -1.0);
+assertEq(new Int32Array(buf)[1025], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[2048] = -1.0 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[61440] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float32Array(buf)[61439], 0.0);
+assertEq(new Float32Array(buf)[61440], -1.0);
+assertEq(new Float32Array(buf)[61441], 0.0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[1] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[0], 0.0);
+assertEq(new Float64Array(buf)[1], -1.0);
+assertEq(new Float64Array(buf)[2], 0.0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[511] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[510], 0.0);
+assertEq(new Float64Array(buf)[511], -1.0);
+assertEq(new Float64Array(buf)[512], 0.0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[512] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[511], 0.0);
+assertEq(new Float64Array(buf)[512], -1.0);
+assertEq(new Float64Array(buf)[513], 0.0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[1024] = -1.0 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[28672] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[28671], 0.0);
+assertEq(new Float64Array(buf)[28672], -1.0);
+assertEq(new Float64Array(buf)[28673], 0.0);
+
+
+var buf = new ArrayBuffer(8192);
+new Uint8Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[1]|0; } return f'), this, null, buf)(),255);
+new Int8Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[1]|0; } return f'), this, null, buf)(),-1);
+var buf = new ArrayBuffer(262144);
+new Uint8Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[126976]|0; } return f'), this, null, buf)(),255);
+new Int8Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[126976]|0; } return f'), this, null, buf)(),-1);
+
+var buf = new ArrayBuffer(8192);
+new Uint16Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[1]|0; } return f'), this, null, buf)(),65535);
+new Int16Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[1]|0; } return f'), this, null, buf)(),-1);
+var buf = new ArrayBuffer(262144);
+new Uint16Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[126976]|0; } return f'), this, null, buf)(),65535);
+new Int16Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[126976]|0; } return f'), this, null, buf)(),-1);
+
+var buf = new ArrayBuffer(8192);
+new Uint32Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[1]|0; } return f'), this, null, buf)(),-1);
+new Int32Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[1]|0; } return f'), this, null, buf)(),-1);
+var buf = new ArrayBuffer(262144);
+new Int32Array(buf)[61440] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[61440]|0; } return f'), this, null, buf)(),-1);
+
+var buf = new ArrayBuffer(8192);
+new Float32Array(buf)[1] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1]; } return f'), this, null, buf)(),-1.0);
+new Float32Array(buf)[1023] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1023]; } return f'), this, null, buf)(),-1.0);
+new Float32Array(buf)[1024] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1024]; } return f'), this, null, buf)(),-1.0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[2048]; } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+new Float32Array(buf)[61440] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[61440]; } return f'), this, null, buf)(),-1.0);
+
+var buf = new ArrayBuffer(8192);
+new Float64Array(buf)[1] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[1]; } return f'), this, null, buf)(),-1.0);
+new Float64Array(buf)[511] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[511]; } return f'), this, null, buf)(),-1.0);
+new Float64Array(buf)[512] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[512]; } return f'), this, null, buf)(),-1.0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[1024]; } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+new Float64Array(buf)[28672] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[28672]; } return f'), this, null, buf)(),-1.0);
+
+// Test bitwise-and optimizations.
+var buf = new ArrayBuffer(8192);
+new Uint8Array(buf)[8191] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[8191&8191]|0; } return f'), this, null, buf)(),255);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[(8191&8191)>>0]|0; } return f'), this, null, buf)(),255);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[8192&8191] = -1; u8[0] = 0; return u8[8192&8191]|0; } return f'), this, null, buf)(),0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[(8192&8191)>>0] = -1; u8[0] = 0; return u8[(8192&8191)>>0]|0; } return f'), this, null, buf)(),0);
+new Int8Array(buf)[8191] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[8191&8191]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[(8191&8191)>>0]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[8192&8191] = -1; i8[0] = 0; return i8[8192&8191]|0; } return f'), this, null, buf)(),0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[(8192&8191)>>0] = -1; i8[0] = 0; return i8[(8192&8191)>>0]|0; } return f'), this, null, buf)(),0);
+var buf = new ArrayBuffer(8192);
+new Uint16Array(buf)[4095] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[(8190&8191)>>1]|0; } return f'), this, null, buf)(),65535);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[(8191&8191)>>1]|0; } return f'), this, null, buf)(),65535);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[(8192&8191)>>1] = -1; u16[0] = 0; return u16[(8192&8191)>>1]|0; } return f'), this, null, buf)(),0);
+new Int16Array(buf)[4095] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[(8190&8191)>>1]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[(8191&8191)>>1]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[(8192&8191)>>1] = -1; i16[0] = 0; return i16[(8192&8191)>>1]|0; } return f'), this, null, buf)(),0);
+
+var buf = new ArrayBuffer(8192);
+new Uint32Array(buf)[2047] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(8188&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(8191&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[(8192&8191)>>2] = -1; u32[0] = 0; return u32[(8192&8191)>>2]|0; } return f'), this, null, buf)(),0);
+new Int32Array(buf)[2047] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(8188&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(8191&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[(8192&8191)>>2] = -1; i32[0] = 0; return i32[(8192&8191)>>2]|0; } return f'), this, null, buf)(),0);
+
+var buf = new ArrayBuffer(8192);
+new Float32Array(buf)[2047] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(8188&8191)>>2]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(8191&8191)>>2]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[(8192&8191)>>2] = -1.0; f32[0] = 0.0; return +f32[(8192&8191)>>2]; } return f'), this, null, buf)(),0.0);
+
+var buf = new ArrayBuffer(8192);
+new Float64Array(buf)[1023] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8184&8191)>>3]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8191&8191)>>3]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8192&8191)>>3] = -1.0; f64[0] = 0.0; return +f64[(8192&8191)>>3]; } return f'), this, null, buf)(),0.0);
+
+
 // Bug 882012
 assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var id=foreign.id;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=+id(2.0);return +doubles[0];}return g"), this, {id: function(x){return x;}}, BUF_64KB)(), 2.0);
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -1,21 +1,55 @@
 load(libdir + "asm.js");
 
 // constants
 var buf = new ArrayBuffer(4096);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x80000000]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x8fffffff]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-1]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-2]|0 } return f'), this, null, buf)(), 0);
+
+// An unshifted literal constant byte index in the range 0 to 2^31-1 inclusive should give a link failure.
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x1fffffff]|0 } return f'), this, null, buf);
+
+
+// An unshifted literal constant byte index outside the range 0 to 2^31-1 inclusive should cause an error compiling.
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x20000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x3fffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x40000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x7fffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x80000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x8fffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0xffffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x100000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x80000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x100000000]|0 } return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int16Array(b); function f() {return arr[-1]|0 } return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-2]|0 } return f');
 
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[10-12]|0 } return f');
+
+// An intish shifted literal constant index should not fail to compile or link.
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x3fffffff>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x3fffffff>>2]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0xffffffff>>2]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[-1>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-1>>2]|0 } return f'), this, null, buf)(), 0);
+// Unsigned (intish) folded constant index.
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff>>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {arr[0] = 1; return arr[(0xffffffff+1)>>>0]|0 } return f'), this, null, buf)(), 1);
+
+// A non-intish shifted literal constant index should cause an error compiling.
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x100000000>>0]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x100000000>>2]|0 } return f');
+
+// Folded non-intish constant expressions should cause an error compiling.
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff+1]|0 } return f');
+
+
+
 function testInt(ctor, shift, scale, disp) {
     var ab = new ArrayBuffer(4096);
     var arr = new ctor(ab);
     for (var i = 0; i < arr.length; i++)
         arr[i] = i;
     var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i) {i=i|0; return arr[((i<<' + scale + ')+' + disp + ')>>' + shift + ']|0 } return f'), this, null, ab);
     for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097])
         assertEq(f(i), arr[((i<<scale)+disp)>>shift]|0);
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -904,16 +904,21 @@ TypedArrayStoreType(ArrayBufferView::Vie
       case ArrayBufferView::TYPE_FLOAT32:
       case ArrayBufferView::TYPE_FLOAT64:
         return ArrayStore_Doublish;
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected array type");
 }
 
+enum NeedsBoundsCheck {
+    NO_BOUNDS_CHECK,
+    NEEDS_BOUNDS_CHECK
+};
+
 /*****************************************************************************/
 
 namespace {
 
 typedef Vector<PropertyName*,1> LabelVector;
 typedef Vector<MBasicBlock*,8> BlockVector;
 
 // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
@@ -1468,16 +1473,25 @@ class MOZ_STACK_CLASS ModuleCompiler
         if (!module_->addExit(ffiIndex, exitIndex))
             return false;
         return exits_.add(p, OldMove(exitDescriptor), *exitIndex);
     }
     bool addGlobalAccess(AsmJSGlobalAccess access) {
         return globalAccesses_.append(access);
     }
 
+    // Note a constraint on the minimum size of the heap.  The heap size is
+    // constrained when linking to be at least the maximum of all such constraints.
+    void requireHeapLengthToBeAtLeast(uint32_t len) {
+        module_->requireHeapLengthToBeAtLeast(len);
+    }
+    uint32_t minHeapLength() const {
+        return module_->minHeapLength();
+    }
+
     bool collectAccesses(MIRGenerator &gen) {
         if (!module_->addHeapAccesses(gen.heapAccesses()))
             return false;
         if (!globalAccesses_.appendAll(gen.globalAccesses()))
             return false;
         return true;
     }
 
@@ -1946,30 +1960,35 @@ class FunctionCompiler
 
     void assign(const Local &local, MDefinition *def)
     {
         if (!curBlock_)
             return;
         curBlock_->setSlot(info().localSlot(local.slot), def);
     }
 
-    MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
+    MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
     {
         if (!curBlock_)
             return NULL;
         MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(vt, ptr);
         curBlock_->add(load);
+        if (chk == NO_BOUNDS_CHECK)
+            load->setSkipBoundsCheck(true);
         return load;
     }
 
-    void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v)
+    void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
     {
         if (!curBlock_)
             return;
-        curBlock_->add(MAsmJSStoreHeap::New(vt, ptr, v));
+        MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(vt, ptr, v);
+        curBlock_->add(store);
+        if (chk == NO_BOUNDS_CHECK)
+            store->setSkipBoundsCheck(true);
     }
 
     MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
     {
         if (!curBlock_)
             return NULL;
         MIRType type = global.varType().toMIRType();
         unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varIndex());
@@ -3083,96 +3102,160 @@ CheckVarRef(FunctionCompiler &f, ParseNo
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
 }
 
 static bool
+FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
+                     NeedsBoundsCheck *needsBoundsCheck)
+{
+    ParseNode *indexNode = BinaryLeft(*indexExpr);
+    ParseNode *maskNode = BinaryRight(*indexExpr);
+
+    uint32_t mask2;
+    if (IsLiteralUint32(maskNode, &mask2)) {
+        // Flag the access to skip the bounds check if the mask ensures that an 'out of
+        // bounds' access can not occur based on the current heap length constraint.
+        if (mozilla::CountLeadingZeroes32(f.m().minHeapLength() - 1) <= mozilla::CountLeadingZeroes32(mask2))
+            *needsBoundsCheck = NO_BOUNDS_CHECK;
+        *mask &= mask2;
+        *indexExpr = indexNode;
+        return true;
+    }
+
+    return false;
+}
+
+static bool
 CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType,
-                 MDefinition **def)
+                 MDefinition **def, NeedsBoundsCheck *needsBoundsCheck)
 {
     ParseNode *viewName = ElemBase(elem);
     ParseNode *indexExpr = ElemIndex(elem);
+    *needsBoundsCheck = NEEDS_BOUNDS_CHECK;
 
     if (!viewName->isKind(PNK_NAME))
         return f.fail(viewName, "base of array access must be a typed array view name");
 
     const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name());
     if (!global || global->which() != ModuleCompiler::Global::ArrayView)
         return f.fail(viewName, "base of array access must be a typed array view name");
 
     *viewType = global->viewType();
 
     uint32_t pointer;
     if (IsLiteralUint32(indexExpr, &pointer)) {
+        if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType)))
+            return f.fail(indexExpr, "constant index out of range");
         pointer <<= TypedArrayShift(*viewType);
+        // It is adequate to note pointer+1 rather than rounding up to the next
+        // access-size boundary because access is always aligned and the constraint
+        // will be rounded up to a larger alignment later.
+        f.m().requireHeapLengthToBeAtLeast(uint32_t(pointer) + 1);
+        *needsBoundsCheck = NO_BOUNDS_CHECK;
         *def = f.constant(Int32Value(pointer));
         return true;
     }
 
+    // Mask off the low bits to account for the clearing effect of a right shift
+    // followed by the left shift implicit in the array access. E.g., H32[i>>2]
+    // loses the low two bits.
+    int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
+
     MDefinition *pointerDef;
     if (indexExpr->isKind(PNK_RSH)) {
         ParseNode *shiftNode = BinaryRight(indexExpr);
         ParseNode *pointerNode = BinaryLeft(indexExpr);
 
         uint32_t shift;
         if (!IsLiteralUint32(shiftNode, &shift))
             return f.failf(shiftNode, "shift amount must be constant");
 
         unsigned requiredShift = TypedArrayShift(*viewType);
         if (shift != requiredShift)
             return f.failf(shiftNode, "shift amount must be %u", requiredShift);
 
+        if (pointerNode->isKind(PNK_BITAND))
+            FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
+
+        // Fold a 'literal constant right shifted' now, and skip the bounds check if
+        // currently possible. This handles the optimization of many of these uses without
+        // the need for range analysis, and saves the generation of a MBitAnd op.
+        if (IsLiteralUint32(pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
+            // Cases: b[c>>n], and b[(c&m)>>n]
+            pointer &= mask;
+            if (pointer < f.m().minHeapLength())
+                *needsBoundsCheck = NO_BOUNDS_CHECK;
+            *def = f.constant(Int32Value(pointer));
+            return true;
+        }
+
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
             return false;
 
         if (!pointerType.isIntish())
             return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
     } else {
         if (TypedArrayShift(*viewType) != 0)
             return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
 
+        JS_ASSERT(mask == -1);
+        bool folded = false;
+
+        if (indexExpr->isKind(PNK_BITAND))
+            folded = FoldMaskedArrayIndex(f, &indexExpr, &mask, needsBoundsCheck);
+
         Type pointerType;
         if (!CheckExpr(f, indexExpr, &pointerDef, &pointerType))
             return false;
 
-        if (!pointerType.isInt())
-            return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
-    }
-
-    // Mask off the low bits to account for clearing effect of a right shift
-    // followed by the left shift implicit in the array access. E.g., H32[i>>2]
-    // loses the low two bits.
-    int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
-    *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
+        if (folded) {
+            if (!pointerType.isIntish())
+                return f.failf(indexExpr, "%s is not a subtype of intish", pointerType.toChars());
+        } else {
+            if (!pointerType.isInt())
+                return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
+        }
+    }
+
+    // Don't generate the mask op if there is no need for it which could happen for
+    // a shift of zero.
+    if (mask == -1)
+        *def = pointerDef;
+    else
+        *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
+
     return true;
 }
 
 static bool
 CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
 {
     ArrayBufferView::ViewType viewType;
     MDefinition *pointerDef;
-    if (!CheckArrayAccess(f, elem, &viewType, &pointerDef))
+    NeedsBoundsCheck needsBoundsCheck;
+    if (!CheckArrayAccess(f, elem, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
-    *def = f.loadHeap(viewType, pointerDef);
+    *def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
     *type = TypedArrayLoadType(viewType);
     return true;
 }
 
 static bool
 CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
 {
     ArrayBufferView::ViewType viewType;
     MDefinition *pointerDef;
-    if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef))
+    NeedsBoundsCheck needsBoundsCheck;
+    if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
     switch (TypedArrayStoreType(viewType)) {
@@ -3181,17 +3264,17 @@ CheckStoreArray(FunctionCompiler &f, Par
             return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
         break;
       case ArrayStore_Doublish:
         if (!rhsType.isDoublish())
             return f.failf(lhs, "%s is not a subtype of doublish", rhsType.toChars());
         break;
     }
 
-    f.storeHeap(viewType, pointerDef, rhsDef);
+    f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
 
     *def = rhsDef;
     *type = rhsType;
     return true;
 }
 
 static bool
 CheckAssignName(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
@@ -4708,17 +4791,24 @@ CheckFunction(ModuleCompiler &m, LifoAll
     if (func->defined())
         return m.failName(fn, "function '%s' already defined", FunctionName(fn));
 
     func->define(fn->pn_pos.begin);
     func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
 
     m.parser().release(mark);
 
+    // Copy the cumulative minimum heap size constraint to the MIR for use in analysis.  The length
+    // is also constrained to be a power of two, so firstly round up - a larger 'heap required
+    // length' can help range analysis to prove that bounds checks are not needed.
+    size_t len = mozilla::RoundUpPow2((size_t) m.minHeapLength());
+    m.requireHeapLengthToBeAtLeast(len);
+
     *mir = f.extractMIR();
+    (*mir)->noteMinAsmJSHeapLength(len);
     *funcOut = func;
     return true;
 }
 
 static bool
 GenerateCode(ModuleCompiler &m, ModuleCompiler::Func &func, MIRGenerator &mir, LIRGraph &lir)
 {
     int64_t before = PRMJ_Now();
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -213,16 +213,22 @@ DynamicallyLinkModule(JSContext *cx, Cal
         if (!IsTypedArrayBuffer(bufferVal))
             return LinkFail(cx, "bad ArrayBuffer argument");
 
         heap = &bufferVal.toObject().as<ArrayBufferObject>();
 
         if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity)
             return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than or equal to 4096");
 
+        // This check is sufficient without considering the size of the loaded datum because heap
+        // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
+        JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
+        if (heap->byteLength() < module.minHeapLength())
+            return LinkFail(cx, "ArrayBuffer byteLength less than the largest source code heap length constraint.");
+
         if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
 
         module.patchHeapAccesses(heap, cx);
     }
 
     AutoObjectVector ffis(cx);
     if (!ffis.resize(module.numFFIs()))
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -21,21 +21,25 @@
 
 using namespace js;
 
 void
 AsmJSModule::patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx)
 {
     JS_ASSERT(IsPowerOfTwo(heap->byteLength()));
 #if defined(JS_CPU_X86)
-    void *heapOffset = (void*)heap->dataPointer();
+    uint8_t *heapOffset = heap->dataPointer();
     void *heapLength = (void*)heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
-        JSC::X86Assembler::setPointer(heapAccesses_[i].patchLengthAt(code_), heapLength);
-        JSC::X86Assembler::setPointer(heapAccesses_[i].patchOffsetAt(code_), heapOffset);
+        const jit::AsmJSHeapAccess &access = heapAccesses_[i];
+        if (access.hasLengthCheck())
+            JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
+        void *addr = access.patchOffsetAt(code_);
+        uint32_t disp = reinterpret_cast<uint32_t>(JSC::X86Assembler::getPointer(addr));
+        JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
     }
 #elif defined(JS_CPU_ARM)
     uint32_t bits = mozilla::CeilingLog2(heap->byteLength());
     for (unsigned i = 0; i < heapAccesses_.length(); i++)
         jit::Assembler::updateBoundsCheck(bits, (jit::Instruction*)(heapAccesses_[i].offset() + code_));
     // We already know the exact extent of areas that need to be patched, just make sure we
     // flush all of them at once.
     jit::AutoFlushCache::updateTop(uintptr_t(code_), pod.codeBytes_);
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -313,16 +313,17 @@ class AsmJSModule
     PropertyName *                        globalArgumentName_;
     PropertyName *                        importArgumentName_;
     PropertyName *                        bufferArgumentName_;
 
     GlobalVector                          globals_;
     ExitVector                            exits_;
     ExportedFunctionVector                exports_;
     HeapAccessVector                      heapAccesses_;
+    uint32_t                              minHeapLength_;
 #if defined(MOZ_VTUNE) or defined(JS_ION_PERF)
     ProfiledFunctionVector                profiledFunctions_;
 #endif
 #if defined(JS_ION_PERF)
     ProfiledBlocksFunctionVector          perfProfiledBlocksFunctions_;
 #endif
 
     struct Pod {
@@ -344,16 +345,17 @@ class AsmJSModule
     AsmJSModuleSourceDesc                 sourceDesc_;
     FunctionCountsVector                  functionCounts_;
 
   public:
     explicit AsmJSModule()
       : globalArgumentName_(NULL),
         importArgumentName_(NULL),
         bufferArgumentName_(NULL),
+        minHeapLength_(AsmJSAllocationGranularity),
         code_(NULL),
         operationCallbackExit_(NULL),
         linked_(false)
     {
         mozilla::PodZero(&pod);
     }
 
     ~AsmJSModule();
@@ -625,16 +627,24 @@ class AsmJSModule
     jit::AsmJSHeapAccess &heapAccess(unsigned i) {
         return heapAccesses_[i];
     }
     const jit::AsmJSHeapAccess &heapAccess(unsigned i) const {
         return heapAccesses_[i];
     }
     void patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx);
 
+    void requireHeapLengthToBeAtLeast(uint32_t len) {
+        if (len > minHeapLength_)
+            minHeapLength_ = len;
+    }
+    uint32_t minHeapLength() const {
+        return minHeapLength_;
+    }
+
     uint8_t *allocateCodeAndGlobalSegment(ExclusiveContext *cx, size_t bytesNeeded);
 
     uint8_t *functionCode() const {
         JS_ASSERT(code_);
         JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
         return code_;
     }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1301,17 +1301,17 @@ OptimizeMIR(MIRGenerator *mir)
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
         }
     }
 
     if (js_IonOptions.rangeAnalysis) {
-        RangeAnalysis r(graph);
+        RangeAnalysis r(mir, graph);
         if (!r.addBetaNobes())
             return false;
         IonSpewPass("Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA Beta"))
             return false;
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2785,23 +2785,16 @@ LIRGenerator::visitHaveSameClass(MHaveSa
 
     JS_ASSERT(lhs->type() == MIRType_Object);
     JS_ASSERT(rhs->type() == MIRType_Object);
 
     return define(new LHaveSameClass(useRegister(lhs), useRegister(rhs), temp()), ins);
 }
 
 bool
-LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
-{
-    LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(useRegisterAtStart(ins->ptr()));
-    return define(lir, ins);
-}
-
-bool
 LIRGenerator::visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins)
 {
     return define(new LAsmJSLoadGlobalVar, ins);
 }
 
 bool
 LIRGenerator::visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins)
 {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -232,17 +232,16 @@ class LIRGenerator : public LIRGenerator
     bool visitThrow(MThrow *ins);
     bool visitIn(MIn *ins);
     bool visitInArray(MInArray *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitCallInstanceOf(MCallInstanceOf *ins);
     bool visitFunctionBoundary(MFunctionBoundary *ins);
     bool visitIsCallable(MIsCallable *ins);
     bool visitHaveSameClass(MHaveSameClass *ins);
-    bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins);
     bool visitAsmJSParameter(MAsmJSParameter *ins);
     bool visitAsmJSReturn(MAsmJSReturn *ins);
     bool visitAsmJSVoidReturn(MAsmJSVoidReturn *ins);
     bool visitAsmJSPassStackArg(MAsmJSPassStackArg *ins);
     bool visitAsmJSCall(MAsmJSCall *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8153,56 +8153,65 @@ class MAsmJSUMod : public MBinaryInstruc
 
   public:
     INSTRUCTION_HEADER(AsmJSUMod);
     static MAsmJSUMod *New(MDefinition *left, MDefinition *right) {
         return new MAsmJSUMod(left, right);
     }
 };
 
-class MAsmJSLoadHeap : public MUnaryInstruction
+class MAsmJSHeapAccess
+{
+    ArrayBufferView::ViewType viewType_;
+    bool skipBoundsCheck_;
+
+  public:
+    MAsmJSHeapAccess(ArrayBufferView::ViewType vt, bool s)
+      : viewType_(vt), skipBoundsCheck_(s)
+    {}
+
+    ArrayBufferView::ViewType viewType() const { return viewType_; }
+    bool skipBoundsCheck() const { return skipBoundsCheck_; }
+    void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; }
+};
+
+class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
-      : MUnaryInstruction(ptr), viewType_(vt)
+      : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
     {
         if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
-    ArrayBufferView::ViewType viewType_;
-
   public:
     INSTRUCTION_HEADER(AsmJSLoadHeap);
 
     static MAsmJSLoadHeap *New(ArrayBufferView::ViewType vt, MDefinition *ptr) {
         return new MAsmJSLoadHeap(vt, ptr);
     }
 
-    ArrayBufferView::ViewType viewType() const { return viewType_; }
     MDefinition *ptr() const { return getOperand(0); }
 };
 
-class MAsmJSStoreHeap : public MBinaryInstruction
+class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSStoreHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v)
-      : MBinaryInstruction(ptr, v), viewType_(vt)
+      : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false)
     {}
 
-    ArrayBufferView::ViewType viewType_;
-
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap);
 
     static MAsmJSStoreHeap *New(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) {
         return new MAsmJSStoreHeap(vt, ptr, v);
     }
 
-    ArrayBufferView::ViewType viewType() const { return viewType_; }
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
 };
 
 class MAsmJSLoadGlobalVar : public MNullaryInstruction
 {
     MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset)
       : globalDataOffset_(globalDataOffset)
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -117,16 +117,22 @@ class MIRGenerator
         return performsAsmJSCall_;
     }
     bool noteHeapAccess(AsmJSHeapAccess heapAccess) {
         return asmJSHeapAccesses_.append(heapAccess);
     }
     const Vector<AsmJSHeapAccess, 0, IonAllocPolicy> &heapAccesses() const {
         return asmJSHeapAccesses_;
     }
+    void noteMinAsmJSHeapLength(uint32_t len) {
+        minAsmJSHeapLength_ = len;
+    }
+    uint32_t minAsmJSHeapLength() const {
+        return minAsmJSHeapLength_;
+    }
     bool noteGlobalAccess(unsigned offset, unsigned globalDataOffset) {
         return asmJSGlobalAccesses_.append(AsmJSGlobalAccess(offset, globalDataOffset));
     }
     const Vector<AsmJSGlobalAccess, 0, IonAllocPolicy> &globalAccesses() const {
         return asmJSGlobalAccesses_;
     }
 
   public:
@@ -140,16 +146,17 @@ class MIRGenerator
     MIRGraph *graph_;
     bool error_;
     size_t cancelBuild_;
 
     uint32_t maxAsmJSStackArgBytes_;
     bool performsAsmJSCall_;
     AsmJSHeapAccessVector asmJSHeapAccesses_;
     AsmJSGlobalAccessVector asmJSGlobalAccesses_;
+    uint32_t minAsmJSHeapLength_;
 
 #if defined(JS_ION_PERF)
     AsmJSPerfSpewer asmJSPerfSpewer_;
 
   public:
     AsmJSPerfSpewer &perfSpewer() { return asmJSPerfSpewer_; }
 #endif
 };
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -3,16 +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/. */
 
 #include "jit/MIRGraph.h"
 
 #include "jsanalyze.h"
 
+#include "jit/AsmJS.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/IonSpewer.h"
 #include "jit/MIR.h"
 
 #include "jsinferinlines.h"
 
 using namespace js;
@@ -22,17 +23,18 @@ MIRGenerator::MIRGenerator(JSCompartment
                            TempAllocator *temp, MIRGraph *graph, CompileInfo *info)
   : compartment(compartment),
     info_(info),
     temp_(temp),
     graph_(graph),
     error_(false),
     cancelBuild_(0),
     maxAsmJSStackArgBytes_(0),
-    performsAsmJSCall_(false)
+    performsAsmJSCall_(false),
+    minAsmJSHeapLength_(AsmJSAllocationGranularity)
 { }
 
 bool
 MIRGenerator::abortFmt(const char *message, va_list ap)
 {
     IonSpewVA(IonSpew_Abort, message, ap);
     error_ = true;
     return false;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1614,16 +1614,36 @@ RangeAnalysis::analyze()
 
             def->computeRange();
             IonSpew(IonSpew_Range, "computing range on %d", def->id());
             SpewRange(def);
         }
 
         if (block->isLoopHeader())
             analyzeLoop(block);
+
+        if (mir->compilingAsmJS()) {
+            for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
+                if (i->isAsmJSLoadHeap()) {
+                    MAsmJSLoadHeap *ins = i->toAsmJSLoadHeap();
+                    Range *range = ins->ptr()->range();
+                    if (range && !range->isLowerInfinite() && range->lower() >= 0 &&
+                        !range->isUpperInfinite() &&
+                        (uint32_t) range->upper() < mir->minAsmJSHeapLength())
+                        ins->setSkipBoundsCheck(true);
+                } else if (i->isAsmJSStoreHeap()) {
+                    MAsmJSStoreHeap *ins = i->toAsmJSStoreHeap();
+                    Range *range = ins->ptr()->range();
+                    if (range && !range->isLowerInfinite() && range->lower() >= 0 &&
+                        !range->isUpperInfinite() &&
+                        (uint32_t) range->upper() < mir->minAsmJSHeapLength())
+                        ins->setSkipBoundsCheck(true);
+                }
+            }
+        }
     }
 
     return true;
 }
 
 bool
 RangeAnalysis::addRangeAssertions()
 {
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -69,21 +69,22 @@ struct SymbolicBound : public TempObject
 class RangeAnalysis
 {
   protected:
     bool blockDominates(MBasicBlock *b, MBasicBlock *b2);
     void replaceDominatedUsesWith(MDefinition *orig, MDefinition *dom,
                                   MBasicBlock *block);
 
   protected:
+    MIRGenerator *mir;
     MIRGraph &graph_;
 
   public:
-    MOZ_CONSTEXPR RangeAnalysis(MIRGraph &graph) :
-        graph_(graph) {}
+    MOZ_CONSTEXPR RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
+        mir(mir), graph_(graph) {}
     bool addBetaNobes();
     bool analyze();
     bool addRangeAssertions();
     bool removeBetaNobes();
     bool truncate();
 
   private:
     void analyzeLoop(MBasicBlock *header);
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -799,44 +799,47 @@ class AsmJSHeapAccess
     uint8_t isFloat32Load_;
     jit::AnyRegister::Code loadedReg_ : 8;
 #endif
 
     JS_STATIC_ASSERT(jit::AnyRegister::Total < UINT8_MAX);
 
   public:
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
+    // If 'cmp' equals 'offset' or if it is not supplied then the
+    // cmpDelta_ is zero indicating that there is no length to patch.
     AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt,
                     AnyRegister loadedReg, uint32_t cmp = UINT32_MAX)
       : offset_(offset),
 # if defined(JS_CPU_X86)
-        cmpDelta_(offset - cmp),
+        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
 # endif
         opLength_(after - offset),
         isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32),
         loadedReg_(loadedReg.code())
     {}
     AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX)
       : offset_(offset),
 # if defined(JS_CPU_X86)
-        cmpDelta_(offset - cmp),
+        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
 # endif
         opLength_(after - offset),
         isFloat32Load_(false),
         loadedReg_(UINT8_MAX)
     {}
 #elif defined(JS_CPU_ARM)
     explicit AsmJSHeapAccess(uint32_t offset)
       : offset_(offset)
     {}
 #endif
 
     uint32_t offset() const { return offset_; }
     void setOffset(uint32_t offset) { offset_ = offset; }
 #if defined(JS_CPU_X86)
+    bool hasLengthCheck() const { return cmpDelta_ > 0; }
     void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
     void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); }
 #endif
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     unsigned opLength() const { return opLength_; }
     bool isLoad() const { return loadedReg_ != UINT8_MAX; }
     bool isFloat32Load() const { return isFloat32Load_; }
     jit::AnyRegister loadedReg() const { return jit::AnyRegister::FromCode(loadedReg_); }
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1774,42 +1774,78 @@ CodeGeneratorARM::visitStoreTypedArrayEl
 bool
 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     const MAsmJSLoadHeap *mir = ins->mir();
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->viewType()) {
-      case ArrayBufferView::TYPE_INT8:    isSigned = true; size = 8; break;
-      case ArrayBufferView::TYPE_UINT8:   isSigned = false; size = 8; break;
-      case ArrayBufferView::TYPE_INT16:   isSigned = true; size = 16; break;
+      case ArrayBufferView::TYPE_INT8:    isSigned = true;  size =  8; break;
+      case ArrayBufferView::TYPE_UINT8:   isSigned = false; size =  8; break;
+      case ArrayBufferView::TYPE_INT16:   isSigned = true;  size = 16; break;
       case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  isSigned = true;  size = 32; break;
       case ArrayBufferView::TYPE_FLOAT64: isFloat = true;   size = 64; break;
-      case ArrayBufferView::TYPE_FLOAT32:
-        isFloat = true;
-        size = 32;
-        break;
+      case ArrayBufferView::TYPE_FLOAT32: isFloat = true;   size = 32; break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
-    Register index = ToRegister(ins->ptr());
-    BufferOffset bo = masm.ma_BoundsCheck(index);
+
+    const LAllocation *ptr = ins->ptr();
+
+    if (ptr->isConstant()) {
+        JS_ASSERT(mir->skipBoundsCheck());
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        JS_ASSERT(ptrImm >= 0);
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->output()));
+            if (size == 32) {
+                masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
+                masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
+            } else {
+                masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always);
+            }
+        }  else {
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
+                                  ToRegister(ins->output()), Offset, Assembler::Always);
+        }
+        return true;
+    }
+
+    Register ptrReg = ToRegister(ptr);
+
+    if (mir->skipBoundsCheck()) {
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->output()));
+            if (size == 32) {
+                masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
+                masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
+            } else {
+                masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always);
+            }
+        } else {
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
+                                  ToRegister(ins->output()), Offset, Assembler::Always);
+        }
+        return true;
+    }
+
+    BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->output()));
         if (size == 32) {
-            masm.ma_vldr(vd.singleOverlay(), HeapReg, index, 0, Assembler::Zero);
+            masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Zero);
             masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Zero);
         } else {
-            masm.ma_vldr(vd, HeapReg, index, 0, Assembler::Zero);
+            masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Zero);
         }
         masm.ma_vmov(NANReg, ToFloatRegister(ins->output()), Assembler::NonZero);
-    }  else {
-        masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, index,
+    } else {
+        masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->output()), Offset, Assembler::Zero);
         masm.ma_mov(Imm32(0), ToRegister(ins->output()), NoSetCond, Assembler::NonZero);
     }
     return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
 }
 
 bool
 CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
@@ -1820,35 +1856,66 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
     bool isFloat = false;
     switch (mir->viewType()) {
       case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_UINT8:   isSigned = false; size = 8; break;
       case ArrayBufferView::TYPE_INT16:
       case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  isSigned = true;  size = 32; break;
-      case ArrayBufferView::TYPE_FLOAT64: isFloat = true;   size = 64; break;
-      case ArrayBufferView::TYPE_FLOAT32:
-        isFloat = true;
-        size = 32;
-        break;
+      case ArrayBufferView::TYPE_FLOAT64: isFloat  = true;  size = 64; break;
+      case ArrayBufferView::TYPE_FLOAT32: isFloat = true;   size = 32; break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
-    Register index = ToRegister(ins->ptr());
+    const LAllocation *ptr = ins->ptr();
+    if (ptr->isConstant()) {
+        JS_ASSERT(mir->skipBoundsCheck());
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        JS_ASSERT(ptrImm >= 0);
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->value()));
+            if (size == 32) {
+                masm.as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), vd, false, Assembler::Always);
+                masm.ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
+            } else {
+                masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always);
+            }
+        } else {
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
+                                  ToRegister(ins->value()), Offset, Assembler::Always);
+        }
+        return true;
+    }
 
-    BufferOffset bo = masm.ma_BoundsCheck(index);
+    Register ptrReg = ToRegister(ptr);
+
+    if (mir->skipBoundsCheck()) {
+        Register ptrReg = ToRegister(ptr);
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->value()));
+            if (size == 32)
+                masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Always);
+            else
+                masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
+        } else {
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
+                                  ToRegister(ins->value()), Offset, Assembler::Always);
+        }
+        return true;
+    }
+
+    BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->value()));
-        if (size == 32) {
-            masm.storeFloat(vd, HeapReg, index, Assembler::Zero);
-        } else {
-            masm.ma_vstr(vd, HeapReg, index, 0, Assembler::Zero);
-        }
-    }  else {
-        masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, index,
+        if (size == 32)
+            masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Zero);
+        else
+            masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Zero);
+    } else {
+        masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->value()), Offset, Assembler::Zero);
     }
     return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
 }
 
 bool
 CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
 {
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -497,35 +497,48 @@ bool
 LIRGeneratorARM::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_Int32);
     LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input()));
     return define(lir, ins);
 }
 
 bool
+LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
+{
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+    LAllocation ptrAlloc;
+
+    // For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed.
+    if (ptr->isConstant() && ins->skipBoundsCheck()) {
+        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+        // A bounds check is only skipped for a positive index.
+        JS_ASSERT(ptrValue >= 0);
+        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    } else
+        ptrAlloc = useRegisterAtStart(ptr);
+
+    return define(new LAsmJSLoadHeap(ptrAlloc), ins);
+}
+
+bool
 LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
-    LAsmJSStoreHeap *lir;
-    switch (ins->viewType()) {
-      case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
-      case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
-      case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterAtStart(ins->value()));
-        break;
-      case ArrayBufferView::TYPE_FLOAT32:
-      case ArrayBufferView::TYPE_FLOAT64:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterAtStart(ins->value()));
-        break;
-      default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
-    }
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+    LAllocation ptrAlloc;
 
-    return add(lir, ins);
+    if (ptr->isConstant() && ins->skipBoundsCheck()) {
+        JS_ASSERT(ptr->toConstant()->value().toInt32() >= 0);
+        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    } else
+        ptrAlloc = useRegisterAtStart(ptr);
+
+    return add(new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
 }
 
 bool
 LIRGeneratorARM::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
 }
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -73,16 +73,17 @@ class LIRGeneratorARM : public LIRGenera
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool lowerPhi(MPhi *phi);
     bool visitGuardShape(MGuardShape *ins);
     bool visitGuardObjectType(MGuardObjectType *ins);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
+    bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -259,16 +259,24 @@ LAllocation
 LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition *mir)
 {
     if (mir->isConstant())
         return LAllocation(mir->toConstant()->vp());
     return useRegisterAtStart(mir);
 }
 
 LAllocation
+LIRGeneratorShared::useRegisterOrNonNegativeConstantAtStart(MDefinition *mir)
+{
+    if (mir->isConstant() && mir->toConstant()->value().toInt32() >= 0)
+        return LAllocation(mir->toConstant()->vp());
+    return useRegisterAtStart(mir);
+}
+
+LAllocation
 LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir)
 {
     if (mir->isConstant() && mir->type() != MIRType_Double)
         return LAllocation(mir->toConstant()->vp());
     return useRegister(mir);
 }
 
 #if defined(JS_CPU_ARM)
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -81,16 +81,17 @@ class LIRGeneratorShared : public MInstr
     // "Storable" is architecture dependend, and will include registers and constants on X86
     // and only registers on ARM.
     // this is a generic "things we can expect to write into memory in 1 instruction"
     inline LAllocation useStorable(MDefinition *mir);
     inline LAllocation useStorableAtStart(MDefinition *mir);
     inline LAllocation useKeepaliveOrConstant(MDefinition *mir);
     inline LAllocation useRegisterOrConstant(MDefinition *mir);
     inline LAllocation useRegisterOrConstantAtStart(MDefinition *mir);
+    inline LAllocation useRegisterOrNonNegativeConstantAtStart(MDefinition *mir);
     inline LAllocation useRegisterOrNonDoubleConstant(MDefinition *mir);
 
 #ifdef JS_NUNBOX32
     inline LUse useType(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayload(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayloadAtStart(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayloadInRegisterAtStart(MDefinition *mir);
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -389,84 +389,109 @@ CodeGeneratorX64::visitStoreTypedArrayEl
     MOZ_ASSUME_UNREACHABLE("NYI");
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     MAsmJSLoadHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    const LAllocation *ptr = ins->ptr();
 
-    Operand srcAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
+    // No need to note the access if it will never fault.
+    bool skipNote = mir->skipBoundsCheck();
+    Operand srcAddr(HeapReg);
+
+    if (ptr->isConstant()) {
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        // Note only a positive index is accepted here because a negative offset would
+        // not wrap back into the protected area reserved for the heap.
+        JS_ASSERT(ptrImm >= 0);
+        srcAddr = Operand(HeapReg, ptrImm);
+    } else {
+        srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
+    }
 
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         FloatRegister dest = ToFloatRegister(ins->output());
         uint32_t before = masm.size();
         masm.movss(srcAddr, dest);
         uint32_t after = masm.size();
         masm.cvtss2sd(dest, dest);
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
+        return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
     }
 
     uint32_t before = masm.size();
     switch (vt) {
       case ArrayBufferView::TYPE_INT8:    masm.movsbl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_UINT8:   masm.movzbl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_INT16:   masm.movswl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_UINT16:  masm.movzwl(srcAddr, ToRegister(ins->output())); break;
-      case ArrayBufferView::TYPE_INT32:   masm.movl(srcAddr, ToRegister(ins->output())); break;
+      case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.movsd(srcAddr, ToFloatRegister(ins->output())); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
     uint32_t after = masm.size();
-    return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
+    return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
 }
 
 bool
 CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
     MAsmJSStoreHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    const LAllocation *ptr = ins->ptr();
+    // No need to note the access if it will never fault.
+    bool skipNote = mir->skipBoundsCheck();
+    Operand dstAddr(HeapReg);
 
-    Operand dstAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
+    if (ptr->isConstant()) {
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        // Note only a positive index is accepted here because a negative offset would
+        // not wrap back into the protected area reserved for the heap.
+        JS_ASSERT(ptrImm >= 0);
+        dstAddr = Operand(HeapReg, ptrImm);
+    } else {
+        dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
+    }
 
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg);
         uint32_t before = masm.size();
         masm.movss(ScratchFloatReg, dstAddr);
         uint32_t after = masm.size();
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
+        return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
     }
 
     uint32_t before = masm.size();
     if (ins->value()->isConstant()) {
         switch (vt) {
-          case ArrayBufferView::TYPE_INT8:    masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
+          case ArrayBufferView::TYPE_INT8:
           case ArrayBufferView::TYPE_UINT8:   masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
-          case ArrayBufferView::TYPE_INT16:   masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
+          case ArrayBufferView::TYPE_INT16:
           case ArrayBufferView::TYPE_UINT16:  masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
-          case ArrayBufferView::TYPE_INT32:   masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
+          case ArrayBufferView::TYPE_INT32:
           case ArrayBufferView::TYPE_UINT32:  masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     } else {
         switch (vt) {
-          case ArrayBufferView::TYPE_INT8:    masm.movb(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_INT8:
           case ArrayBufferView::TYPE_UINT8:   masm.movb(ToRegister(ins->value()), dstAddr); break;
-          case ArrayBufferView::TYPE_INT16:   masm.movw(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_INT16:
           case ArrayBufferView::TYPE_UINT16:  masm.movw(ToRegister(ins->value()), dstAddr); break;
-          case ArrayBufferView::TYPE_INT32:   masm.movl(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_INT32:
           case ArrayBufferView::TYPE_UINT32:  masm.movl(ToRegister(ins->value()), dstAddr); break;
           case ArrayBufferView::TYPE_FLOAT64: masm.movsd(ToFloatRegister(ins->value()), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     }
     uint32_t after = masm.size();
-    return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
+    return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
 
     CodeOffsetLabel label;
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -138,30 +138,51 @@ bool
 LIRGeneratorX64::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_Int32);
     LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input()));
     return define(lir, ins);
 }
 
 bool
+LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
+{
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+
+    // The X64 does not inline an explicit bounds check so has no need to keep the
+    // index in a register, however only a positive index is accepted because a
+    // negative offset encoded as an offset in the addressing mode would not wrap
+    // back into the protected area reserved for the heap.
+    if (ptr->isConstant() && ptr->toConstant()->value().toInt32() >= 0) {
+        LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(LAllocation(ptr->toConstant()->vp()));
+        return define(lir, ins);
+    }
+    return define(new LAsmJSLoadHeap(useRegisterAtStart(ptr)), ins);
+}
+
+bool
 LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
     LAsmJSStoreHeap *lir;
+
+    // Note only a positive constant index is accepted because a negative offset
+    // encoded as an offset in the addressing mode would not wrap back into the
+    // protected area reserved for the heap.
+    LAllocation ptrAlloc = useRegisterOrNonNegativeConstantAtStart(ptr);
     switch (ins->viewType()) {
       case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
       case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
       case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterOrConstantAtStart(ins->value()));
+        lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterOrConstantAtStart(ins->value()));
         break;