Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 23 Apr 2013 20:49:27 -0400
changeset 140619 fef5f202b2dc612585184e214fe9ebfc2e2748ac
parent 140571 d8202613aaea2d07176f9f3c2a7c61cc5aa37f07 (current diff)
parent 140618 9a8232d88dc4272613a69c5fe5d60e5922925b78 (diff)
child 140646 e63dfc0d3607ba495093fd8dab9276b098a6885b
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
first release with
nightly linux32
fef5f202b2dc / 23.0a1 / 20130424030917 / files
nightly linux64
fef5f202b2dc / 23.0a1 / 20130424030917 / files
nightly mac
fef5f202b2dc / 23.0a1 / 20130424030917 / files
nightly win32
fef5f202b2dc / 23.0a1 / 20130424030917 / files
nightly win64
fef5f202b2dc / 23.0a1 / 20130424030917 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
browser/themes/linux/places/pageStarred.png
browser/themes/linux/places/starPage.png
browser/themes/windows/places/editBookmark.png
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -34,52 +34,70 @@ this.AccessFu = {
 
     try {
       Cc['@mozilla.org/android/bridge;1'].
         getService(Ci.nsIAndroidBridge).handleGeckoMessage(
           JSON.stringify({ type: 'Accessibility:Ready' }));
       Services.obs.addObserver(this, 'Accessibility:Settings', false);
     } catch (x) {
       // Not on Android
-      aWindow.addEventListener(
-        'ContentStart',
-        (function(event) {
-           let content = aWindow.shell.contentBrowser.contentWindow;
-           content.addEventListener('mozContentEvent', this, false, true);
-         }).bind(this), false);
+      if (Utils.MozBuildApp === 'b2g') {
+        aWindow.addEventListener('ContentStart', this, false);
+      }
     }
 
     try {
       this._activatePref = this.prefsBranch.getIntPref('activate');
     } catch (x) {
       this._activatePref = ACCESSFU_DISABLE;
     }
 
     Input.quickNavMode.updateModes(this.prefsBranch);
 
     this._enableOrDisable();
   },
 
   /**
+   * Shut down chrome-layer accessibility functionality from the outside.
+   */
+  detach: function detach() {
+    // Avoid disabling twice.
+    if (this._enabled) {
+      this._disable();
+    }
+    if (Utils.MozBuildApp === 'mobile/android') {
+      Services.obs.removeObserver(this, 'Accessibility:Settings');
+    } else if (Utils.MozBuildApp === 'b2g') {
+      Utils.win.shell.contentBrowser.contentWindow.removeEventListener(
+        'mozContentEvent', this);
+      Utils.win.removeEventListener('ContentStart', this);
+    }
+    this.prefsBranch.removeObserver('activate', this);
+    Utils.uninit();
+  },
+
+  /**
    * Start AccessFu mode, this primarily means controlling the virtual cursor
    * with arrow keys.
    */
   _enable: function _enable() {
     if (this._enabled)
       return;
     this._enabled = true;
 
     Cu.import('resource://gre/modules/accessibility/Utils.jsm');
     Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
     Cu.import('resource://gre/modules/accessibility/Presentation.jsm');
 
     Logger.info('enable');
 
-    for each (let mm in Utils.AllMessageManagers)
+    for each (let mm in Utils.AllMessageManagers) {
+      this._addMessageListeners(mm);
       this._loadFrameScript(mm);
+    }
 
     // Add stylesheet
     let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
     let stylesheet = Utils.win.document.createProcessingInstruction(
       'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
     Utils.win.document.insertBefore(stylesheet, Utils.win.document.firstChild);
     this.stylesheet = Cu.getWeakReference(stylesheet);
 
@@ -88,45 +106,57 @@ this.AccessFu = {
     TouchAdapter.start();
 
     Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
     Services.obs.addObserver(this, 'Accessibility:NextObject', false);
     Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
     Services.obs.addObserver(this, 'Accessibility:Focus', false);
     Utils.win.addEventListener('TabOpen', this);
     Utils.win.addEventListener('TabSelect', this);
+
+    if (this.readyCallback) {
+      this.readyCallback();
+      delete this.readyCallback;
+    }
   },
 
   /**
    * Disable AccessFu and return to default interaction mode.
    */
   _disable: function _disable() {
     if (!this._enabled)
       return;
 
     this._enabled = false;
 
     Logger.info('disable');
 
     Utils.win.document.removeChild(this.stylesheet.get());
 
-    for each (let mm in Utils.AllMessageManagers)
+    for each (let mm in Utils.AllMessageManagers) {
       mm.sendAsyncMessage('AccessFu:Stop');
+      this._removeMessageListeners(mm);
+    }
 
     Input.stop();
     Output.stop();
     TouchAdapter.stop();
 
     Utils.win.removeEventListener('TabOpen', this);
     Utils.win.removeEventListener('TabSelect', this);
 
     Services.obs.removeObserver(this, 'remote-browser-frame-shown');
     Services.obs.removeObserver(this, 'Accessibility:NextObject');
     Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
     Services.obs.removeObserver(this, 'Accessibility:Focus');
+
+    if (this.doneCallback) {
+      this.doneCallback();
+      delete this.doneCallback;
+    }
   },
 
   _enableOrDisable: function _enableOrDisable() {
     try {
       if (this._activatePref == ACCESSFU_ENABLE ||
           this._systemPref && this._activatePref == ACCESSFU_AUTO)
         this._enable();
       else
@@ -138,18 +168,20 @@ this.AccessFu = {
 
   receiveMessage: function receiveMessage(aMessage) {
     if (Logger.logLevel >= Logger.DEBUG)
       Logger.debug('Recieved', aMessage.name, JSON.stringify(aMessage.json));
 
     switch (aMessage.name) {
       case 'AccessFu:Ready':
         let mm = Utils.getMessageManager(aMessage.target);
-        mm.sendAsyncMessage('AccessFu:Start',
-                            {method: 'start', buildApp: Utils.MozBuildApp});
+        if (this._enabled) {
+          mm.sendAsyncMessage('AccessFu:Start',
+                              {method: 'start', buildApp: Utils.MozBuildApp});
+        }
         break;
       case 'AccessFu:Present':
         this._output(aMessage.json, aMessage.target);
         break;
       case 'AccessFu:Input':
         Input.setEditState(aMessage.json);
         break;
     }
@@ -164,22 +196,45 @@ this.AccessFu = {
           Output[presenter.type](presenter.details, aBrowser);
         }
       } catch (x) {
         Logger.logException(x);
       }
   },
 
   _loadFrameScript: function _loadFrameScript(aMessageManager) {
+    if (this._processedMessageManagers.indexOf(aMessageManager) < 0) {
+      aMessageManager.loadFrameScript(
+        'chrome://global/content/accessibility/content-script.js', true);
+      this._processedMessageManagers.push(aMessageManager);
+    } else if (this._enabled) {
+      // If the content-script is already loaded and AccessFu is enabled,
+      // send an AccessFu:Start message.
+      aMessageManager.sendAsyncMessage('AccessFu:Start',
+        {method: 'start', buildApp: Utils.MozBuildApp});
+    }
+  },
+
+  _addMessageListeners: function _addMessageListeners(aMessageManager) {
     aMessageManager.addMessageListener('AccessFu:Present', this);
     aMessageManager.addMessageListener('AccessFu:Input', this);
     aMessageManager.addMessageListener('AccessFu:Ready', this);
-    aMessageManager.
-      loadFrameScript(
-        'chrome://global/content/accessibility/content-script.js', true);
+  },
+
+  _removeMessageListeners: function _removeMessageListeners(aMessageManager) {
+    aMessageManager.removeMessageListener('AccessFu:Present', this);
+    aMessageManager.removeMessageListener('AccessFu:Input', this);
+    aMessageManager.removeMessageListener('AccessFu:Ready', this);
+  },
+
+  _handleMessageManager: function _handleMessageManager(aMessageManager) {
+    if (this._enabled) {
+      this._addMessageListeners(aMessageManager);
+    }
+    this._loadFrameScript(aMessageManager);
   },
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case 'Accessibility:Settings':
         this._systemPref = JSON.parse(aData).enabled;
         this._enableOrDisable();
         break;
@@ -202,36 +257,43 @@ this.AccessFu = {
           this._activatePref = this.prefsBranch.getIntPref('activate');
           this._enableOrDisable();
         } else if (aData == 'quicknav_modes') {
           Input.quickNavMode.updateModes(this.prefsBranch);
         }
         break;
       case 'remote-browser-frame-shown':
       {
-        this._loadFrameScript(
-          aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager);
+        let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager;
+        this._handleMessageManager(mm);
         break;
       }
     }
   },
 
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
+      case 'ContentStart':
+      {
+        Utils.win.shell.contentBrowser.contentWindow.addEventListener(
+          'mozContentEvent', this, false, true);
+        break;
+      }
       case 'mozContentEvent':
       {
         if (aEvent.detail.type == 'accessibility-screenreader') {
           this._systemPref = aEvent.detail.enabled;
           this._enableOrDisable();
         }
         break;
       }
       case 'TabOpen':
       {
-        this._loadFrameScript(Utils.getMessageManager(aEvent.target));
+        let mm = Utils.getMessageManager(aEvent.target);
+        this._handleMessageManager(mm);
         break;
       }
       case 'TabSelect':
       {
         if (this._focused) {
           let mm = Utils.getMessageManager(Utils.CurrentBrowser);
           // We delay this for half a second so the awesomebar could close,
           // and we could use the current coordinates for the content item.
@@ -250,17 +312,21 @@ this.AccessFu = {
     this._output(Presentation.announce(aAnnouncement),
                  Utils.CurrentBrowser);
   },
 
   // So we don't enable/disable twice
   _enabled: false,
 
   // Layerview is focused
-  _focused: false
+  _focused: false,
+
+  // Keep track of message managers tha already have a 'content-script.js'
+  // injected.
+  _processedMessageManagers: []
 };
 
 var Output = {
   start: function start() {
     Cu.import('resource://gre/modules/Geometry.jsm');
   },
 
   stop: function stop() {
--- a/accessible/src/jsat/EventManager.jsm
+++ b/accessible/src/jsat/EventManager.jsm
@@ -33,16 +33,20 @@ this.EventManager = {
 
     } catch (x) {
       Logger.error('Failed to start EventManager');
       Logger.logException(x);
     }
   },
 
   stop: function stop() {
+    if (!this._started) {
+      return;
+    }
+    Logger.info('EventManager.stop', Utils.MozBuildApp);
     Services.obs.removeObserver(this, 'accessible-event');
     this._started = false;
   },
 
   handleEvent: function handleEvent(aEvent) {
     try {
       switch (aEvent.type) {
       case 'DOMActivate':
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -24,16 +24,23 @@ this.Utils = {
   init: function Utils_init(aWindow) {
     if (this._win)
       // XXX: only supports attaching to one window now.
       throw new Error('Only one top-level window could used with AccessFu');
 
     this._win = Cu.getWeakReference(aWindow);
   },
 
+  uninit: function Utils_uninit() {
+    if (!this._win) {
+      return;
+    }
+    delete this._win;
+  },
+
   get win() {
     return this._win.get();
   },
 
   get AccRetrieval() {
     if (!this._AccRetrieval) {
       this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
         getService(Ci.nsIAccessibleRetrieval);
@@ -173,23 +180,35 @@ this.Logger = {
   DEBUG: 0,
   INFO: 1,
   WARNING: 2,
   ERROR: 3,
   _LEVEL_NAMES: ['DEBUG', 'INFO', 'WARNING', 'ERROR'],
 
   logLevel: 1, // INFO;
 
+  test: false,
+
   log: function log(aLogLevel) {
     if (aLogLevel < this.logLevel)
       return;
 
     let message = Array.prototype.slice.call(arguments, 1).join(' ');
-    dump('[' + Utils.ScriptName + '] ' +
-         this._LEVEL_NAMES[aLogLevel] +' ' + message + '\n');
+    message = '[' + Utils.ScriptName + '] ' + this._LEVEL_NAMES[aLogLevel] +
+      ' ' + message + '\n';
+    dump(message);
+    // Note: used for testing purposes. If |this.test| is true, also log to
+    // the console service.
+    if (this.test) {
+      try {
+        Services.console.logStringMessage(message);
+      } catch (ex) {
+        // There was an exception logging to the console service.
+      }
+    }
   },
 
   info: function info() {
     this.log.apply(
       this, [this.INFO].concat(Array.prototype.slice.call(arguments)));
   },
 
   debug: function debug() {
--- a/accessible/src/jsat/content-script.js
+++ b/accessible/src/jsat/content-script.js
@@ -211,26 +211,27 @@ function scroll(aMessage) {
 
   if (!tryToScroll()) {
     // Failed to scroll anything in this document. Try in parent document.
     aMessage.json.origin = 'child';
     sendAsyncMessage('AccessFu:Scroll', aMessage.json);
   }
 }
 
-addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
-addMessageListener('AccessFu:Activate', activateCurrent);
-addMessageListener('AccessFu:Scroll', scroll);
-
 addMessageListener(
   'AccessFu:Start',
   function(m) {
+    Logger.debug('AccessFu:Start');
     if (m.json.buildApp)
       Utils.MozBuildApp = m.json.buildApp;
 
+    addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
+    addMessageListener('AccessFu:Activate', activateCurrent);
+    addMessageListener('AccessFu:Scroll', scroll);
+
     EventManager.start(
       function sendMessage(aName, aDetails) {
         sendAsyncMessage(aName, aDetails);
       });
 
     docShell.QueryInterface(Ci.nsIInterfaceRequestor).
       getInterface(Ci.nsIWebProgress).
       addProgressListener(EventManager,
@@ -242,16 +243,20 @@ addMessageListener(
     addEventListener('DOMActivate', EventManager, true);
   });
 
 addMessageListener(
   'AccessFu:Stop',
   function(m) {
     Logger.debug('AccessFu:Stop');
 
+    removeMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
+    removeMessageListener('AccessFu:Activate', activateCurrent);
+    removeMessageListener('AccessFu:Scroll', scroll);
+
     EventManager.stop();
 
     docShell.QueryInterface(Ci.nsIInterfaceRequestor).
       getInterface(Ci.nsIWebProgress).
       removeProgressListener(EventManager);
     removeEventListener('scroll', EventManager, true);
     removeEventListener('resize', EventManager, true);
     // XXX: Ideally this would be an a11y event. Bug #742280.
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -657,16 +657,33 @@ function shortenString(aString, aMaxLeng
 
   // Trim the string if its length is > MAX_TRIM_LENGTH characters.
   var trimOffset = MAX_TRIM_LENGTH / 2;
   return aString.substring(0, trimOffset - 1) + "..." +
     aString.substring(aString.length - trimOffset, aString.length);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// General Utils
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Return main chrome window (crosses chrome boundary)
+ */
+function getMainChromeWindow(aWindow)
+{
+  return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                .getInterface(Components.interfaces.nsIWebNavigation)
+                .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
+                .rootTreeItem
+                .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                .getInterface(Components.interfaces.nsIDOMWindow);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // Private
 ////////////////////////////////////////////////////////////////////////////////
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible general
 
 function getNodePrettyName(aNode)
 {
--- a/accessible/tests/mochitest/jsat/Makefile.in
+++ b/accessible/tests/mochitest/jsat/Makefile.in
@@ -7,12 +7,14 @@ DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 relativesrcdir = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_A11Y_FILES =\
+jsatcommon.js \
+test_alive.html \
 test_utterance_order.html \
 $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -0,0 +1,91 @@
+// A common module to run tests on the AccessFu module
+
+/**
+  * A global variable holding an array of test functions.
+  */
+var gTestFuncs = [];
+/**
+  * A global Iterator for the array of test functions.
+  */
+var gIterator;
+
+Components.utils.import('resource://gre/modules/Services.jsm');
+Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
+Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
+
+var AccessFuTest = {
+
+  addFunc: function AccessFuTest_addFunc(aFunc) {
+    if (aFunc) {
+      gTestFuncs.push(aFunc);
+    }
+  },
+
+  _waitForExplicitFinish: false,
+
+  waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
+    this._waitForExplicitFinish = true;
+  },
+
+  finish: function AccessFuTest_finish() {
+    // Disable the console service logging.
+    Logger.test = false;
+    AccessFu.doneCallback = function doneCallback() {
+      // This is being called once AccessFu has been shut down.
+      // Detach AccessFu from everything it attached itself to.
+      AccessFu.detach();
+      // and finish the test run.
+      SimpleTest.finish();
+    };
+    // Tear down accessibility and make AccessFu stop.
+    SpecialPowers.setIntPref("accessibility.accessfu.activate", 0);
+  },
+
+  nextTest: function AccessFuTest_nextTest() {
+    var testFunc;
+    try {
+      // Get the next test function from the iterator. If none left,
+      // StopIteration exception is thrown.
+      testFunc = gIterator.next()[1];
+    } catch (ex) {
+      // StopIteration exception.
+      this.finish();
+      return;
+    }
+    testFunc();
+  },
+
+  runTests: function AccessFuTest_runTests() {
+    if (gTestFuncs.length === 0) {
+      ok(false, "No tests specified!");
+      simpleTest.finish();
+      return;
+    }
+
+    // Create an Iterator for gTestFuncs array.
+    gIterator = Iterator(gTestFuncs);
+
+    // Start AccessFu and put it in stand-by.
+    Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
+
+    AccessFu.attach(getMainChromeWindow(window));
+
+    AccessFu.readyCallback = function readyCallback() {
+      // Enable logging to the console service.
+      Logger.test = true;
+      // This is being called once accessibility has been turned on.
+
+      if (AccessFuTest._waitForExplicitFinish) {
+        // Run all test functions asynchronously.
+        AccessFuTest.nextTest();
+      } else {
+        // Run all test functions synchronously.
+        [testFunc() for (testFunc of gTestFuncs)];
+        AccessFuTest.finish();
+      }
+    };
+
+    // Invoke the whole thing.
+    SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_alive.html
@@ -0,0 +1,81 @@
+<html>
+
+<head>
+  <title>AccessFu test for enabling</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="./jsatcommon.js"></script>
+  <script type="application/javascript">
+
+    function confirmAccessFuStart() {
+      ok(AccessFu._enabled, "AccessFu was started and enabled.");
+      AccessFuTest.nextTest();
+    }
+
+    function makeEventManagerListener(waitForMessage, callback) {
+      return {
+        observe: function observe(aMessage) {
+          // Ignore unexpected messages.
+          if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
+            return;
+          }
+          if (aMessage.message.indexOf(waitForMessage) < 0) {
+            return;
+          }
+          Services.console.unregisterListener(this);
+          callback();
+        }
+      };
+    }
+
+    function testEventManagerStartStop() {
+      // Firs listen for initial 'EventManager.start' and disable AccessFu.
+      var initialStartListener = makeEventManagerListener("EventManager.start",
+        function () {
+          ok(EventManager._started, "EventManager was started.");
+          Services.console.registerListener(stopListener);
+          AccessFu._disable();
+        });
+      // Listen for 'EventManager.stop' and enable AccessFu again.
+      var stopListener = makeEventManagerListener("EventManager.stop",
+        function () {
+          isnot(EventManager._started, true, "EventManager was stopped.");
+          Services.console.registerListener(finalStartListener);
+          AccessFu._enable();
+        });
+      // Make sure EventManager is started again.
+      var finalStartListener = makeEventManagerListener("EventManager.start",
+        function () {
+          ok(EventManager._started, "EventManager was started again.");
+          AccessFuTest.finish();
+        });
+
+      Services.console.registerListener(initialStartListener);
+    }
+
+    function doTest() {
+      AccessFuTest.addFunc(confirmAccessFuStart);
+      AccessFuTest.addFunc(testEventManagerStartStop);
+      AccessFuTest.waitForExplicitFinish();
+      AccessFuTest.runTests();  // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+<body>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+     title="[AccessFu] Add mochitest for enabling">
+    Mozilla Bug 811307
+  </a>
+</body>
+</html>
\ No newline at end of file
--- a/b2g/config/panda/config.json
+++ b/b2g/config/panda/config.json
@@ -1,22 +1,23 @@
 {
-    "config_version": 1,
+    "config_version": 2,
     "tooltool_manifest": "releng-pandaboard.tt",
     "mock_target": "mozilla-centos6-i386",
-    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel"],
+    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "git"],
     "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
     "build_targets": ["boottarball", "systemtarball", "userdatatarball", "package-tests"],
     "upload_files": [
         "{workdir}/out/target/product/panda/*.tar.bz2",
         "{workdir}/out/target/product/panda/tests/*.zip",
         "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
         "{srcdir}/b2g/config/panda/README",
         "{workdir}/sources.xml"
     ],
+    "b2g_manifest": "pandaboard.xml",
     "gecko_l10n_root": "http://hg.mozilla.org/l10n-central",
     "gaia": {
         "vcs": "hgtool",
         "repo": "http://hg.mozilla.org/integration/gaia-central",
         "l10n": {
             "vcs": "hgtool",
             "root": "http://hg.mozilla.org/gaia-l10n"
         }
--- a/b2g/config/panda/releng-pandaboard.tt
+++ b/b2g/config/panda/releng-pandaboard.tt
@@ -1,14 +1,8 @@
 [
 {
-"size": 678265436,
-"digest": "36d05d77831be476e639095c04f25557171bb61c6764b2f6a49e253471aac8855adff17989089f1dce790d7e860c91d0b1d0f268fbc8fc661fca0c83ca7d65f5",
-"algorithm": "sha512",
-"filename": "gonk.tar.xz"
-},
-{
 "size": 2116507,
 "digest": "be67a012963a5c162834f9fcb989bcebd2d047dcb4e17ee23031b694dcf7cdfd6d7a6545d7a1f5e7293b6d24415403972f4ea1ab8c6c78fefcabfaf3f6875214",
 "algorithm": "sha512",
 "filename": "download-panda.tar.bz2"
 }
 ]
--- a/b2g/config/unagi/config.json
+++ b/b2g/config/unagi/config.json
@@ -1,20 +1,22 @@
 {
-    "config_version": 1,
+    "config_version": 2,
     "tooltool_manifest": "releng-unagi.tt",
     "mock_target": "mozilla-centos6-i386",
-    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "java-1.6.0-openjdk-devel"],
+    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "java-1.6.0-openjdk-devel", "git"],
     "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
     "build_targets": [],
     "upload_files": [
         "{objdir}/dist/b2g-update/*.mar",
         "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
         "{workdir}/sources.xml"
     ],
+    "b2g_manifest": "unagi.xml",
+    "additional_source_tarballs": ["backup-unagi.tar.bz2"],
     "zip_files": [
         ["{workdir}/out/target/product/unagi/*.img", "out/target/product/unagi/"],
         ["{workdir}/boot.img", "out/target/product/unagi/"],
         "{workdir}/flash.sh",
         "{workdir}/load-config.sh",
         "{workdir}/.config",
         "{workdir}/sources.xml"
     ],
--- a/b2g/config/unagi/releng-unagi.tt
+++ b/b2g/config/unagi/releng-unagi.tt
@@ -1,14 +1,20 @@
 [
 {
-"size": 833424196,
-"digest": "f47e040bac9a0e872dc7289993093c3d1be1befbab2d7caad17645a222147398573aa563f5485ca00ccfbf8c3cefc12d09fe91bf10499baa6d373e80de6bdd70",
+"size": 84128995,
+"digest": "b833dae269b02fec9a0549f467a78717a7b2bf96512caafa3736efe72610b50c5d2073b68afcdb2fea0779e2007e5ec9efc25b14d94f06e194e4ac66d49c676e",
 "algorithm": "sha512",
-"filename": "gonk.tar.xz"
+"filename": "backup-unagi.tar.bz2"
+},
+{
+"size": 1570553,
+"digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7",
+"algorithm": "sha512",
+"filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip"
 },
 {
 "size": 8622080,
 "digest": "36681be904b20a52dbebf38b86466026430d59adb0e72428ae7557a442d037eb378d278aab181b04a753821ff0a99b6228380d59f86ddd5fbf291284fe54932b",
 "algorithm": "sha512",
 "filename": "boot.img"
 }
 ]
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1,12 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+////////////////////////////////////////////////////////////////////////////////
+//// StarUI
 
 var StarUI = {
   _itemId: -1,
   uri: null,
   _batching: false,
 
   _element: function(aID) {
     return document.getElementById(aID);
@@ -213,20 +215,16 @@ var StarUI = {
   },
 
   quitEditMode: function SU_quitEditMode() {
     this._element("editBookmarkPanelContent").hidden = true;
     this._element("editBookmarkPanelBottomButtons").hidden = true;
     gEditItemOverlay.uninitPanel(true);
   },
 
-  editButtonCommand: function SU_editButtonCommand() {
-    this.showEditBookmarkPopup();
-  },
-
   cancelButtonOnCommand: function SU_cancelButtonOnCommand() {
     this._actionOnHide = "cancel";
     this.panel.hidePopup();
   },
 
   removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
     this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
     this._actionOnHide = "remove";
@@ -236,16 +234,19 @@ var StarUI = {
   beginBatch: function SU_beginBatch() {
     if (!this._batching) {
       PlacesUtils.transactionManager.beginBatch(null);
       this._batching = true;
     }
   }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+//// PlacesCommandHook
+
 var PlacesCommandHook = {
   /**
    * Adds a bookmark to the page loaded in the given browser.
    *
    * @param aBrowser
    *        a <browser> element.
    * @param [optional] aParent
    *        The folder in which to create a new bookmark if the page loaded in
@@ -288,38 +289,47 @@ var PlacesCommandHook = {
 
       var parent = aParent != undefined ?
                    aParent : PlacesUtils.unfiledBookmarksFolderId;
       var descAnno = { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description };
       var txn = new PlacesCreateBookmarkTransaction(uri, parent, 
                                                     PlacesUtils.bookmarks.DEFAULT_INDEX,
                                                     title, null, [descAnno]);
       PlacesUtils.transactionManager.doTransaction(txn);
+      itemId = txn.item.id;
       // Set the character-set
       if (charset && !PrivateBrowsingUtils.isWindowPrivate(aBrowser.contentWindow))
         PlacesUtils.setCharsetForURI(uri, charset);
-      itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
     }
 
     // Revert the contents of the location bar
     if (gURLBar)
       gURLBar.handleRevert();
 
-    // dock the panel to the star icon when possible, otherwise dock
-    // it to the content area
-    if (aBrowser.contentWindow == window.content) {
-      var starIcon = aBrowser.ownerDocument.getElementById("star-button");
-      if (starIcon && isElementVisible(starIcon)) {
-        if (aShowEditUI)
-          StarUI.showEditBookmarkPopup(itemId, starIcon, "bottomcenter topright");
-        return;
-      }
+    // If it was not requested to open directly in "edit" mode, we are done.
+    if (!aShowEditUI)
+      return;
+
+    // Try to dock the panel to:
+    // 1. the bookmarks menu button
+    // 2. the page-proxy-favicon
+    // 3. the content area
+    if (BookmarksMenuButton.anchor) {
+      StarUI.showEditBookmarkPopup(itemId, BookmarksMenuButton.anchor,
+                                   "bottomcenter topright");
+      return;
     }
 
-    StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
+    let pageProxyFavicon = document.getElementById("page-proxy-favicon");
+    if (isElementVisible(pageProxyFavicon)) {
+      StarUI.showEditBookmarkPopup(itemId, pageProxyFavicon,
+                                   "bottomcenter topright");
+    } else {
+      StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
+    }
   },
 
   /**
    * Adds a bookmark to the page loaded in the current tab. 
    */
   bookmarkCurrentPage: function PCH_bookmarkCurrentPage(aShowEditUI, aParent) {
     this.bookmarkPage(gBrowser.selectedBrowser, aParent, aShowEditUI);
   },
@@ -455,16 +465,19 @@ var PlacesCommandHook = {
     }
     else {
       organizer.PlacesOrganizer.selectLeftPaneQuery(aLeftPaneRoot);
       organizer.focus();
     }
   }
 };
 
+////////////////////////////////////////////////////////////////////////////////
+//// HistoryMenu
+
 // View for the history menu.
 function HistoryMenu(aPopupShowingEvent) {
   // Workaround for Bug 610187.  The sidebar does not include all the Places
   // views definitions, and we don't need them there.
   // Defining the prototype inheritance in the prototype itself would cause
   // browser.js to halt on "PlacesMenu is not defined" error.
   this.__proto__.__proto__ = PlacesMenu.prototype;
   XPCOMUtils.defineLazyServiceGetter(this, "_ss",
@@ -681,16 +694,19 @@ HistoryMenu.prototype = {
     if (placesNode) {
       if (!PrivateBrowsingUtils.isWindowPrivate(window))
         PlacesUIUtils.markPageAsTyped(placesNode.uri);
       openUILink(placesNode.uri, aEvent, { ignoreAlt: true });
     }
   }
 };
 
+////////////////////////////////////////////////////////////////////////////////
+//// BookmarksEventHandler
+
 /**
  * Functions for handling events in the Bookmarks Toolbar and menu.
  */
 var BookmarksEventHandler = {
   /**
    * Handler for click event for an item in the bookmarks toolbar or menu.
    * Menus and submenus from the folder buttons bubble up to this handler.
    * Left-click is handled in the onCommand function.
@@ -806,16 +822,18 @@ var BookmarksEventHandler = {
     if (!tooltipUrl.hidden)
       tooltipUrl.value = url;
 
     // Show tooltip.
     return true;
   }
 };
 
+////////////////////////////////////////////////////////////////////////////////
+//// PlacesMenuDNDHandler
 
 // Handles special drag and drop functionality for Places menus that are not
 // part of a Places view (e.g. the bookmarks menu in the menubar).
 var PlacesMenuDNDHandler = {
   _springLoadDelay: 350, // milliseconds
   _loadTimer: null,
   _closerTimer: null,
 
@@ -824,66 +842,77 @@ var PlacesMenuDNDHandler = {
    * @param   event
    *          The DragEnter event that spawned the opening. 
    */
   onDragEnter: function PMDH_onDragEnter(event) {
     // Opening menus in a Places popup is handled by the view itself.
     if (!this._isStaticContainer(event.target))
       return;
 
+    let popup = event.target.lastChild;
+    if (this._loadTimer || popup.state === "showing" || popup.state === "open")
+      return;
+
     this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    this._loadTimer.initWithCallback(function() {
-      PlacesMenuDNDHandler._loadTimer = null;
-      event.target.lastChild.setAttribute("autoopened", "true");
-      event.target.lastChild.showPopup(event.target.lastChild);
+    this._loadTimer.initWithCallback(() => {
+      this._loadTimer = null;
+      popup.setAttribute("autoopened", "true");
+      popup.showPopup(popup);
     }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
     event.preventDefault();
     event.stopPropagation();
   },
 
   /**
-   * Handles dragexit on the <menu> element.
+   * Handles dragleave on the <menu> element.
    * @returns true if the element is a container element (menu or 
    *          menu-toolbarbutton), false otherwise.
    */
-  onDragExit: function PMDH_onDragExit(event) {
+  onDragLeave: function PMDH_onDragLeave(event) {
+    // Handle menu-button separate targets.
+    if (event.relatedTarget === event.currentTarget ||
+        event.relatedTarget.parentNode === event.currentTarget)
+      return;
+
     // Closing menus in a Places popup is handled by the view itself.
     if (!this._isStaticContainer(event.target))
       return;
 
+    let popup = event.target.lastChild;
+
     if (this._loadTimer) {
       this._loadTimer.cancel();
       this._loadTimer = null;
     }
     this._closeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     this._closeTimer.initWithCallback(function() {
       this._closeTimer = null;
       let node = PlacesControllerDragHelper.currentDropTarget;
       let inHierarchy = false;
       while (node && !inHierarchy) {
         inHierarchy = node == event.target;
         node = node.parentNode;
       }
-      if (!inHierarchy && event.target.lastChild &&
-          event.target.lastChild.hasAttribute("autoopened")) {
-        event.target.lastChild.removeAttribute("autoopened");
-        event.target.lastChild.hidePopup();
+      if (!inHierarchy && popup && popup.hasAttribute("autoopened")) {
+        popup.removeAttribute("autoopened");
+        popup.hidePopup();
       }
     }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   /**
    * Determines if a XUL element represents a static container.
    * @returns true if the element is a container element (menu or 
    *`         menu-toolbarbutton), false otherwise.
    */
   _isStaticContainer: function PMDH__isContainer(node) {
     let isMenu = node.localName == "menu" ||
                  (node.localName == "toolbarbutton" &&
-                  node.getAttribute("type") == "menu");
+                  (node.getAttribute("type") == "menu" ||
+                   node.getAttribute("type") == "menu-button"));
     let isStatic = !("_placesNode" in node) && node.lastChild &&
                    node.lastChild.hasAttribute("placespopup") &&
                    !node.parentNode.hasAttribute("placespopup");
     return isMenu && isStatic;
   },
 
   /**
    * Called when the user drags over the <menu> element.
@@ -910,189 +939,23 @@ var PlacesMenuDNDHandler = {
     let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
                                 PlacesUtils.bookmarks.DEFAULT_INDEX,
                                 Ci.nsITreeView.DROP_ON);
     PlacesControllerDragHelper.onDrop(ip, event.dataTransfer);
     event.stopPropagation();
   }
 };
 
-
-var PlacesStarButton = {
-  _hasBookmarksObserver: false,
-  uninit: function PSB_uninit()
-  {
-    if (this._hasBookmarksObserver) {
-      PlacesUtils.removeLazyBookmarkObserver(this);
-    }
-    if (this._pendingStmt) {
-      this._pendingStmt.cancel();
-      delete this._pendingStmt;
-    }
-  },
-
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsINavBookmarkObserver
-  ]),
-
-  get _starredTooltip()
-  {
-    delete this._starredTooltip;
-    return this._starredTooltip =
-      gNavigatorBundle.getString("starButtonOn.tooltip");
-  },
-  get _unstarredTooltip()
-  {
-    delete this._unstarredTooltip;
-    return this._unstarredTooltip =
-      gNavigatorBundle.getString("starButtonOff.tooltip");
-  },
-
-  updateState: function PSB_updateState()
-  {
-    this._starIcon = document.getElementById("star-button");
-    if (!this._starIcon || (this._uri && gBrowser.currentURI.equals(this._uri))) {
-      return;
-    }
-
-    // Reset tracked values.
-    this._uri = gBrowser.currentURI;
-    this._itemIds = [];
-
-    if (this._pendingStmt) {
-      this._pendingStmt.cancel();
-      delete this._pendingStmt;
-    }
-
-    // We can load about:blank before the actual page, but there is no point in handling that page.
-    if (isBlankPageURL(this._uri.spec)) {
-      return;
-    }
-
-    this._pendingStmt = PlacesUtils.asyncGetBookmarkIds(this._uri, function (aItemIds, aURI) {
-      // Safety check that the bookmarked URI equals the tracked one.
-      if (!aURI.equals(this._uri)) {
-        Components.utils.reportError("PlacesStarButton did not receive current URI");
-        return;
-      }
-
-      // It's possible that onItemAdded gets called before the async statement
-      // calls back.  For such an edge case, retain all unique entries from both
-      // arrays.
-      this._itemIds = this._itemIds.filter(
-        function (id) aItemIds.indexOf(id) == -1
-      ).concat(aItemIds);
-      this._updateStateInternal();
-
-      // Start observing bookmarks if needed.
-      if (!this._hasBookmarksObserver) {
-        try {
-          PlacesUtils.addLazyBookmarkObserver(this);
-          this._hasBookmarksObserver = true;
-        } catch(ex) {
-          Components.utils.reportError("PlacesStarButton failed adding a bookmarks observer: " + ex);
-        }
-      }
-
-      delete this._pendingStmt;
-    }, this);
-  },
-
-  _updateStateInternal: function PSB__updateStateInternal()
-  {
-    if (!this._starIcon) {
-      return;
-    }
+////////////////////////////////////////////////////////////////////////////////
+//// PlacesToolbarHelper
 
-    if (this._itemIds.length > 0) {
-      this._starIcon.setAttribute("starred", "true");
-      this._starIcon.setAttribute("tooltiptext", this._starredTooltip);
-    }
-    else {
-      this._starIcon.removeAttribute("starred");
-      this._starIcon.setAttribute("tooltiptext", this._unstarredTooltip);
-    }
-  },
-
-  onClick: function PSB_onClick(aEvent)
-  {
-    // Ignore clicks on the star while we update its state.
-    if (aEvent.button == 0 && !this._pendingStmt) {
-      PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
-    }
-    // Don't bubble to the textbox, to avoid unwanted selection of the address.
-    aEvent.stopPropagation();
-  },
-
-  // nsINavBookmarkObserver
-  onItemAdded:
-  function PSB_onItemAdded(aItemId, aFolder, aIndex, aItemType, aURI)
-  {
-    if (!this._starIcon) {
-      return;
-    }
-
-    if (aURI && aURI.equals(this._uri)) {
-      // If a new bookmark has been added to the tracked uri, register it.
-      if (this._itemIds.indexOf(aItemId) == -1) {
-        this._itemIds.push(aItemId);
-        this._updateStateInternal();
-      }
-    }
-  },
-
-  onItemRemoved:
-  function PSB_onItemRemoved(aItemId, aFolder, aIndex, aItemType)
-  {
-    if (!this._starIcon) {
-      return;
-    }
-
-    let index = this._itemIds.indexOf(aItemId);
-    // If one of the tracked bookmarks has been removed, unregister it.
-    if (index != -1) {
-      this._itemIds.splice(index, 1);
-      this._updateStateInternal();
-    }
-  },
-
-  onItemChanged:
-  function PSB_onItemChanged(aItemId, aProperty, aIsAnnotationProperty,
-                             aNewValue, aLastModified, aItemType)
-  {
-    if (!this._starIcon) {
-      return;
-    }
-
-    if (aProperty == "uri") {
-      let index = this._itemIds.indexOf(aItemId);
-      // If the changed bookmark was tracked, check if it is now pointing to
-      // a different uri and unregister it.
-      if (index != -1 && aNewValue != this._uri.spec) {
-        this._itemIds.splice(index, 1);
-        this._updateStateInternal();
-      }
-      // If another bookmark is now pointing to the tracked uri, register it.
-      else if (index == -1 && aNewValue == this._uri.spec) {
-        this._itemIds.push(aItemId);
-        this._updateStateInternal();
-      }
-    }
-  },
-
-  onBeginUpdateBatch: function () {},
-  onEndUpdateBatch: function () {},
-  onItemVisited: function () {},
-  onItemMoved: function () {}
-};
-
-
-// This object handles the initialization and uninitialization of the bookmarks
-// toolbar.  updateState is called when the browser window is opened and
-// after closing the toolbar customization dialog.
+/**
+ * This object handles the initialization and uninitialization of the bookmarks
+ * toolbar.
+ */
 let PlacesToolbarHelper = {
   _place: "place:folder=TOOLBAR",
 
   get _viewElt() {
     return document.getElementById("PlacesToolbar");
   },
 
   init: function PTH_init() {
@@ -1122,160 +985,319 @@ let PlacesToolbarHelper = {
   },
 
   customizeDone: function PTH_customizeDone() {
     this._isCustomizing = false;
     this.init();
   }
 };
 
+////////////////////////////////////////////////////////////////////////////////
+//// BookmarksMenuButton
 
-// Handles the bookmarks menu button shown when the main menubar is hidden.
+/**
+ * Handles the bookmarks menu-button in the toolbar.
+ */
 let BookmarksMenuButton = {
   get button() {
-    return document.getElementById("bookmarks-menu-button");
+    if (!this._button) {
+      this._button = document.getElementById("bookmarks-menu-button");
+    }
+    return this._button;
   },
 
-  get buttonContainer() {
-    return document.getElementById("bookmarks-menu-button-container");
+  get star() {
+    if (!this._star && this.button) {
+      this._star = document.getAnonymousElementByAttribute(this.button,
+                                                           "anonid",
+                                                           "button");
+    }
+    return this._star;
+  },
+
+  get anchor() {
+    if (!this._anchor && this.star && isElementVisible(this.star)) {
+      // Anchor to the icon, so the panel looks more natural.
+      this._anchor = document.getAnonymousElementByAttribute(this.star,
+                                                             "class",
+                                                             "toolbarbutton-icon");
+    }
+    return this._anchor;
   },
 
-  get personalToolbar() {
-    delete this.personalToolbar;
-    return this.personalToolbar = document.getElementById("PersonalToolbar");
+  STATUS_UPDATING: -1,
+  STATUS_UNSTARRED: 0,
+  STATUS_STARRED: 1,
+  get status() {
+    if (this._pendingStmt)
+      return this.STATUS_UPDATING;
+    return this.button &&
+           this.button.hasAttribute("starred") ? this.STATUS_STARRED
+                                               : this.STATUS_UNSTARRED;
   },
 
-  get bookmarksToolbarItem() {
-    return document.getElementById("personal-bookmarks");
+  get _starredTooltip()
+  {
+    delete this._starredTooltip;
+    return this._starredTooltip =
+      gNavigatorBundle.getString("starButtonOn.tooltip");
   },
 
-  init: function BMB_init() {
-    this.updatePosition();
-
-    // Any other stuff that does not regard the button itself should be
-    // handled in the onPopupShowing handler, so it does not hit Ts.
+  get _unstarredTooltip()
+  {
+    delete this._unstarredTooltip;
+    return this._unstarredTooltip =
+      gNavigatorBundle.getString("starButtonOff.tooltip");
   },
 
-  _popupNeedsUpdate: {},
+  /**
+   * The popup contents must be updated when the user customizes the UI, or
+   * changes the personal toolbar collapsed status.  In such a case, any needed
+   * change should be handled in the popupshowing helper, for performance
+   * reasons.
+   */
+  _popupNeedsUpdate: true,
+  onToolbarVisibilityChange: function BMB_onToolbarVisibilityChange() {
+    this._popupNeedsUpdate = true;
+  },
+
   onPopupShowing: function BMB_onPopupShowing(event) {
     // Don't handle events for submenus.
     if (event.target != event.currentTarget)
       return;
 
-    let popup = event.target;
-    let needsUpdate = this._popupNeedsUpdate[popup.id];
-
-    // Check if popup contents need to be updated.  Note that if needsUpdate is
-    // undefined we have never seen the popup, thus it should be updated.
-    if (needsUpdate === false)
+    if (!this._popupNeedsUpdate)
       return;
-    this._popupNeedsUpdate[popup.id] = false;
+    this._popupNeedsUpdate = false;
 
-    function getPlacesAnonymousElement(aAnonId)
-      document.getAnonymousElementByAttribute(popup.parentNode,
-                                              "placesanonid",
-                                              aAnonId);
+    let popup = event.target;
+    let getPlacesAnonymousElement =
+      aAnonId => document.getAnonymousElementByAttribute(popup.parentNode,
+                                                         "placesanonid",
+                                                         aAnonId);
 
     let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar");
     if (viewToolbarMenuitem) {
       // Update View bookmarks toolbar checkbox menuitem.
-      viewToolbarMenuitem.setAttribute("checked",
-                                       !this.personalToolbar.collapsed);
+      let personalToolbar = document.getElementById("PersonalToolbar");
+      viewToolbarMenuitem.setAttribute("checked", !personalToolbar.collapsed);
     }
 
     let toolbarMenuitem = getPlacesAnonymousElement("toolbar-autohide");
     if (toolbarMenuitem) {
       // If bookmarks items are visible, hide Bookmarks Toolbar menu and the
       // separator after it.
       toolbarMenuitem.collapsed = toolbarMenuitem.nextSibling.collapsed =
-        isElementVisible(this.bookmarksToolbarItem);
+        isElementVisible(document.getElementById("personal-bookmarks"));
     }
   },
 
-  updatePosition: function BMB_updatePosition() {
-    // Popups will have to be updated when the user customizes the UI, or
-    // changes personal toolbar collapsed status.  Both of those location call
-    // updatePosition(), so this is the only point asking for popup updates.
-    for (let popupId in this._popupNeedsUpdate) {
-      this._popupNeedsUpdate[popupId] = true;
+  /**
+   * Handles star styling based on page proxy state changes.
+   */
+  onPageProxyStateChanged: function BMB_onPageProxyStateChanged(aState) {
+    if (!this.star) {
+      return;
     }
 
-    let button = this.button;
-    if (!button)
-      return;
-
-    // If the toolbar containing bookmarks is visible, we want to move the
-    // button to bookmarksToolbarItem.
-    let bookmarksToolbarItem = this.bookmarksToolbarItem;
-    let bookmarksOnVisibleToolbar = bookmarksToolbarItem &&
-                                    !bookmarksToolbarItem.parentNode.collapsed &&
-                                    bookmarksToolbarItem.parentNode.getAttribute("autohide") != "true";
-
-    // If the container has been moved by the user to the toolbar containing
-    // bookmarks, we want to preserve the desired position.
-    let container = this.buttonContainer;
-    let containerNearBookmarks = container && bookmarksToolbarItem &&
-                                 container.parentNode == bookmarksToolbarItem.parentNode;
-
-    if (bookmarksOnVisibleToolbar && !containerNearBookmarks) {
-      if (button.parentNode != bookmarksToolbarItem) {
-        this._uninitView();
-        bookmarksToolbarItem.appendChild(button);
-      }
+    if (aState == "invalid") {
+      this.star.setAttribute("disabled", "true");
+      this.button.removeAttribute("starred");
     }
     else {
-      if (container && button.parentNode != container) {
-        this._uninitView();
-        container.appendChild(button);
-      }
+      this.star.removeAttribute("disabled");
     }
     this._updateStyle();
   },
 
   _updateStyle: function BMB__updateStyle() {
-    let button = this.button;
-    if (!button)
+    if (!this.star) {
       return;
+    }
 
-    let container = this.buttonContainer;
-    let containerOnPersonalToolbar = container &&
-                                     (container.parentNode == this.personalToolbar ||
-                                      container.parentNode.parentNode == this.personalToolbar);
+    let personalToolbar = document.getElementById("PersonalToolbar");
+    let onPersonalToolbar = this.button.parentNode == personalToolbar ||
+                            this.button.parentNode.parentNode == personalToolbar;
 
-    if (button.parentNode == this.bookmarksToolbarItem ||
-        containerOnPersonalToolbar) {
-      button.classList.add("bookmark-item");
-      button.classList.remove("toolbarbutton-1");
+    if (onPersonalToolbar) {
+      this.button.classList.add("bookmark-item");
+      this.button.classList.remove("toolbarbutton-1");
     }
     else {
-      button.classList.remove("bookmark-item");
-      button.classList.add("toolbarbutton-1");
+      this.button.classList.remove("bookmark-item");
+      this.button.classList.add("toolbarbutton-1");
     }
   },
 
   _uninitView: function BMB__uninitView() {
     // When an element with a placesView attached is removed and re-inserted,
     // XBL reapplies the binding causing any kind of issues and possible leaks,
     // so kill current view and let popupshowing generate a new one.
-    let button = this.button;
-    if (button && button._placesView)
-      button._placesView.uninit();
+    if (this.button && this.button._placesView) {
+      this.button._placesView.uninit();
+    }
   },
 
   customizeStart: function BMB_customizeStart() {
     this._uninitView();
-    let button = this.button;
-    let container = this.buttonContainer;
-    if (button && container && button.parentNode != container) {
-      // Move button back to the container, so user can move or remove it.
-      container.appendChild(button);
-      this._updateStyle();
-    }
   },
 
   customizeChange: function BMB_customizeChange() {
     this._updateStyle();
   },
 
   customizeDone: function BMB_customizeDone() {
-    this.updatePosition();
-  }
+    delete this._button;
+    delete this._star;
+    delete this._anchor;
+    this.onToolbarVisibilityChange();
+    this._updateStyle();
+  },
+
+  _hasBookmarksObserver: false,
+  uninit: function BMB_uninit() {
+    this._uninitView();
+
+    if (this._hasBookmarksObserver) {
+      PlacesUtils.removeLazyBookmarkObserver(this);
+    }
+
+    if (this._pendingStmt) {
+      this._pendingStmt.cancel();
+      delete this._pendingStmt;
+    }
+  },
+
+  updateStarState: function BMB_updateStarState() {
+    if (!this.button || (this._uri && gBrowser.currentURI.equals(this._uri))) {
+      return;
+    }
+
+    // Reset tracked values.
+    this._uri = gBrowser.currentURI;
+    this._itemIds = [];
+
+    if (this._pendingStmt) {
+      this._pendingStmt.cancel();
+      delete this._pendingStmt;
+    }
+
+    // We can load about:blank before the actual page, but there is no point in handling that page.
+    if (isBlankPageURL(this._uri.spec)) {
+      return;
+    }
+
+    this._pendingStmt = PlacesUtils.asyncGetBookmarkIds(this._uri, function (aItemIds, aURI) {
+      // Safety check that the bookmarked URI equals the tracked one.
+      if (!aURI.equals(this._uri)) {
+        Components.utils.reportError("BookmarksMenuButton did not receive current URI");
+        return;
+      }
+
+      // It's possible that onItemAdded gets called before the async statement
+      // calls back.  For such an edge case, retain all unique entries from both
+      // arrays.
+      this._itemIds = this._itemIds.filter(
+        function (id) aItemIds.indexOf(id) == -1
+      ).concat(aItemIds);
+
+      this._updateStar();
+
+      // Start observing bookmarks if needed.
+      if (!this._hasBookmarksObserver) {
+        try {
+          PlacesUtils.addLazyBookmarkObserver(this);
+          this._hasBookmarksObserver = true;
+        } catch(ex) {
+          Components.utils.reportError("BookmarksMenuButton failed adding a bookmarks observer: " + ex);
+        }
+      }
+
+      delete this._pendingStmt;
+    }, this);
+  },
+
+  _updateStar: function BMB__updateStar() {
+    if (!this.button) {
+      return;
+    }
+
+    if (this._itemIds.length > 0) {
+      this.button.setAttribute("starred", "true");
+      this.button.setAttribute("tooltiptext", this._starredTooltip);
+    }
+    else {
+      this.button.removeAttribute("starred");
+      this.button.setAttribute("tooltiptext", this._unstarredTooltip);
+    }
+  },
+
+  onCommand: function BMB_onCommand(aEvent) {
+    if (aEvent.target != aEvent.currentTarget) {
+      return;
+    }
+    // Ignore clicks on the star if we are updating its state.
+    if (!this._pendingStmt) {
+      PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
+    }
+  },
+
+  // nsINavBookmarkObserver
+  onItemAdded: function BMB_onItemAdded(aItemId, aParentId, aIndex, aItemType,
+                                        aURI) {
+    if (!this.button) {
+      return;
+    }
+
+    if (aURI && aURI.equals(this._uri)) {
+      // If a new bookmark has been added to the tracked uri, register it.
+      if (this._itemIds.indexOf(aItemId) == -1) {
+        this._itemIds.push(aItemId);
+        this._updateStar();
+      }
+    }
+  },
+
+  onItemRemoved: function BMB_onItemRemoved(aItemId) {
+    if (!this.button) {
+      return;
+    }
+
+    let index = this._itemIds.indexOf(aItemId);
+    // If one of the tracked bookmarks has been removed, unregister it.
+    if (index != -1) {
+      this._itemIds.splice(index, 1);
+      this._updateStar();
+    }
+  },
+
+  onItemChanged: function BMB_onItemChanged(aItemId, aProperty,
+                                            aIsAnnotationProperty, aNewValue) {
+    if (!this.button) {
+      return;
+    }
+
+    if (aProperty == "uri") {
+      let index = this._itemIds.indexOf(aItemId);
+      // If the changed bookmark was tracked, check if it is now pointing to
+      // a different uri and unregister it.
+      if (index != -1 && aNewValue != this._uri.spec) {
+        this._itemIds.splice(index, 1);
+        this._updateStar();
+      }
+      // If another bookmark is now pointing to the tracked uri, register it.
+      else if (index == -1 && aNewValue == this._uri.spec) {
+        this._itemIds.push(aItemId);
+        this._updateStar();
+      }
+    }
+  },
+
+  onBeginUpdateBatch: function () {},
+  onEndUpdateBatch: function () {},
+  onBeforeItemRemoved: function () {},
+  onItemVisited: function () {},
+  onItemMoved: function () {},
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsINavBookmarkObserver
+  ]),
 };
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -299,29 +299,30 @@ panel[noactions] > richlistbox > richlis
 .unified-nav-current {
   font-weight: bold;
 }
 
 toolbarbutton.bookmark-item {
   max-width: 13em;
 }
 
-%ifdef MENUBAR_CAN_AUTOHIDE
-#toolbar-menubar:not([autohide="true"]) ~ #nav-bar > #bookmarks-menu-button-container,
-#toolbar-menubar:not([autohide="true"]) ~ toolbar > #personal-bookmarks > #bookmarks-menu-button,
-#toolbar-menubar:not([autohide="true"]) > #personal-bookmarks > #bookmarks-menu-button {
-  display: none;
-}
-%endif
-
 #editBMPanel_tagsSelector {
   /* override default listbox width from xul.css */
   width: auto;
 }
 
+/* The star doesn't make sense as text */
+toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+  display: -moz-box !important;
+}
+toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-text,
+toolbar[mode="full"] #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-text {
+  display: none;
+}
+
 menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
   display: none;
 }
 
 menuitem.spell-suggestion {
   font-weight: bold;
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -941,17 +941,16 @@ var gBrowserInit = {
 
 #ifdef MENUBAR_CAN_AUTOHIDE
     updateAppButtonDisplay();
 #endif
 
     // Misc. inits.
     CombinedStopReload.init();
     TabsOnTop.init();
-    BookmarksMenuButton.init();
     gPrivateBrowsingUI.init();
     TabsInTitlebar.init();
     retrieveToolbarIconsizesFromTheme();
 
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this, uriToLoad, mustLoadSidebar);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
@@ -1312,17 +1311,17 @@ var gBrowserInit = {
     Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
 
     try {
       gBrowser.removeProgressListener(window.XULBrowserWindow);
       gBrowser.removeTabsProgressListener(window.TabsProgressListener);
     } catch (ex) {
     }
 
-    PlacesStarButton.uninit();
+    BookmarksMenuButton.uninit();
 
     TabsOnTop.uninit();
 
     TabsInTitlebar.uninit();
 
     var enumerator = Services.wm.getEnumerator(null);
     enumerator.getNext();
     if (!enumerator.hasMoreElements()) {
@@ -2230,16 +2229,18 @@ function setUrlAndSearchBarWidthForCondi
 function UpdatePageProxyState()
 {
   if (gURLBar && gURLBar.value != gLastValidURLStr)
     SetPageProxyState("invalid");
 }
 
 function SetPageProxyState(aState)
 {
+  BookmarksMenuButton.onPageProxyStateChanged(aState);
+
   if (!gURLBar)
     return;
 
   if (!gProxyFavIcon)
     gProxyFavIcon = document.getElementById("page-proxy-favicon");
 
   gURLBar.setAttribute("pageproxystate", aState);
   gProxyFavIcon.setAttribute("pageproxystate", aState);
@@ -3401,17 +3402,17 @@ function BrowserToolboxCustomizeDone(aTo
   CombinedStopReload.init();
   UpdateUrlbarSearchSplitterState();
   setUrlAndSearchBarWidthForConditionalForwardButton();
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
-    PlacesStarButton.updateState();
+    BookmarksMenuButton.updateStarState();
     SocialShareButton.updateShareState();
   }
 
   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)
@@ -3875,17 +3876,17 @@ var XULBrowserWindow = {
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         // Update starring UI
-        PlacesStarButton.updateState();
+        BookmarksMenuButton.updateStarState();
         SocialShareButton.updateShareState();
       }
 
       // Show or hide browser chrome based on the whitelist
       if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
       } else {
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
@@ -4493,17 +4494,17 @@ function onViewToolbarCommand(aEvent) {
 function setToolbarVisibility(toolbar, isVisible) {
   var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
                         "autohide" : "collapsed";
 
   toolbar.setAttribute(hidingAttribute, !isVisible);
   document.persist(toolbar.id, hidingAttribute);
 
   PlacesToolbarHelper.init();
-  BookmarksMenuButton.updatePosition();
+  BookmarksMenuButton.onToolbarVisibilityChange();
   gBrowser.updateWindowResizers();
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -518,17 +518,17 @@
       <hbox class="titlebar-placeholder" type="caption-buttons" ordinal="1000"/>
 #endif
     </toolbar>
 
     <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
              toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
              fullscreentoolbar="true" mode="icons" customizable="true"
              iconsize="large"
-             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,downloads-button,home-button,bookmarks-menu-button-container,window-controls"
+             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,window-controls"
              context="toolbar-context-menu">
 
       <toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
                    context="backForwardMenu" removable="true"
                    forwarddisabled="true"
                    title="&backForwardItem.title;">
         <toolbarbutton id="back-button" class="toolbarbutton-1"
                        label="&backCmd.label;"
@@ -615,19 +615,16 @@
                    onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
 
             <label id="share-button-status" collapsed="true" role="status"/>
             <image id="share-button"
                    class="urlbar-icon"
                    hidden="true"
                    onclick="SocialShareButton.onClick(event);"/>
 
-            <image id="star-button"
-                   class="urlbar-icon"
-                   onclick="PlacesStarButton.onClick(event);"/>
             <image id="go-button"
                    class="urlbar-icon"
                    tooltiptext="&goEndCap.tooltip;"
                    onclick="gURLBar.handleCommand(event);"/>
           </hbox>
           <toolbarbutton id="urlbar-go-button"
                          class="chromeclass-toolbar-additional"
                          onclick="gURLBar.handleCommand(event);"
@@ -668,16 +665,92 @@
                      orient="horizontal"
                      label="&webrtcIndicatorButton.label;"
                      tooltiptext="&webrtcIndicatorButton.tooltip;">
         <menupopup onpopupshowing="WebrtcIndicator.fillPopup(this);"
                    onpopuphiding="WebrtcIndicator.clearPopup(this);"
                    oncommand="WebrtcIndicator.menuCommand(event.target);"/>
       </toolbarbutton>
 
+      <toolbarbutton id="bookmarks-menu-button"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     persist="class"
+                     removable="true"
+                     type="menu-button"
+                     label="&bookmarksMenuButton.label;"
+                     tooltiptext="&bookmarksMenuButton.tooltip;"
+                     ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
+                     ondragover="PlacesMenuDNDHandler.onDragOver(event);"
+                     ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
+                     ondrop="PlacesMenuDNDHandler.onDrop(event);"
+                     oncommand="BookmarksMenuButton.onCommand(event);">
+        <menupopup id="BMB_bookmarksPopup"
+                   placespopup="true"
+                   context="placesContext"
+                   openInTabs="children"
+                   oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+                   onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
+                   onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
+                                   if (!this.parentNode._placesView)
+                                     new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
+                   tooltip="bhTooltip" popupsinherittooltip="true">
+          <menuitem id="BMB_viewBookmarksToolbar"
+                    placesanonid="view-toolbar"
+                    toolbarId="PersonalToolbar"
+                    type="checkbox"
+                    oncommand="onViewToolbarCommand(event)"
+                    label="&viewBookmarksToolbar.label;"/>
+          <menuseparator/>
+          <menuitem id="BMB_bookmarksShowAll"
+                    label="&showAllBookmarks2.label;"
+                    command="Browser:ShowAllBookmarks"
+                    key="manBookmarkKb"/>
+          <menuseparator/>
+          <menuitem id="BMB_subscribeToPageMenuitem"
+#ifndef XP_MACOSX
+                    class="menuitem-iconic"
+#endif
+                    label="&subscribeToPageMenuitem.label;"
+                    oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                    onclick="checkForMiddleClick(this, event);"
+                    observes="singleFeedMenuitemState"/>
+          <menu id="BMB_subscribeToPageMenupopup"
+#ifndef XP_MACOSX
+                class="menu-iconic"
+#endif
+                label="&subscribeToPageMenupopup.label;"
+                observes="multipleFeedsMenuState">
+            <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
+                       onpopupshowing="return FeedHandler.buildFeedList(event.target);"
+                       oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                       onclick="checkForMiddleClick(this, event);"/>
+          </menu>
+          <menuseparator/>
+          <menu id="BMB_bookmarksToolbar"
+                placesanonid="toolbar-autohide"
+                class="menu-iconic bookmark-item"
+                label="&personalbarCmd.label;"
+                container="true">
+            <menupopup id="BMB_bookmarksToolbarPopup"
+                       placespopup="true"
+                       context="placesContext"
+                       onpopupshowing="if (!this.parentNode._placesView)
+                                         new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+          </menu>
+          <menuseparator/>
+          <!-- Bookmarks menu items -->
+          <menuseparator builder="end"
+                         class="hide-if-empty-places-result"/>
+          <menuitem id="BMB_unsortedBookmarks"
+                    label="&bookmarksMenuButton.unsorted.label;"
+                    oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
+                    class="menuitem-iconic"/>
+        </menupopup>
+      </toolbarbutton>
+
       <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                      persist="class" removable="true"
                      label="&homeButton.label;"
                      ondragover="homeButtonObserver.onDragOver(event)"
                      ondragenter="homeButtonObserver.onDragOver(event)"
                      ondrop="homeButtonObserver.onDrop(event)"
                      ondragexit="homeButtonObserver.onDragExit(event)"
                      onclick="BrowserGoHome(event);"
@@ -726,101 +799,16 @@
                       label="&social.addons.label;"/>
             <menuitem label="&social.learnMore.label;"
                       accesskey="&social.learnMore.accesskey;"
                       oncommand="SocialUI.showLearnMore();"/>
           </menupopup>
         </toolbarbutton>
       </toolbaritem>
 
-      <toolbaritem id="bookmarks-menu-button-container"
-                   class="chromeclass-toolbar-additional"
-                   removable="true"
-                   title="&bookmarksMenuButton.label;">
-        <toolbarbutton id="bookmarks-menu-button"
-                       type="menu"
-                       class="toolbarbutton-1"
-                       label="&bookmarksMenuButton.label;"
-                       tooltiptext="&bookmarksMenuButton.tooltip;"
-                       ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
-                       ondragover="PlacesMenuDNDHandler.onDragOver(event);"
-                       ondragexit="PlacesMenuDNDHandler.onDragExit(event);"
-                       ondrop="PlacesMenuDNDHandler.onDrop(event);">
-          <menupopup id="BMB_bookmarksPopup"
-                     placespopup="true"
-                     context="placesContext"
-                     openInTabs="children"
-                     oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
-                     onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
-                     onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
-                                     if (!this.parentNode._placesView)
-                                       new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
-                     tooltip="bhTooltip" popupsinherittooltip="true">
-            <menuitem id="BMB_viewBookmarksToolbar"
-                      placesanonid="view-toolbar"
-                      toolbarId="PersonalToolbar"
-                      type="checkbox"
-                      oncommand="onViewToolbarCommand(event)"
-                      label="&viewBookmarksToolbar.label;"/>
-            <menuseparator/>
-            <menuitem id="BMB_bookmarksShowAll"
-                      label="&showAllBookmarks2.label;"
-                      command="Browser:ShowAllBookmarks"
-                      key="manBookmarkKb"/>
-            <menuseparator/>
-            <menuitem id="BMB_bookmarkThisPage"
-#ifndef XP_MACOSX
-                      class="menuitem-iconic"
-#endif
-                      label="&bookmarkThisPageCmd.label;"
-                      command="Browser:AddBookmarkAs"
-                      key="addBookmarkAsKb"/>
-            <menuitem id="BMB_subscribeToPageMenuitem"
-#ifndef XP_MACOSX
-                      class="menuitem-iconic"
-#endif
-                      label="&subscribeToPageMenuitem.label;"
-                      oncommand="return FeedHandler.subscribeToFeed(null, event);"
-                      onclick="checkForMiddleClick(this, event);"
-                      observes="singleFeedMenuitemState"/>
-            <menu id="BMB_subscribeToPageMenupopup"
-#ifndef XP_MACOSX
-                  class="menu-iconic"
-#endif
-                  label="&subscribeToPageMenupopup.label;"
-                  observes="multipleFeedsMenuState">
-              <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
-                         onpopupshowing="return FeedHandler.buildFeedList(event.target);"
-                         oncommand="return FeedHandler.subscribeToFeed(null, event);"
-                         onclick="checkForMiddleClick(this, event);"/>
-            </menu>
-            <menuseparator/>
-            <menu id="BMB_bookmarksToolbar"
-                  placesanonid="toolbar-autohide"
-                  class="menu-iconic bookmark-item"
-                  label="&personalbarCmd.label;"
-                  container="true">
-              <menupopup id="BMB_bookmarksToolbarPopup"
-                         placespopup="true"
-                         context="placesContext"
-                         onpopupshowing="if (!this.parentNode._placesView)
-                                           new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
-            </menu>
-            <menuseparator/>
-            <!-- Bookmarks menu items -->
-            <menuseparator builder="end"
-                           class="hide-if-empty-places-result"/>
-            <menuitem id="BMB_unsortedBookmarks"
-                      label="&bookmarksMenuButton.unsorted.label;"
-                      oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
-                      class="menuitem-iconic"/>
-          </menupopup>
-        </toolbarbutton>
-      </toolbaritem>
-
       <hbox id="window-controls" hidden="true" pack="end">
         <toolbarbutton id="minimize-button"
                        tooltiptext="&fullScreenMinimize.tooltip;"
                        oncommand="window.minimize();"/>
 
         <toolbarbutton id="restore-button"
                        tooltiptext="&fullScreenRestore.tooltip;"
                        oncommand="BrowserFullScreen();"/>
--- a/browser/base/content/test/browser_bug432599.js
+++ b/browser/base/content/test/browser_bug432599.js
@@ -12,25 +12,25 @@ function invokeUsingCtrlD(phase) {
     EventUtils.synthesizeKey("d", { accelKey: true });
     break;
   }
 }
 
 function invokeUsingStarButton(phase) {
   switch (phase) {
   case 1:
-    EventUtils.sendMouseEvent({ type: "click" }, "star-button");
+     EventUtils.synthesizeMouseAtCenter(BookmarksMenuButton.star, {});
     break;
   case 2:
   case 4:
     EventUtils.synthesizeKey("VK_ESCAPE", {});
     break;
   case 3:
-    EventUtils.synthesizeMouse(document.getElementById("star-button"),
-                               1, 1, { clickCount: 2 });
+     EventUtils.synthesizeMouseAtCenter(BookmarksMenuButton.star,
+                                        { clickCount: 2 });
     break;
   }
 }
 
 var testURL = "data:text/plain,Content";
 var bookmarkId;
 
 function add_bookmark(aURI, aTitle) {
@@ -39,36 +39,38 @@ function add_bookmark(aURI, aTitle) {
                                               aTitle);
 }
 
 // test bug 432599
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
-  gBrowser.selectedBrowser.addEventListener("load", function () {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     waitForStarChange(false, initTest);
   }, true);
 
   content.location = testURL;
 }
 
 function initTest() {
   // First, bookmark the page.
   bookmarkId = add_bookmark(makeURI(testURL), "Bug 432599 Test");
 
   checkBookmarksPanel(invokers[currentInvoker], 1);
 }
 
 function waitForStarChange(aValue, aCallback) {
-  let starButton = document.getElementById("star-button");
-  if (PlacesStarButton._pendingStmt || starButton.hasAttribute("starred") != aValue) {
+  let expectedStatus = aValue ? BookmarksMenuButton.STATUS_STARRED
+                              : BookmarksMenuButton.STATUS_UNSTARRED;
+  if (BookmarksMenuButton.status == BookmarksMenuButton.STATUS_UPDATING ||
+      BookmarksMenuButton.status != expectedStatus) {
     info("Waiting for star button change.");
-    setTimeout(arguments.callee, 50, aValue, aCallback);
+    setTimeout(waitForStarChange, 50, aValue, aCallback);
     return;
   }
   aCallback();
 }
 
 let invokers = [invokeUsingStarButton, invokeUsingCtrlD];
 let currentInvoker = 0;
 
--- a/browser/base/content/test/browser_bug581253.js
+++ b/browser/base/content/test/browser_bug581253.js
@@ -1,15 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 let testURL = "data:text/plain,nothing but plain text";
 let testTag = "581253_tag";
-let starButton = document.getElementById("star-button");
 let timerID = -1;
 
 function test() {
   registerCleanupFunction(function() {
     PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
     if (timerID > 0) {
       clearTimeout(timerID);
     }
@@ -30,36 +29,37 @@ function test() {
     ok(PlacesUtils.bookmarks.isBookmarked(uri), "the test url is bookmarked");
     waitForStarChange(true, onStarred);
   }), true);
 
   content.location = testURL;
 }
 
 function waitForStarChange(aValue, aCallback) {
-  if (PlacesStarButton._pendingStmt || starButton.hasAttribute("starred") != aValue) {
+  let expectedStatus = aValue ? BookmarksMenuButton.STATUS_STARRED
+                              : BookmarksMenuButton.STATUS_UNSTARRED;
+  if (BookmarksMenuButton.status == BookmarksMenuButton.STATUS_UPDATING ||
+      BookmarksMenuButton.status != expectedStatus) {
     info("Waiting for star button change.");
-    info("pendingStmt: " + (!!PlacesStarButton._pendingStmt) + ", hasAttribute: " + starButton.hasAttribute("starred") + ", tracked uri: " + PlacesStarButton._uri.spec);
-    timerID = setTimeout(arguments.callee, 50, aValue, aCallback);
+    setTimeout(waitForStarChange, 50, aValue, aCallback);
     return;
   }
-  timerID = -1;
   aCallback();
 }
 
 function onStarred() {
-  ok(starButton.getAttribute("starred") == "true",
+  is(BookmarksMenuButton.status, BookmarksMenuButton.STATUS_STARRED,
      "star button indicates that the page is bookmarked");
 
   let uri = makeURI(testURL);
   let tagTxn = new PlacesTagURITransaction(uri, [testTag]);
   PlacesUtils.transactionManager.doTransaction(tagTxn);
 
   StarUI.panel.addEventListener("popupshown", onPanelShown, false);
-  starButton.click();
+  BookmarksMenuButton.star.click();
 }
 
 function onPanelShown(aEvent) {
   if (aEvent.target == StarUI.panel) {
     StarUI.panel.removeEventListener("popupshown", arguments.callee, false);
     let tagsField = document.getElementById("editBMPanel_tagsField");
     ok(tagsField.value == testTag, "tags field value was set");
     tagsField.focus();
@@ -88,15 +88,15 @@ function waitForClearHistory(aCallback)
 
 function onPanelHidden(aEvent) {
   if (aEvent.target == StarUI.panel) {
     StarUI.panel.removeEventListener("popuphidden", arguments.callee, false);
 
     executeSoon(function() {
       ok(!PlacesUtils.bookmarks.isBookmarked(makeURI(testURL)),
          "the bookmark for the test url has been removed");
-      ok(!starButton.hasAttribute("starred"),
+      is(BookmarksMenuButton.status, BookmarksMenuButton.STATUS_UNSTARRED,
          "star button indicates that the bookmark has been removed");
       gBrowser.removeCurrentTab();
       waitForClearHistory(finish);
     });
   }
 }
--- a/browser/base/content/test/browser_bug624734.js
+++ b/browser/base/content/test/browser_bug624734.js
@@ -6,17 +6,18 @@
 
 function test() {
   waitForExplicitFinish();
 
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   tab.linkedBrowser.addEventListener("load", (function(event) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
-    is(PlacesStarButton._starIcon.getAttribute("tooltiptext"), PlacesStarButton._unstarredTooltip,
+    is(BookmarksMenuButton.button.getAttribute("tooltiptext"),
+       BookmarksMenuButton._unstarredTooltip,
        "Star icon should have the unstarred tooltip text");
   
     gBrowser.removeCurrentTab();
     finish();
   }), true);
 
   tab.linkedBrowser.loadURI("http://example.com/browser/browser/base/content/test/dummy_page.html");
 }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1165,17 +1165,17 @@ BrowserGlue.prototype = {
     var notifyBox = win.gBrowser.getNotificationBox();
     var notification = notifyBox.appendNotification(text, title, null,
                                                     notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                                     buttons);
     notification.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 11;
+    const UI_VERSION = 12;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
     let currentUIVersion = 0;
     try {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } catch(ex) {}
     if (currentUIVersion >= UI_VERSION)
       return;
 
@@ -1331,16 +1331,43 @@ BrowserGlue.prototype = {
     if (currentUIVersion < 11) {
       Services.prefs.clearUserPref("dom.disable_window_move_resize");
       Services.prefs.clearUserPref("dom.disable_window_flip");
       Services.prefs.clearUserPref("dom.event.contextmenu.enabled");
       Services.prefs.clearUserPref("javascript.enabled");
       Services.prefs.clearUserPref("permissions.default.image");
     }
 
+    if (currentUIVersion < 12) {
+      // Remove bookmarks-menu-button-container, then place
+      // bookmarks-menu-button into its position.
+      let currentsetResource = this._rdf.GetResource("currentset");
+      let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
+      let currentset = this._getPersist(toolbarResource, currentsetResource);
+      // Need to migrate only if toolbar is customized.
+      if (currentset) {
+        if (currentset.contains("bookmarks-menu-button-container"))
+          currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,"$2");
+
+        // Now insert the new button.
+        if (currentset.contains("downloads-button")) {
+          currentset = currentset.replace(/(^|,)downloads-button($|,)/,
+                                          "$1bookmarks-menu-button,downloads-button$2");
+        } else if (currentset.contains("home-button")) {
+          currentset = currentset.replace(/(^|,)home-button($|,)/,
+                                          "$1bookmarks-menu-button,home-button$2");
+        } else {
+          // Just append.
+          currentset = currentset.replace(/(^|,)window-controls($|,)/,
+                                          "$1bookmarks-menu-button,window-controls$2")
+        }
+        this._setPersist(toolbarResource, currentsetResource, currentset);
+      }
+    }
+
     if (this._dirty)
       this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
 
     delete this._rdf;
     delete this._dataSource;
 
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
--- a/browser/metro/base/content/Util.js
+++ b/browser/metro/base/content/Util.js
@@ -310,16 +310,40 @@ let Util = {
   clamp: function(num, min, max) {
     return Math.max(min, Math.min(max, num));
   },
 
   /*
    * Screen and layout utilities
    */
 
+   /*
+    * translateToTopLevelWindow - Given an element potentially within
+    * a subframe, calculate the offsets up to the top level browser.
+    */
+  translateToTopLevelWindow: function translateToTopLevelWindow(aElement) {
+    let offsetX = 0;
+    let offsetY = 0;
+    let element = aElement;
+    while (element &&
+           element.ownerDocument &&
+           element.ownerDocument.defaultView != content) {
+      element = element.ownerDocument.defaultView.frameElement;
+      let rect = element.getBoundingClientRect();
+      offsetX += rect.left;
+      offsetY += rect.top;
+    }
+    let win = null;
+    if (element == aElement)
+      win = content;
+    else
+      win = element.contentDocument.defaultView;
+    return { targetWindow: win, offsetX: offsetX, offsetY: offsetY };
+  },
+
   get displayDPI() {
     delete this.displayDPI;
     return this.displayDPI = this.getWindowUtils(window).displayDPI;
   },
 
   isPortrait: function isPortrait() {
     return (window.innerWidth <= window.innerHeight);
   },
--- a/browser/metro/base/content/bindings/grid.xml
+++ b/browser/metro/base/content/bindings/grid.xml
@@ -407,17 +407,17 @@
 
     <!-- Internal methods -->
       <field name="_xslideHandler"/>
       <constructor>
         <![CDATA[
           if (this.controller && this.controller.gridBoundCallback != undefined)
             this.controller.gridBoundCallback();
           // set up cross-slide gesture handling for multiple-selection grids
-          if (CrossSlide && "multiple" == this.getAttribute("seltype")) {
+          if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
             this._xslideHandler = new CrossSlide.Handler(this, {
                   REARRANGESTART: this.crossSlideBoundary
             });
             this.addEventListener("touchstart", this._xslideHandler, false);
             this.addEventListener("touchmove", this._xslideHandler, false);
             this.addEventListener("touchend", this._xslideHandler, false);
           }
           // XXX This event was never actually implemented (bug 223411).
--- a/browser/metro/base/content/contenthandlers/Content.js
+++ b/browser/metro/base/content/contenthandlers/Content.js
@@ -42,171 +42,16 @@ let HTMLFrameSetElement = Ci.nsIDOMHTMLF
 let HTMLSelectElement = Ci.nsIDOMHTMLSelectElement;
 let HTMLOptionElement = Ci.nsIDOMHTMLOptionElement;
 
 const kReferenceDpi = 240; // standard "pixel" size used in some preferences
 
 const kStateActive = 0x00000001; // :active pseudoclass for elements
 
 /*
- * ElementTouchHelper
- *
- * Assists users by watching for mouse clicks in content and redirect
- * them to the best found target.
- */
-const ElementTouchHelper = {
-  get radius() {
-    let prefs = Services.prefs;
-    delete this.radius;
-    return this.radius = { "top": prefs.getIntPref("ui.touch.radius.topmm"),
-                           "right": prefs.getIntPref("ui.touch.radius.rightmm"),
-                           "bottom": prefs.getIntPref("ui.touch.radius.bottommm"),
-                           "left": prefs.getIntPref("ui.touch.radius.leftmm")
-                         };
-  },
-
-  get weight() {
-    delete this.weight;
-    return this.weight = { "visited": Services.prefs.getIntPref("ui.touch.radius.visitedWeight")
-                         };
-  },
-
-  /* Retrieve the closest element to a point by looking at borders position */
-  getClosest: function getClosest(aWindowUtils, aX, aY) {
-    if (!this.dpiRatio)
-      this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
-
-    let dpiRatio = this.dpiRatio;
-
-    let target = aWindowUtils.elementFromPoint(aX, aY,
-                                               true,   /* ignore root scroll frame*/
-                                               false); /* don't flush layout */
-
-    // return early if the click is just over a clickable element
-    if (this._isElementClickable(target))
-      return target;
-
-    let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio,
-                                                   this.radius.right * dpiRatio,
-                                                   this.radius.bottom * dpiRatio,
-                                                   this.radius.left * dpiRatio, true, false);
-
-    let threshold = Number.POSITIVE_INFINITY;
-    for (let i = 0; i < nodes.length; i++) {
-      let current = nodes[i];
-      if (!current.mozMatchesSelector || !this._isElementClickable(current))
-        continue;
-
-      let rect = current.getBoundingClientRect();
-      let distance = this._computeDistanceFromRect(aX, aY, rect);
-
-      // increase a little bit the weight for already visited items
-      if (current && current.mozMatchesSelector("*:visited"))
-        distance *= (this.weight.visited / 100);
-
-      if (distance < threshold) {
-        target = current;
-        threshold = distance;
-      }
-    }
-
-    return target;
-  },
-
-  _isElementClickable: function _isElementClickable(aElement) {
-    const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label";
-    for (let elem = aElement; elem; elem = elem.parentNode) {
-      if (this._hasMouseListener(elem))
-        return true;
-      if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector))
-        return true;
-    }
-    return false;
-  },
-
-  _computeDistanceFromRect: function _computeDistanceFromRect(aX, aY, aRect) {
-    let x = 0, y = 0;
-    let xmost = aRect.left + aRect.width;
-    let ymost = aRect.top + aRect.height;
-
-    // compute horizontal distance from left/right border depending if X is
-    // before/inside/after the element's rectangle
-    if (aRect.left < aX && aX < xmost)
-      x = Math.min(xmost - aX, aX - aRect.left);
-    else if (aX < aRect.left)
-      x = aRect.left - aX;
-    else if (aX > xmost)
-      x = aX - xmost;
-
-    // compute vertical distance from top/bottom border depending if Y is
-    // above/inside/below the element's rectangle
-    if (aRect.top < aY && aY < ymost)
-      y = Math.min(ymost - aY, aY - aRect.top);
-    else if (aY < aRect.top)
-      y = aRect.top - aY;
-    if (aY > ymost)
-      y = aY - ymost;
-
-    return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
-  },
-
-  _els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService),
-  _clickableEvents: ["mousedown", "mouseup", "click"],
-  _hasMouseListener: function _hasMouseListener(aElement) {
-    let els = this._els;
-    let listeners = els.getListenerInfoFor(aElement, {});
-    for (let i = 0; i < listeners.length; i++) {
-      if (this._clickableEvents.indexOf(listeners[i].type) != -1)
-        return true;
-    }
-    return false;
-  }
-};
-
-
-/*
- * Global functions
- */
-
-/*
- * elementFromPoint - find the closes element at a point. searches
- * sub-frames.
- *
- * @param aX, aY browser coordinates
- * @return
- *  element - element at the position, or null if no active browser or
- *            element was found.
- *  frameX - x position within the subframe element was found. aX if no
- *           sub-frame was found.
- *  frameY - y position within the subframe element was found. aY if no
- *           sub-frame was found.
- */
-function elementFromPoint(aX, aY) {
-  // browser's elementFromPoint expect browser-relative client coordinates.
-  // subtract browser's scroll values to adjust
-  let cwu = Util.getWindowUtils(content);
-  let elem = ElementTouchHelper.getClosest(cwu, aX, aY);
-
-  // step through layers of IFRAMEs and FRAMES to find innermost element
-  while (elem && (elem instanceof HTMLIFrameElement ||
-                  elem instanceof HTMLFrameElement)) {
-    // adjust client coordinates' origin to be top left of iframe viewport
-    let rect = elem.getBoundingClientRect();
-    aX -= rect.left;
-    aY -= rect.top;
-    let windowUtils = elem.contentDocument
-                          .defaultView
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
-    elem = ElementTouchHelper.getClosest(windowUtils, aX, aY);
-  }
-  return { element: elem, frameX: aX, frameY: aY };
-}
-
-/*
  * getBoundingContentRect
  *
  * @param aElement
  * @return Bounding content rect adjusted for scroll and frame offsets.
  */
 function getBoundingContentRect(aElement) {
   if (!aElement)
     return new Rect(0, 0, 0, 0);
@@ -344,18 +189,17 @@ let Content = {
         break;
 
       case "pagehide":
         if (aEvent.target == content.document)
           this._resetFontSize();          
         break;
 
       case "touchstart":
-        let touch = aEvent.changedTouches[0];
-        this._onTouchStart(touch.clientX, touch.clientY);
+        this._onTouchStart(aEvent);
         break;
     }
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     if (this._debugEvents) Util.dumpLn("Content:", aMessage.name);
     let json = aMessage.json;
     let x = json.x;
@@ -398,37 +242,33 @@ let Content = {
         break;
     }
   },
 
   /******************************************************
    * Event handlers
    */
 
-  _onTouchStart: function _onTouchStart(x, y) {
-    let { element } = elementFromPoint(x, y);
-    if (!element)
-      return;
+  _onTouchStart: function _onTouchStart(aEvent) {
+    let element = aEvent.target;
 
     // There is no need to have a feedback for disabled element
     let isDisabled = element instanceof HTMLOptionElement ?
       (element.disabled || element.parentNode.disabled) : element.disabled;
     if (isDisabled)
       return;
 
     // Set the target element to active
     this._doTapHighlight(element);
   },
 
   _onClickCapture: function _onClickCapture(aEvent) {
-    ContextMenuHandler.reset();
+    let element = aEvent.target;
 
-    let { element: element } = elementFromPoint(aEvent.clientX, aEvent.clientY);
-    if (!element)
-      return;
+    ContextMenuHandler.reset();
 
     // Only show autocomplete after the item is clicked
     if (!this.lastClickElement || this.lastClickElement != element) {
       this.lastClickElement = element;
       if (aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE &&
           !(element instanceof HTMLSelectElement)) {
         return;
       }
@@ -437,19 +277,20 @@ let Content = {
     this.formAssistant.focusSync = true;
     this.formAssistant.open(element, aEvent);
     this._cancelTapHighlight();
     this.formAssistant.focusSync = false;
 
     // A tap on a form input triggers touch input caret selection
     if (Util.isTextInput(element) &&
         aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
+      let { offsetX, offsetY } = Util.translateToTopLevelWindow(element);
       sendAsyncMessage("Content:SelectionCaret", {
-        xPos: aEvent.clientX,
-        yPos: aEvent.clientY
+        xPos: aEvent.clientX + offsetX,
+        yPos: aEvent.clientY + offsetY
       });
     }
   },
 
   // Checks clicks we care about - events bubbling up from about pages.
   _onClickBubble: function _onClickBubble(aEvent) {
     // Don't trust synthetic events
     if (!aEvent.isTrusted)
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
+++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
@@ -154,55 +154,30 @@ var ContextMenuHandler = {
     }
     this.reset();
   },
 
   /******************************************************
    * Utility routines
    */
 
-   /*
-    * _translateToTopLevelWindow - Given a potential coordinate set within
-    * a subframe, translate up to the parent window which is what front
-    * end code expect.
-    */
-  _translateToTopLevelWindow: function _translateToTopLevelWindow(aPopupNode) {
-    let offsetX = 0;
-    let offsetY = 0;
-    let element = aPopupNode;
-    while (element &&
-           element.ownerDocument &&
-           element.ownerDocument.defaultView != content) {
-      element = element.ownerDocument.defaultView.frameElement;
-      let rect = element.getBoundingClientRect();
-      offsetX += rect.left;
-      offsetY += rect.top;
-    }
-    let win = null;
-    if (element == aPopupNode)
-      win = content;
-    else
-      win = element.contentDocument.defaultView;
-    return { targetWindow: win, offsetX: offsetX, offsetY: offsetY };
-  },
-
   /*
    * _processPopupNode - Generate and send a Content:ContextMenu message
    * to browser detailing the underlying content types at this.popupNode.
    * Note the event we receive targets the sub frame (if there is one) of
    * the page.
    */
   _processPopupNode: function _processPopupNode(aPopupNode, aX, aY, aInputSrc) {
     if (!aPopupNode)
       return;
 
     let { targetWindow: targetWindow,
           offsetX: offsetX,
           offsetY: offsetY } =
-      this._translateToTopLevelWindow(aPopupNode);
+      Util.translateToTopLevelWindow(aPopupNode);
 
     let popupNode = this.popupNode = aPopupNode;
     let imageUrl = "";
 
     let state = {
       types: [],
       label: "",
       linkURL: "",
--- a/browser/metro/base/content/contenthandlers/SelectionHandler.js
+++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js
@@ -569,17 +569,18 @@ var SelectionHandler = {
   /*
    * Selection bounds will fall outside the bound of a control if the control
    * can scroll. Clip UI cache data to the bounds of the target so monocles
    * don't draw outside the control.
    */
   _restrictSelectionRectToEditBounds: function _restrictSelectionRectToEditBounds() {
     if (!this._targetIsEditable)
       return;
-    let bounds = this._getTargetClientRect();
+
+    let bounds = this._getTargetBrowserRect();
     if (this._cache.start.xPos < bounds.left)
       this._cache.start.xPos = bounds.left;
     if (this._cache.end.xPos < bounds.left)
       this._cache.end.xPos = bounds.left;
     if (this._cache.caret.xPos < bounds.left)
       this._cache.caret.xPos = bounds.left;
     if (this._cache.start.xPos > bounds.right)
       this._cache.start.xPos = bounds.right;
@@ -789,17 +790,17 @@ var SelectionHandler = {
   /*
    * Constrains a selection point within a text input control bounds.
    *
    * @param aPoint - client coordinate point
    * @param aHalfLineHeight - half the line height at the point
    * @return new constrained point struct
    */
   _constrainPointWithinControl: function _cpwc(aPoint, aHalfLineHeight) {
-    let bounds = this._getTargetClientRect();
+    let bounds = this._getTargetBrowserRect();
     let point = { xPos: aPoint.xPos, yPos: aPoint.yPos };
     if (point.xPos <= bounds.left)
       point.xPos = bounds.left + 2;
     if (point.xPos >= bounds.right)
       point.xPos = bounds.right - 2;
     if (point.yPos <= (bounds.top + aHalfLineHeight))
       point.yPos = (bounds.top + aHalfLineHeight);
     if (point.yPos >= (bounds.bottom - aHalfLineHeight))
@@ -810,17 +811,17 @@ var SelectionHandler = {
   /*
    * _pointOrientationToRect(aPoint, aRect)
    *
    * Returns a table representing which sides of target aPoint is offset
    * from: { left: offset, top: offset, right: offset, bottom: offset }
    * Works on client coordinates.
    */
   _pointOrientationToRect: function _pointOrientationToRect(aPoint) {
-    let bounds = this._targetElement.getBoundingClientRect();
+    let bounds = this._getTargetBrowserRect();
     let result = { left: 0, right: 0, top: 0, bottom: 0 };
     if (aPoint.xPos <= bounds.left)
       result.left = bounds.left - aPoint.xPos;
     if (aPoint.xPos >= bounds.right)
       result.right = aPoint.xPos - bounds.right;
     if (aPoint.yPos <= bounds.top)
       result.top = bounds.top - aPoint.yPos;
     if (aPoint.yPos >= bounds.bottom)
@@ -1098,17 +1099,17 @@ var SelectionHandler = {
     // there's no need to adjust. It will be above the keyboard.
     if (this._cache.element.bottom <= aNewViewHeight) {
       return 0;
     }
     
     // height of the target element
     let targetHeight = this._cache.element.bottom - this._cache.element.top;
     // height of the browser view.
-    let viewBottom = this._targetElement.ownerDocument.defaultView.innerHeight;
+    let viewBottom = content.innerHeight;
 
     // If the target is shorter than the new content height, we can go ahead
     // and center it.
     if (targetHeight <= aNewViewHeight) {
       // Try to center the element vertically in the new content area, but
       // don't position such that the bottom of the browser view moves above
       // the top of the chrome. We purposely do not resize the browser window
       // by making it taller when trying to center elements that are near the
@@ -1289,20 +1290,37 @@ var SelectionHandler = {
     seldata.element.bottom = r.bottom + this._contentOffset.y;
 
     // If we don't have a range we can attach to let SelectionHelperUI know.
     seldata.selectionRangeFound = !!rects.length;
 
     return seldata;
   },
 
+  /*
+   * Returns bounds of the element relative to the inner sub frame it sits
+   * in.
+   */
   _getTargetClientRect: function _getTargetClientRect() {
     return this._targetElement.getBoundingClientRect();
   },
 
+  /*
+   * Returns bounds of the element relative to the top level browser.
+   */
+  _getTargetBrowserRect: function _getTargetBrowserRect() {
+    let client = this._getTargetClientRect();
+    return {
+      left: client.left +  this._contentOffset.x,
+      top: client.top +  this._contentOffset.y,
+      right: client.right +  this._contentOffset.x,
+      bottom: client.bottom +  this._contentOffset.y
+    };
+  },
+
    /*
     * Translate a top level client point to frame relative client point.
     */
   _clientPointToFramePoint: function _clientPointToFramePoint(aClientPoint) {
     let point = {
       xPos: aClientPoint.xPos - this._contentOffset.x,
       yPos: aClientPoint.yPos - this._contentOffset.y
     };
--- a/browser/metro/base/tests/mochitest/Makefile.in
+++ b/browser/metro/base/tests/mochitest/Makefile.in
@@ -37,16 +37,18 @@ BROWSER_TESTS = \
 ifndef MOZ_DEBUG
 BROWSER_TESTS += \
   browser_selection_basic.js \
   browser_selection_basic.html \
   browser_selection_textarea.js \
   browser_selection_textarea.html \
   browser_selection_frame_content.js \
   browser_selection_frame_content.html \
+  browser_selection_inputs.js \
+  browser_selection_inputs.html \
   $(NULL)
 endif
 
 BROWSER_TEST_RESOURCES = \
   res/image01.png \
   $(NULL)
 
 libs:: $(BROWSER_TESTS)
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js
@@ -20,31 +20,32 @@ function setUpAndTearDown() {
   emptyClipboard();
   if (gWindow)
     clearSelection(gWindow);
   if (gFrame)
     clearSelection(gFrame);
   yield waitForCondition(function () {
       return !SelectionHelperUI.isSelectionUIVisible;
     }, kCommonWaitMs, kCommonPollMs);
-  yield hideContextUI();
 }
 
 gTests.push({
   desc: "normalize browser",
   setUp: setUpAndTearDown,
   tearDown: setUpAndTearDown,
   run: function test() {
     info(chromeRoot + "browser_selection_basic.html");
     yield addTab(chromeRoot + "browser_selection_basic.html");
 
     yield waitForCondition(function () {
         return !StartUI.isStartPageVisible;
       }, 10000, 100);
 
+    yield hideContextUI();
+
     gWindow = Browser.selectedTab.browser.contentWindow;
     InputSourceHelper.isPrecise = false;
   },
 });
 
 gTests.push({
   desc: "tap-hold to select",
   setUp: setUpAndTearDown,
--- a/browser/metro/base/tests/mochitest/browser_selection_frame_content.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_content.js
@@ -20,31 +20,32 @@ function setUpAndTearDown() {
   emptyClipboard();
   if (gWindow)
     clearSelection(gWindow);
   if (gFrame)
     clearSelection(gFrame);
   yield waitForCondition(function () {
       return !SelectionHelperUI.isSelectionUIVisible;
     }, kCommonWaitMs, kCommonPollMs);
-  yield hideContextUI();
 }
 
 gTests.push({
   desc: "normalize browser",
   setUp: setUpAndTearDown,
   tearDown: setUpAndTearDown,
   run: function test() {
     info(chromeRoot + "browser_selection_frame_content.html");
     yield addTab(chromeRoot + "browser_selection_frame_content.html");
 
     yield waitForCondition(function () {
       return !StartUI.isStartPageVisible;
       }, 10000, 100);
 
+    yield hideContextUI();
+
     gWindow = Browser.selectedTab.browser.contentWindow;
     gFrame = gWindow.document.getElementById("frame1");
 
     InputSourceHelper.isPrecise = false;
   },
 });
 
 gTests.push({
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_selection_inputs.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  </head>
+<body style="margin: 5px 5px 5px 85px;">
+<input id="a" style="width:300px; height:25px;" value="The rabbit-hole went straight on like a tunnel for some way and then dipped suddenly down" type="text">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_selection_inputs.js
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gWindow = null;
+var gInput = null;
+
+const kMarkerOffsetY = 12;
+const kCommonWaitMs = 5000;
+const kCommonPollMs = 100;
+
+///////////////////////////////////////////////////
+// form input tests
+///////////////////////////////////////////////////
+
+function setUpAndTearDown() {
+  emptyClipboard();
+  if (gWindow)
+    clearSelection(gWindow);
+  if (gInput)
+    clearSelection(gInput);
+  yield waitForCondition(function () {
+      return !SelectionHelperUI.isSelectionUIVisible;
+    }, kCommonWaitMs, kCommonPollMs);
+}
+
+/*
+  5px top margin
+  25px tall text input
+  300px wide
+*/
+
+gTests.push({
+  desc: "normalize browser",
+  setUp: setUpAndTearDown,
+  tearDown: setUpAndTearDown,
+  run: function test() {
+    info(chromeRoot + "browser_selection_inputs.html");
+    yield addTab(chromeRoot + "browser_selection_inputs.html");
+
+    yield waitForCondition(function () {
+      return !StartUI.isStartPageVisible;
+      }, 10000, 100);
+
+    yield hideContextUI();
+
+    gWindow = Browser.selectedTab.browser.contentWindow;
+    gInput = gWindow.document.getElementById("a");
+    InputSourceHelper.isPrecise = false;
+  },
+});
+
+gTests.push({
+  desc: "basic text input selection",
+  setUp: setUpAndTearDown,
+  tearDown: setUpAndTearDown,
+  run: function test() {
+    gInput.focus();
+    gInput.selectionStart = gInput.selectionEnd = 0;
+
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClick(200, 17);
+    yield promise;
+
+    checkContextUIMenuItemVisibility(["context-select",
+                                      "context-select-all"]);
+
+    let menuItem = document.getElementById("context-select");
+    ok(menuItem, "menu item exists");
+    ok(!menuItem.hidden, "menu item visible");
+    let popupPromise = waitForEvent(document, "popuphidden");
+    EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow);
+    yield popupPromise;
+
+    yield waitForCondition(function () {
+        return SelectionHelperUI.isSelectionUIVisible;
+      }, kCommonWaitMs, kCommonPollMs);
+
+    is(getTrimmedSelection(gInput).toString(), "went", "selection test");
+  },
+});
+
+gTests.push({
+  desc: "drag left to scroll",
+  setUp: setUpAndTearDown,
+  tearDown: setUpAndTearDown,
+  run: function test() {
+    gInput.selectionStart = gInput.selectionEnd = gInput.value.length;
+
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClick(190, 17);
+    yield promise;
+
+    checkContextUIMenuItemVisibility(["context-select",
+                                      "context-select-all"]);
+
+    let menuItem = document.getElementById("context-select");
+    ok(menuItem, "menu item exists");
+    ok(!menuItem.hidden, "menu item visible");
+    let popupPromise = waitForEvent(document, "popuphidden");
+    EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow);
+    yield popupPromise;
+
+    yield waitForCondition(function () {
+        return SelectionHelperUI.isSelectionUIVisible;
+      }, kCommonWaitMs, kCommonPollMs);
+
+    // check text selection
+    is(getTrimmedSelection(gInput).toString(), "way", "selection test");
+
+    // to the left
+    let xpos = SelectionHelperUI.startMark.xPos;
+    let ypos = SelectionHelperUI.startMark.yPos + 10;
+    var touchdrag = new TouchDragAndHold();
+    yield touchdrag.start(gWindow, xpos, ypos, 10, ypos);
+    yield waitForCondition(function () {
+      return getTrimmedSelection(gInput).toString() == 
+        "The rabbit-hole went straight on like a tunnel for some way";
+    }, 6000, 2000);
+    touchdrag.end();
+
+    yield waitForCondition(function () {
+        return !SelectionHelperUI.hasActiveDrag;
+      }, kCommonWaitMs, kCommonPollMs);
+    yield SelectionHelperUI.pingSelectionHandler();
+  },
+});
+
+gTests.push({
+  desc: "drag right to scroll and bug 862025",
+  setUp: setUpAndTearDown,
+  tearDown: setUpAndTearDown,
+  run: function test() {
+    gInput.selectionStart = gInput.selectionEnd = 0;
+
+    let promise = waitForEvent(document, "popupshown");
+    sendContextMenuClick(230, 17);
+    yield promise;
+
+    checkContextUIMenuItemVisibility(["context-select",
+                                      "context-select-all"]);
+
+    let menuItem = document.getElementById("context-select");
+    ok(menuItem, "menu item exists");
+    ok(!menuItem.hidden, "menu item visible");
+    let popupPromise = waitForEvent(document, "popuphidden");
+    EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow);
+    yield popupPromise;
+
+    yield waitForCondition(function () {
+        return SelectionHelperUI.isSelectionUIVisible;
+      }, kCommonWaitMs, kCommonPollMs);
+
+    // check text selection
+    is(getTrimmedSelection(gInput).toString(), "straight", "selection test");
+
+    // to the right
+    let xpos = SelectionHelperUI.endMark.xPos;
+    let ypos = SelectionHelperUI.endMark.yPos + 10;
+    var touchdrag = new TouchDragAndHold();
+    yield touchdrag.start(gWindow, xpos, ypos, 510, ypos);
+    yield waitForCondition(function () {
+      return getTrimmedSelection(gInput).toString() == 
+        "straight on like a tunnel for some way and then dipped suddenly down";
+    }, 6000, 2000);
+    touchdrag.end();
+
+    yield waitForCondition(function () {
+        return !SelectionHelperUI.hasActiveDrag;
+      }, kCommonWaitMs, kCommonPollMs);
+    yield SelectionHelperUI.pingSelectionHandler();
+
+    // down - selection shouldn't change
+    let xpos = SelectionHelperUI.endMark.xPos;
+    let ypos = SelectionHelperUI.endMark.yPos + 10;
+    yield touchdrag.start(gWindow, xpos, ypos, xpos, ypos + 150);
+    yield waitForMs(2000);
+    touchdrag.end();
+
+    is(getTrimmedSelection(gInput).toString(), "straight on like a tunnel for some way and then dipped suddenly down", "selection test");
+
+    // left and up - selection should shrink
+    let xpos = SelectionHelperUI.endMark.xPos;
+    let ypos = SelectionHelperUI.endMark.yPos + 10;
+    yield touchdrag.start(gWindow, xpos, ypos, 105, 25);
+    yield waitForCondition(function () {
+      return getTrimmedSelection(gInput).toString() == 
+        "straight on like a tunnel for";
+    }, 6000, 2000);
+    touchdrag.end();
+  },
+});
+
+function test() {
+  if (!isLandscapeMode()) {
+    todo(false, "browser_selection_tests need landscape mode to run.");
+    return;
+  }
+
+  requestLongerTimeout(3);
+  runTests();
+}
--- a/browser/metro/base/tests/mochitest/browser_selection_textarea.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_textarea.js
@@ -20,31 +20,32 @@ function setUpAndTearDown() {
   emptyClipboard();
   if (gWindow)
     clearSelection(gWindow);
   if (gFrame)
     clearSelection(gFrame);
   yield waitForCondition(function () {
       return !SelectionHelperUI.isSelectionUIVisible;
     }, kCommonWaitMs, kCommonPollMs);
-  yield hideContextUI();
 }
 
 gTests.push({
   desc: "normalize browser",
   setUp: setUpAndTearDown,
   tearDown: setUpAndTearDown,
   run: function test() {
     info(chromeRoot + "browser_selection_textarea.html");
     yield addTab(chromeRoot + "browser_selection_textarea.html");
 
     yield waitForCondition(function () {
       return !StartUI.isStartPageVisible;
       }, 10000, 100);
 
+    yield hideContextUI();
+
     gWindow = Browser.selectedTab.browser.contentWindow;
     InputSourceHelper.isPrecise = false;
   },
 });
 
 gTests.push({
   desc: "textarea basic selection",
   setUp: setUpAndTearDown,
--- a/browser/metro/base/tests/mochitest/head.js
+++ b/browser/metro/base/tests/mochitest/head.js
@@ -179,17 +179,17 @@ function fireAppBarDisplayEvent()
  *
  * @param aUrl the URL to load
  * @returns a task that resolves to the new tab object after the URL is loaded.
  */
 function addTab(aUrl) {
   return Task.spawn(function() {
     info("Opening "+aUrl+" in a new tab");
     let tab = Browser.addTab(aUrl, true);
-    yield waitForEvent(tab.browser, "pageshow");
+    yield tab.pageShowPromise;
 
     is(tab.browser.currentURI.spec, aUrl, aUrl + " is loaded");
     registerCleanupFunction(function() Browser.closeTab(tab));
     throw new Task.Result(tab);
   });
 }
 
 /**
@@ -208,17 +208,16 @@ function addTab(aUrl) {
  *    }
  *
  * @param aSubject the element that should receive the event
  * @param aEventName the event to wait for
  * @param aTimeoutMs the number of miliseconds to wait before giving up
  * @returns a Promise that resolves to the received event, or to an Error
  */
 function waitForEvent(aSubject, aEventName, aTimeoutMs) {
-  info("waitForEvent: on " + aSubject + " event: " + aEventName);
   let eventDeferred = Promise.defer();
   let timeoutMs = aTimeoutMs || kDefaultWait;
   let timerID = setTimeout(function wfe_canceller() {
     aSubject.removeEventListener(aEventName, onEvent);
     eventDeferred.reject( new Error(aEventName+" event timeout") );
   }, timeoutMs);
 
   function onEvent(aEvent) {
@@ -543,18 +542,21 @@ function sendTouchDrag(aWindow, aStartX,
  */
 function TouchDragAndHold() {
 }
 
 TouchDragAndHold.prototype = {
   _timeoutStep: 2,
   _numSteps: 50,
   _debug: false,
+  _win: null,
 
   callback: function callback() {
+    if (this._win == null)
+      return;
     if (++this._step.steps >= this._numSteps) {
       EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
                                         { type: "touchmove" }, this._win);
       this._defer.resolve();
       return;
     }
     this._currentPoint.xPos += this._step.x;
     this._currentPoint.yPos += this._step.y;
--- a/browser/metro/modules/CrossSlide.jsm
+++ b/browser/metro/modules/CrossSlide.jsm
@@ -140,17 +140,17 @@ CrossSlideHandler.prototype = {
         this._onTouchMove(aEvent);
         break;
       case "touchend":
         this._onTouchEnd(aEvent);
         break;
     }
   },
 
-  cancel: function(){
+  cancel: function(aEvent){
     this._fireProgressEvent("cancelled", aEvent);
     this.drag = null;
   },
 
   _onTouchStart: function onTouchStart(aEvent){
     if (aEvent.touches.length > 1)
       return;
     let touch = aEvent.touches[0];
@@ -178,17 +178,17 @@ CrossSlideHandler.prototype = {
     if (!this.drag) {
       return;
     }
     // event is handled here, dont let it bubble further
     aEvent.stopPropagation();
 
     if (aEvent.touches.length!==1) {
       // cancel if another touch point gets involved
-      return this.cancel();
+      return this.cancel(aEvent);
     }
 
     let startPt = this.drag.origin;
     let endPt = this.drag.position = pointFromTouchEvent(aEvent);
 
     let scrollAxis = this.drag.scrollAxis,
         crossAxis = this.drag.crossAxis;
 
@@ -197,28 +197,28 @@ CrossSlideHandler.prototype = {
     // distance along the scrolling axis
     let scrollAxisDistance = Math.abs(endPt[scrollAxis] - startPt[scrollAxis]);
 
     let currState = this.drag.state;
     let newState = this.getCrossSlideState(crossAxisDistance, scrollAxisDistance);
 
     if (-1 == newState) {
       // out of bounds, cancel the event always
-      return this.cancel();
+      return this.cancel(aEvent);
     }
 
     let isWithinCone = withinCone(crossAxisDistance, scrollAxisDistance);
     if (currState < CrossSlidingState.SELECTING && !isWithinCone) {
       // ignore, no progress to report
       return;
     }
     if (currState >= CrossSlidingState.SELECTING && !isWithinCone) {
       // we're committed to a cross-slide gesture,
       // so going out of bounds at this point means aborting
-      return this.cancel();
+      return this.cancel(aEvent);
     }
 
     if (currState > newState) {
       // moved backwards, ignoring
       return;
     }
 
     this.drag.state = newState;
@@ -227,17 +227,17 @@ CrossSlideHandler.prototype = {
   _onTouchEnd: function(aEvent){
     if (!this.drag)
       return;
 
     // event is handled, dont let it bubble further
     aEvent.stopPropagation();
 
     if (this.drag.state < CrossSlidingState.SELECTING) {
-      return this.cancel();
+      return this.cancel(aEvent);
     }
 
     this._fireProgressEvent("completed", aEvent);
     this._fireSelectEvent(aEvent);
     this.drag = null;
   },
 
   /**
index ca448250e6012337314a7d5eea9373139c807618..bcc8f63dcd9fef85eaec6181a36f75e7519c81b6
GIT binary patch
literal 5429
zc$@(>70T*~P)<h;3K|Lk000e1NJLTq0058x001Be1^@s6{fzO=000#HNkl<ZcwX(A
z2~<=^y2o*eF_Sn^lSyJGZ=x}ps7WT7$u{${I1}>{6%n@>QCv_EQA9;nl|@BFabM!T
zfE#LpMiCK(pll+t$SMdlG)*t8-SmEM_y2u$fsT+S(RXIfd*{5abAIR6t@^6bdw=~^
z)xB-^uP%efg}**@LPXf8saw;AIc!ofMoiw4_L}|1FxJ;K{?+ke|9E}E=3?r@<6`#+
z#aQn!l$TBX#3Xkd!;YwTe9i7L3a6O#rb#hzEGL`j#4srwzDC2uVGqY_3J(t@&>eGT
z&H8rb^5ws`_W3!p?EaKDr|0*%&OMA&$D@$zY-h}y)8l(K*6!a|`cQde;J|?+?CtG;
z_gEu0hHbb-pbNKtL!6!@%gQ$#H)}jMU*>k6yMyyLD{wrq6afcrVQS!c?sbPv8j@=z
zi|rWqntepghpxwvl2wGF`}c7FK`|;sWvH&LghVQayk3gN=6WbqEzoGyFc=LmnM~~h
zp+|QDKtAO~lWgK-UOpkT^}&$5){0?8yxidkub3Fj+BG!}1Aa{$G>(1BBy$+Y%O{4a
zD@Wuj%SY+7qPH{X{Iu{H3dga)=w9vNFRxg(?7;n!5*YOQPHYSdLqzyStPWgdTobrz
z|LTCiPJcIVZm(F$_Rmemn?c~y*FVHM@M&worG|pL6#_104Y=e$a5wy5i1$Km#0Msl
zDE8lF&qp79G`OOoVx~%^3SP2g$)5rAc;vww)~|<3Ro@BL_G|KtoV+FVzdR1B(+lfx
zB)%96HeJI%mz@=GS{DzWgP919zKg#)f2STcadT>iM0CWTXYIv>>vvEmZ-82BfLdpO
zMrZsPy%AagPC#ei1c6443h852p1KLoa|(w!bhD$?mBaH4Eiq<&%SkiU^QrADYiIpT
zlI{c|$;MOM9!~mS7geGOX0urjRplopPCD*~w$E_bWs*<)s{MNP2?+{HxR#IrjYfm!
zmKMn6a_DqAP#@yr;vf=<P+U}m^=sEkeOIoWV#z?xJ$No73|#Cum<kTU+)w~h*?I7p
zBvG6bj3+;aG3o;tkNpLP!|%Xwa1<T?0)6~a<T%^${|<TF+}xgc<&{_70`MJiWy_W=
z(-IRCpM7MB=s$@Tr2^G8H4LH7+PaHpndi$zht;_i4R9rK0`^=*!cQr<k#bESAvpy;
zyW-%u`bYR3&%t}M_NpoKtn3iz3-?3##AQE5yB)&kZu{}|(xaH_cM48HXW<-r9&^|I
zfCb?furT5hmTb8K@156vRUno*G@_9yss}@=n@a|2O`IGiPJ!mq=QOS5gDbS6chX6M
zqeiL2*IzD(%pW>oW2dR{%LPIB{`>Ke6~Bl2k`d4=FM<q$uJH)uMQ@_6@NML0j8*e8
zhY%*gy3n<Ul5QlSwN(v?qz>X*2^94WB$F1@hn>52A@TZklBk-*QegYmt$cu=-=y{_
z45r*|;C~zoE^`N9)ZpP_u1SLN{Ma^$j=t9>(SeaLCawS*+csl_9VAQyy1~R^SZsUz
z+eDT%0RZ&gym|A;)YQ~j0FDs1OG-)t_wL>M9)MntBv5Eb2%4JZsHhS#ggR^MZq37{
zt;FmaxNJ<o>9||S$h-wfT@6&NDnf}`Newb@r{d(L6xjP;#KN6v7)sL_8%tnP$Q8J4
zjK{3i=P@Jj6mc3eR-MMIpcu>!IV%X{5*BL_$aha-CxNzX+q^zv^9F9?rZDW>5sIv2
zduSA4Fd4-HW;0K4RIdhasDf4=3|Y~qwY=QXd&u~3?7v!u)3>TEq~yk-x%?xTO&XZ^
z20H4QK!lz|tfzAoq=lnE62^oC2mfvFp1o*Ms!&y314*3})iq+=ymbpI5{mk<apNXj
ziNA`%;u1(@a!93e`1|=atzNb2IYFR1o4}p@9J=s<(5&hO-FC_{XJ>;iPi&J&+Xy~s
zWxG7ox9<%Y6TB<}g{@o1x3nmrRMuOineHFOaP6^U2^G<CKbaKI5iboMJor`0KJSNy
zhE6dU40`}16WK<iG1<q*$DS=&2MieSCl=d2#=1~JYl6_wB%}361w*K_Hfym%?IXh6
zHvGu#ioJ(b`x9_CHw#*=793|JICw4dgu$SPTCGM--W~YtzJi$%Nf_gGiW}0lu!iIM
z>sT983a7P~@Xy7E@Riq5Oz@6^x5se!yY<7IRXaNg<p13bi$EbEA-y+6h8x**!HSu$
z0pGk8t<{r2nTNzNkN`?*Z(vZHg!(3<?Cv)vS`<f;h=MdLj^<i8nRpbH1!JI*htQaM
zq7i0JLdRM<N~t{%IjJ9kO=W=rfiVR|#i*6aP(c|cFaI9atX=~@U%%qJxp_i;^yi~P
zJUy2-+$$&&uBTklfU60KEGCR3VCx+=MG=36@!;Dq?0yaU%`ZX|^dt-y+yO%?Sm6?u
zK)>g8=ywf)G0xp05Pi7?Nh+&ER$+bNl!++Lo#1lr-hMbC{Bh^bogYgilF3r3bUIDJ
zi}ZT^dH``mb~{CMFE7wF-YOQ0?J3qFGBWZ58vBY>fdW^pf})`gg{5T-q0ZW@#g3e^
zB`qu}19QXUk#RQ<a)k_JEO?$57Q54?PoZ-I^m-j+aw*cXb1**O0=`)rkKvAyY269r
z<mrXI;eW=x^gygw_SwS%k*G-z3)Ghc;sFSfs+GmiH|~X2>Wb#FKeh>=&xTo-L3KG=
z?*9pTxf{xIT+N0$$KBQhIz<BI<OM=>k8Y^y5{=J=$&f32j=t#-YD-4r(q$JxApd{>
zN+=B|E-T0V@=Evz1j1?N%nfvDM$otUKtNz1s6OCtfB*Z`unilG_X>+qQeJ@vl_Ger
zTv@~<;F1EN-#ZN4?QJm0a=;gzhB4lQM0pL`_0K}L^$kHHvsyuMZ$TIN5{wt-S_EQ}
zHZ)36TK>Q)b+TGP<XyXVqV#?ti)H5>0=;zU(xou~ej+l6t3)c1-Gy8thsY-ilai9g
z(AYs%1@iL`fJ{+~yuxCJP-kt{VrgfkI(Vf7|DeTkNog_E>Q;+DF=x-T3uF`oQmK?E
zxnGP=mz=@E-D#j5nX0Qm3*5)R&*LTd&FckO#V(kPRaoQkI|O<RM1a>Qct%7c=wPx{
zfs{%ml+ESPHXMdVx)d6T8_dRH`iuueIn0K9s3k5iHu!c3v>`M+oMbnlNvXEb+MEJi
z!v?fG7y&a^L-G`nNU<>K^XYRcp{jTf1(|IE`S|%kLIP#w-9uJRJ{B%oR7Ao&-?2NJ
zGjCqU+N19RSGSvYbMui+)Jo;>@%7V@1SE@Bn-n)ANKzF#imCDjWt_L54g0;2iCE!_
ze}HyfKNw=BTLdDBA*+`lzqqtpY9!Sx#&V=F9Rl?nK79C)sHmuM^o36bkVvFk5$Fz)
z$?C_CAOEU<|NgJCv4RBNKE9O%I*2+E4T(&QJNX3+R%dOj?_r8N1lk^3fYDwtlrAfT
zEI{+8Fom6op>@n`CmcR<2y(d;N@WWwMHSr&<QyCYUzc7eP5%Ts*>ae5H%WjJ@Xg_<
zFCB--fakC*a0x;Vr&txJxw#3AjdG~!W1*4xK`ovMPLqt5@)5+F;MB>~_AR)kU_qc(
z(WI$t-NcGkr?sF{--n_32vnlapsNpqvhrgnt3ILgG0@3_psk&NqTBPpD;z_XE?=&%
zs+A%&>n<|x<Y2CgOL9kc_FBFom`P;W-MBBx%goM0W=<Z&(t36c1qlqX3!n{s8d_Ta
zXaek@-82OJy_hy($`hao?#tFjmNk)X+eqk-Pp}9?pJTaS-9i1GpfQ~i!t>8RKWN8}
z9TRw-ztX<;xzk0U3?|rt0|zFJ8a3)aXvohyGG`a(Z<l#{Ym-uLKwMWXkXKxc^t*Ws
zR=2b#CMAN!I=jpgb`ft(+LGqEGYQjzFQd4;3N0<oEZeYk4`D!kg91m79fq&J4-`}{
zD67Oc|L&)-`ILb+`aTDhXe^r|1Zb^-v){cAKhGgpvMC1Zj^6Af5G|m2w#aR0sE0x!
zN25Fot@3?P)y#mZ${uQ|FIpwuB*A#7s%Map5y<>$nfX?NTYqZ;2`H0Eo=9k<OQ4ne
zLn9BR&+&n}ZZS+P5x9HP6-L>_VXp3r?&lWXCn*X@q8tRS2{z1enLm;VL)QsJ5UYDG
zTeiT9&T|V(kd<GEikdo*d<}v?F*BJsU9m`{KQW1&3kz!ibaaOP*f@(oUMp5mW)UHk
z1hz&+bp=?=LHZ)6-%ew(!ns|S@`peCVd%Yk_uSj3v#c%x-7YOHUG(m|?~b(VTKc_g
z_vE*;XG~kNY`OO8^#q7&tB`U#yA!M}KJh9jMm=rjH;!x!8_S+B+xx_Di+86Y`0!6?
zY-)g3tAS3ZwZP7sTbih^9%~N%1gGCvM0<M-Mb5SNEdtSY8@}^<!96TWSOaZ1e#;^d
zZ8@Lr5Qt?tsZ@fxx>_jeDDz17kvzd@t?M92fNMQVSuv6)`JS;RkXoZgi>e;V#zeF#
zzH5_57J^n;uz*>83j6o9ts$m6&D<Mx<{S#ktB{;|2T7@!aGp0`G1h*93tcND{*($j
zW)n!dm5$qa1;{UXfLoc_aB*80$0X37nb9Ul+zObL5_Ys?B38KD``Ed#I}^^+c0hmh
zYl}c0OTADfu0&FLmQ`4vyXR8Ooa?-ZV)d7=UI86{ArL)rezUo`*&D#Mc7bvk?e!E|
zB6xrB!3V=xtW}Ao%$Vx9$aATh{ejAwO58}h%@FFB;^RPlswp$3I8bb#hqv+U3G=dj
znZg$RdR7@)HLdK)v9N`2Q8tr2jc8S?ka+t(#`<39w2ikWkiW-^5a*6Y)|J2D!r|d)
zsrVY}Rt&_V&>ez65z%Qs3$%4(&fa~yN_d_#*42rtYip}T;u?{trn*Wbsm>PHi_a^X
zY7aCuOLjLm)*fz@i4&xHy~c3#_@T0h$nYX-0;$w0G_^ER#*{%WD}=o6Iuw#~kcuxt
zTyqmJ8^Oj2n~1mG8Z~R)JXLl<DKhhlaPe9)4n?1V=SqKey-*>R;l#NgNSs2-JVm%k
zS*lDV!P*TQ@%8xeQv`v|&4MQMDVSAt0KksIxhA-euqHCqWUzAq7&S0er$ZlYZxP6C
z@e;1AstgaRA6SL;u^75<PdQGXWbf*+xSq~`A&`rU%V#uAT?LRtWb-_KlV*{Q_0SdS
zOPo1#=BM30P-J2_PM_>Re?WC1?nlc%AolV_u(pZcINDp}c?kAUkUbpq*q0>;5_&Wh
zVtEq`1|#$a1H`gMgwV9Z`Y5K;gG6frt#p4GK}(*&%EiOrxpWKy7Cr@kx4!ULyGIac
z^T~9JKqSBk+Fm{&DoLDE)uM_Mzoh(RO?COnj&qGhU8>V+9&FjR=}1?BG+GUlU&xj+
zg%q`QH4uqu*Hm>MWfdg?04xF#y+8lrixplzex|Ja5+r40WBY-lSh&KM-87U{*CFd(
zF=c`~)OQEP6*V|^`YfF0%)LNkpB5I;mtCRXHVB6Jl`I3Xqj0X@@&b&BK`>qqX*-X6
zfz^p%7|&0$2sCTHOO%WIBA!4u@GPd|9{*$9=nv<(x+ZseC;95DeZ#`S#saubWYEJy
zui)Td$LZ6jf0~hzF&RKA>)W?)-(-49Ej+>RR-y^hCXS;&syufw77VJl*iUu%iuHB-
z9*ka3Ffk~*uqleIbJ${;!RkUA>!TRUtBmdh8ozWGzVQmiWdBo`zB&eTR(}WAkiC{|
z{MOSM4-2$V-KvVN6^lH@HC2lr39)Di8$$xkw<b^v3DiIWN#zoVB_dQ-mkZ()m)&D@
z6^POQZ=ZhV>pb7J;o`L<%G!6ZZQo(cU*v((DhW+(cacRIs<6Bo2acblsbpTtfPn*F
zZ+{Za8?Sl5aO^!n5Y3w2!qIT(O#$t?XP{mG%+KfR`#~G_ER2_CfsJjK;#uP5-{9GY
z?r|^Txh?`cy>Q{e4_jMX*Liz;|8w5Fc_T@nzSQ1}Ua0=j$;s(s`jYNmwQAKTOcTQH
zS0x%ddHk2NUEM?sR;RWne(Pl_%PMSYV(S}PB(t5C0IRb$*2iMHzD)hp^9%`8g1H;6
z2oDga1)jp3(DPWh@glso#ld$^0&U%IAne#p%06k>9+PQ#nMwkTQnobP|4$%+M$mXO
zrD{A*VQV@zuFEBOYVgpZBORtrJ3Yl|CO_NF19KO;qo}gB6S48v@$H<s5gogjyxDfK
zif!+kxdeA`*B66J^Z<9wy`6>N5?qM|;I7VZBYvJBk0SCNGaGC7uPZ%>LBuOBzWCzP
zT{{O28uYvNr_e78vA<QAFvJTCR)3tIbFjrR+hMV7I)l}R{4e~R<5AeQW6Pe7*C3Ay
zi{H9^^PX;QLO&8|$UA?1-;KU}_!WXWe$GK6e4jhZ?$LkFL9uKM8*67PHvTV?ddzNk
zdQ?wIdUUS`R3Bjzz{X>dcD(0Fao$qCuE5_!BK`0<V{O>7&kO0%PjV)Xy?|CC<K#0u
zwGDdI@iuG%t**?RZ)EQd&H4;f=Xi0{7HlonhE0|}#7bgMdQ`6q8PUCOr$;}b<Mk4l
zb=P6moCBv5fijYw`m!kIB8w$_iM}=g{-2W;)$?3g>bFL1%N-bXH862a!f&WL6_bkE
z8(~o8qE#M?#;QH$f-CPDXzXR10JaRYS)3op<5vq0v#}5+Z5+(nD=>3)xSukfOFiCW
zw@m<>K>a9_+$c|V=FGebFf(+72H<5d8}g|7BTUMz#CCAn928yq+(=_?(%Aks0c<dd
zdZ!)laVY!y*LZ_6l|+dI-{1+p-W9yUNx;<P4}&rVIkCfdiakMNe(`YGhE1ds)pwV2
zCe0=A&1*Yh(p-Qz&xfO!b2b5NScP_9*BV7fVA3VSq&^Fi_6iZ-R@dD?bIl2)NB6m9
zZLAGj_UVxp)u&ab%!Qf#ew?p^x-k{4iey%&wle5d1xP#IThFGvuH$Xk1bXXs%u6tF
z8W?qQRAeqTrbYK{O^<%6xjfCqXwXPt;&sS6_llWf-|sr!hE1UV%sA1{Bq`cv&O9}M
zPm6joirSth`lm%bu|M<Vv!>d@Na`ENQ`;9d0c`vNn!-+`NB50mGFcJqL+ZOkF=wdW
f(<Xq8e@Xdo>D__1{Xtw&00000NkvXXu0mjfy0&6Y
index 5ae54323a35b2233a196ba428bf306e90cc0c482..2851657ecaf324ffd663dbb6b3e37586c977b939
GIT binary patch
literal 8925
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
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -470,20 +470,16 @@ menuitem:not([type]):not(.menuitem-toolt
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
 }
 
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbar {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
-#BMB_bookmarkThisPage {
-  list-style-image: url("chrome://browser/skin/places/starPage.png");
-}
-
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png");
 }
 
 #appmenu_downloads,
 #menu_openDownloads {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 16px 16px 0px);
@@ -648,29 +644,20 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 #downloads-button {
   -moz-image-region: rect(0px 24px 24px 0px);
 }
 
 #history-button {
   -moz-image-region: rect(0px 48px 24px 24px);
 }
 
-#bookmarks-button,
-#bookmarks-menu-button {
+#bookmarks-button {
   -moz-image-region: rect(0px 72px 24px 48px);
 }
 
-#bookmarks-menu-button.bookmark-item {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-}
-
-#bookmarks-menu-button.toolbarbutton-1 {
-  -moz-box-orient: horizontal;
-}
-
 #print-button {
   list-style-image: url("moz-icon://stock/gtk-print?size=toolbar");
 }
 #print-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-print?size=toolbar&state=disabled");
 }
 
 #new-tab-button {
@@ -721,18 +708,18 @@ toolbar[mode="full"] .toolbarbutton-1 > 
   list-style-image: url("chrome://browser/skin/sync-24-throbber.png");
   -moz-image-region: rect(0px 24px 24px 0px);
 }
 
 #feed-button {
   -moz-image-region: rect(0px 168px 24px 144px);
 }
 
-#feed-button[disabled="true"] > .toolbarbutton-icon {
-  opacity: .3;
+#feed-button[disabled] > .toolbarbutton-icon {
+  opacity: .4;
 }
 
 #webrtc-status-button {
   -moz-image-region: rect(0px 192px 24px 168px);
 }
 
 /* 16px primary toolbar buttons */
 toolbar[iconsize="small"] .toolbarbutton-1:not([type="menu-button"]) {
@@ -814,19 +801,17 @@ toolbar[iconsize="small"] #downloads-but
   -moz-image-region: rect(0px 16px 16px 0px);
 }
 
 toolbar[iconsize="small"] #webrtc-status-button /* temporary placeholder (bug 824825) */,
 toolbar[iconsize="small"] #history-button {
   -moz-image-region: rect(0px 32px 16px 16px);
 }
 
-toolbar[iconsize="small"] #bookmarks-button,
-toolbar[iconsize="small"] #bookmarks-menu-button,
-#bookmarks-menu-button.bookmark-item {
+toolbar[iconsize="small"] #bookmarks-button {
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
 toolbar[iconsize="small"] #print-button {
   list-style-image: url("moz-icon://stock/gtk-print?size=menu");
 }
 toolbar[iconsize="small"] #print-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-print?size=menu&state=disabled");
@@ -1345,17 +1330,18 @@ toolbar[iconsize="small"] #webrtc-status
 }
 
 #treecolAutoCompleteImage {
   max-width : 36px;
 }
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
-  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+  list-style-image: url("chrome://browser/skin/places/star-icons.png");
+  -moz-image-region: rect(0px 32px 16px 16px);
   width: 16px;
   height: 16px;
 }
 
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
   list-style-image: url(moz-icon://stock/gtk-find?size=menu);
   width: 16px;
@@ -1487,23 +1473,44 @@ richlistitem[type~="action"][actiontype=
 #unsharePopupText {
   height: 48px;
 }
 
 #unsharePopupBottomButtons {
   margin-top: 1em;
 }
 
-/* Star button */
-#star-button {
-  list-style-image: url("chrome://browser/skin/places/starPage.png");
-}
-
-#star-button[starred="true"] {
-  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+/* bookmarks menu-button */
+
+#bookmarks-menu-button {
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+  -moz-image-region: rect(0px 216px 24px 192px);
+}
+
+#bookmarks-menu-button[starred] {
+  -moz-image-region: rect(24px 216px 48px 192px);
+}
+
+toolbar[iconsize="small"] #bookmarks-menu-button,
+#bookmarks-menu-button.bookmark-item {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 144px 16px 128px);
+}
+
+toolbar[iconsize="small"] #bookmarks-menu-button[starred],
+#bookmarks-menu-button.bookmark-item[starred] {
+  -moz-image-region: rect(16px 144px 32px 128px);
+}
+
+#bookmarks-menu-button[disabled] > .toolbarbutton-icon,
+#bookmarks-menu-button[disabled] > .toolbarbutton-menu-dropmarker,
+#bookmarks-menu-button[disabled] > .toolbarbutton-menubutton-dropmarker,
+#bookmarks-menu-button[disabled] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
+#bookmarks-menu-button > .toolbarbutton-menubutton-button[disabled] > .toolbarbutton-icon {
+  opacity: .4;
 }
 
 /* Bookmarking panel */
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
 }
--- a/browser/themes/linux/devtools/common.css
+++ b/browser/themes/linux/devtools/common.css
@@ -125,17 +125,17 @@
   -moz-box-align: stretch;
 }
 
 .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
 .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none !important;
   list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
   -moz-box-align: center;
-  margin: 0 3px;
+  padding: 0 3px;
 }
 
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
   margin: 0 3px;
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -75,24 +75,23 @@ browser.jar:
   skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
   skin/classic/browser/newtab/controls.png            (newtab/controls.png)
   skin/classic/browser/newtab/noise.png               (newtab/noise.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
   skin/classic/browser/places/calendar.png            (places/calendar.png)
 * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
   skin/classic/browser/places/livemark-item.png       (places/livemark-item.png)
-  skin/classic/browser/places/pageStarred.png         (places/pageStarred.png)
+  skin/classic/browser/places/star-icons.png          (places/star-icons.png)
   skin/classic/browser/places/starred48.png           (places/starred48.png)
   skin/classic/browser/places/unstarred48.png         (places/unstarred48.png)
   skin/classic/browser/places/places.css              (places/places.css)
   skin/classic/browser/places/organizer.css           (places/organizer.css)
   skin/classic/browser/places/organizer.xml           (places/organizer.xml)
   skin/classic/browser/places/query.png               (places/query.png)
-  skin/classic/browser/places/starPage.png            (places/starPage.png)
   skin/classic/browser/places/tag.png                 (places/tag.png)
   skin/classic/browser/places/toolbarDropMarker.png   (places/toolbarDropMarker.png)
   skin/classic/browser/places/unsortedBookmarks.png   (places/unsortedBookmarks.png)
   skin/classic/browser/places/downloads.png           (places/downloads.png)
   skin/classic/browser/preferences/alwaysAsk.png      (preferences/alwaysAsk.png)
   skin/classic/browser/preferences/mail.png           (preferences/mail.png)
   skin/classic/browser/preferences/Options.png        (preferences/Options.png)
 #ifdef MOZ_SERVICES_SYNC
deleted file mode 100644
index 61a9f90e05b15a6206727be766294d7baa593995..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2f50c6ab973e15623a9fec0dfe47a14ae72eb78d
GIT binary patch
literal 1106
zc$@)J1g-mtP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^000CTNkl<ZNXN}p
zYfPJE6mGeVZIS_kP**K#L<dX6WQ-*`O>~J->!KKg0sR3+a1pm3h*^dSLFE@Pg$zVu
z7JI>{#0{-f0}N#L!ew$N;1Jzb3vQ0pwku!1>+yMKXBcZ2_~%KU<m7$N`Ob6RbNN)N
z$Px#6AUQdChhDGWqS~O?K2v2nj8SffDf(CFJ2vXC%FD~kSXfvn^7(w#<>loM5s`W$
z{&quDyKDTQv^aHG29XmR3SL@T8mrZ6cOvM=fVH8a;iaCQo^9(TfsF5Pm^O*Bc$pN*
zOV0M&B%Ep16&yig>gwvUY&P2~(2oN{v$L~DT3T8jh91360`LAd_?n>b6Ql&4bbj=Z
zh_mWQ_Uxm#1L+wV89HRYtEQ&rMV{xI5wIUPBM3r&Wo4xvHoBywq`R4I$R`>fjsxBX
znjOaIPNykm$YF}*q@bG=u9uWR2Z`J{!ixlPHw>F^m@V)Q5YGr~Z)$4V<956AmzI_a
z0)fDRU@-Ur0(Y_CA<T%P*vWC+QJ2f5hwc9Q`ube>YVF3Tj<dFxg_XZYNZ>AzEM6f+
z;pqmEeK=nx-Zw`6izjLM_hx0vn#05QJ4PVgVzKOl*^ht|_yMr33Vt19z$h>QO!xKm
z?SrodS=6~Ej!1M%kw_7yNnYt9WyMO0xJbVYJS*A^>SK&RRAy%8Jw~JP8CjOullK6Q
zFa$<`L8coFhNlt}6LriN1QL+Rsd?M0q9XZ-6b23mAnu=}@Z(UwCE0rv_=v=nQJU#~
zT!7DM_$L0>2q`Hkn#RV)=Ojt8hH5{uhJce1Y-?+KAv-(!F1V;yHC>F|p#JpC*Y`@i
z*M>m#Bwc=sq`zJ#=~Ce}$X5;%?;W7gllM!oHNz+Nrfy74O}%e&a`KH(IfmB|7@C`#
zJCKu;lXZ*kH=qCcpeP3{BzfMu0dk;|T;r8&8ag754>sO|9*?I2!MZ~P#zJ8FfY<A-
z$j!~ojO35*?_NH4oMf(_<f~ty=2qbQe|7E@c`leS4RJq3Bv4XP@)(|(HDMpi8s#{R
zalGCSR^0Uz6&39bw~&yT+HQ>VbKWsD+&mW0B3)g!k^fTvHC++TQqVU=c0){%mF%YA
zI-M@Qwzl?Ze7u*{8_VZVb#?Xg1qB6rola+dXfk?RTU+z7B{zp5uyg3kbc|a7<9Lx4
z2HzCyrg*=@wAnLnFA?~Fn`DWj;SQ|=+g;%vU~5@f*>1nz|9(Y9#S_KF#aST07DY6E
zNZShw3-@3T9IL9TdK6~~VF*0pG$+XJnGco0&yys(agz~eDXi1&#@J5=+qcPoOxNQs
znXTm~!x3Q5%J#K3EiG;9+DnZ_vn8|z?_50rs|6)6J52Fij7%7UyK!fM%@@$4RR0(G
Y2j#6;%FCN>rvLx|07*qoM6N<$f*6()VgLXD
deleted file mode 100644
index 3193a3535f1e390211aa403ad757b10cc9d5a884..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
index 8b83fdc84177048c31088130e112ff5433750ad3..a91ba7bfd66d6fc9301e77f111b3aa21aaec5b50
GIT binary patch
literal 8478
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
M07*qoM6N<$g36?RIsgCw
index f721e5057c8a2ed4302f12962130e75150b3ff3a..10b7994858574011d207310e4b79b165f468330a
GIT binary patch
literal 23908
zc$|!z<6mW88^*J3+qP}j)MVQ>PMus+O|~&%vTaSa%~O-@dgk{ZJp0A|thei4-}~Cv
zwbxqFYAUkG2m}aVU|`7da#HGGV2~&O#%gdd|K305jZMM83>oF6#J~F(oo6}9>04us
zVq)?pE9*PDy4zJX^0lxTyHRrT#dau1d<bAquD+uoyzOH(Aw`k~1bfY<PIH`P_?xl`
zggcG={c(}Qeo?AuXi#?1$6AaKc-I&bA_zNe0%boi#m(#bMf!d-1`q^^%(Tz=PInU|
zp@98gkH5eFFDhw^m4Sa_%$OT8{3PU95M~KiD#sbFd*ZKN)W4Q|A@4I+q+}vsVmapT
zj)Q}P$T%_7HqClhKjY%M#Wxy~AFbpNzPO1j(VB4Ztz&}4gNSJ9iw@v-<3mfN=p$9+
zS!t7;-D2l}&kR)bm6esXEX>U9l7$J`UlEFr7o9->z!o*Y?-uP{P+Xl$T?Q$T&QBpU
za1w)1m*Mp@Ovc<-@90Ax<7EwA!<6Ve4@;kwMCKA(_?KjmUb7#M$Mr`hAi#e<feLJh
zU-T}%9*~t%(=I*~oRpg@EHkA$pEG6k_M?^BvY$slz^}BRz{J357cO+atEJ`9{L@}6
z=+Md8S(`6+x)K{3Tf2Q~!{ffJsj_k+J1dKzB%iV5Xw)YBXZy<9+Ub$|{-Hj?=}~Sj
zv9+`Fm7u1kW@vkRJMW+Pmz0zgYMfSI!m9)GXC0Bq8EzX%@>7j@Vs$p934$|{Q_lK|
zmKl|)$;qy~{CrVy(J!hkZOY=gHUo9_+u`HmV@8juvSuc!wr5sWR#Ns%CGuu~B~Nyc
zce{c1_ZQmerta<!rcsZuwiT^gB2N#G97|HO6WP0w5t(0ud(T4aX9>cTqNJ9VmX!0(
zG;ugotrah1<8NOv?&1_Ur{@cOeLz}PR_Bo8G*O++%d4xWa?;9^(eS+1+JBSmSW8=O
zZ|&%K#b|vvS;)%DidhW6vWyi<T4no7gjlw`ylmjr`9b&-GRqm~?Cgy7{PI%ZTL~oc
zflz!&RGu(W@21le^*FyGK0dxXSALwfmX?vNqvOT(OMvs|=%agmbtYK6rMI^?Chl<w
z`h0rR&kjy5E)N?1o+3r@KE$Suj*gfVFkjbuwMcmU@@I8Saa~v}P)@<uvRlqtp3{n&
zb8UusZ(G|l!H0p#zZEh(aO_g89ZE_|^GeTZl{+XQeM~;@93CAxow`EQ*Vo5pW@ZM>
zFDyVd*?yzxv7M(H8%=s_I@dom7bcnE7fG)*jfw-wZ}N&Jmdf#dYO1JAE8Bf3WG%@B
zwnzHXFUBnXq@^{rgBiNgWw6rybon-a?9~=nS~BptX=QveJZd>LqW^v{ke}6?K!f_D
z9j=~_`V%nlS#cA386<8d8xR$<y3DolvtEqV(Z=Qk^nUu_w-bM?=TO_;{sJjdR$qT3
z`TC(=75-}#IWYkJ(qCfaoW?fKU4jDp^icM5cXwC+wt?o4NJB~Yz}s_%WP>9z6hy6V
z_43YYS{8+{#HHcb<mA0#9{bs~W#Y%P+;M9(oGV7GY_JN?&*)t9H*C<RHc$42f2J(0
zyl4_mxtwS`DzexhpSBIMPp(mc;pN$xzkbV_JWcg2^U>WK$Cb*MzznN9A?`(B;Mm5(
z!d*>OCRTv1_RdNZ%X?Z@)m;f<$<+yEQ<s!#NRYL&y}kd8S^ebLkasRudIq$>b7c1w
z@aLJp%;q14M9pfmXa+4xW%)D;{)BRcZ&>otDLP<}2<TvlVi08TIQJ50xhLJBP~Z?y
zm8;o0L#3BSDV`X<yha=H%k)3Gqj|?JpK|ZE+>MTrtd6JrISZI?bFN(2u6q2hHg9)&
zjVH$_#iahJ6&4nQW!?BiIM556@Dme4=~z0T)p@hc)sm^$H(&@c)P>}^Z?cgvY_<tx
z!P(r@Bo!Vi|J^^x>t=trzK(}DWp|M>=g>Qy#gJfvV=vDf-Q{xBssN$<9!A(c80?8<
z2i`Nk#d$~MXj<?}=y&KL+bpUFgt8d3n(~Qza^6GbIdxB`|I;Ne@nf?%tRqVT7!x<p
zk{Ujr7m}Zag0vh~1)NztcEeNjWlYs5ZQsOXBo_an_v6CZdiRY^5O`!m(k0kZ@KadL
zhX{n%VM`x&loN8pQ!rh{pItuR*}Y=qf1&%xD%XEYG+W}kt#IN_IXl(6q|)tMGn0y<
z_KRnRzc4ZYcM|*bk9cf<Ch5nCY%!p{dO^T_Zy<c7F6bp2bt~}g0#-Lt5#klTV0{M9
zLp)I|d_d)5;ZBmJ<ct>%9aZ%Gx`xoX>xKv`pnF&26)y1D+oZ@l^Myjfw5{)j?JP~-
zX-_9G@&{H6sCdTsf!iqn*P7>aq0EE;HH1Y;{6#i$ya7sz*`Tcs2K5yvKzep)lL2)f
z1<LcUnS(ps4s)+QV=2%AZ2$alCtn3{a<wzp3<XE}=d$WGl|!Hk02PAHa{7Fq6Q_&f
zox2~Fy{)HnGH+DB?p2HNF8472BGvBID%Prz(>sQJ<icMGBk8!t0riiwg_5;`$e{$U
zIb3^XuK#rR`*jZ@*TJMs$Uv%)C*$)OXRM-ZrXZ{Sl)AC;&LJ#mfQwk#Af#D2<MWFf
z#_SRZcCidLq9(4eHDjSU{&LXgIvR^Xmb&ho^8Tef4=~U$LOe^6laFccKXfW&&y25K
zG-Jb$u<VC}i`$yz85Szfbw*0~n8KA=Ee2mWBU{iUu(Y!ynrS(xghVM-!V9q`md1Os
zMgs>4D{+2_FP`qtCP8Xb)e$T(_rs1Jqk{?Jds)~TL2AW9W3ff)$s>tFkhsoR0X4X5
zp3i<Xt=7IfeF8QFR>4J#c<h!kEGkJr=w0;9*~`{dAnzdEl*9hg$J-Na0(m&_aymdU
zSWH}9>kNZz^dB~V(&jBVQ^H1x;7(whFVE^kwava<RnFMGkU`VAG4u=!3f$vhBIfvk
zUVN=<c6TOf_i7#q11%ZBFeao`WK%sO!80U5@2;2GTYjKLP=3%SMNjVt^lLpU(07yK
zxLJMD6xC&VcBgOxoFeL*%<3SHPO?BctFcjw!&(DJpi#5k5{}T@s^0Pi5k*r5xO3q9
z&1j72g-9&-Gy3u_-*oe@$tj0qg_?yj)%fCpmHBwl=41Sm%ga0jf?M|K!|n{wi;u{1
zRdXTM{GOhENq0Us++!91J;MFU8ZNnlSE56jZ-<#+2?7g(Ujo`?M5G%&$SH=EF~Uu0
z@zXvvHMODucc~=$bD2N5+X+h?us_Tq5$xAhRAk1I-&r?}dNMh-^ab5;?UZ&~p{%v@
z(NGf<dskvPlPBz))s2qgJ3%dULThnbil#Ty>Wbprwin@ac%$p`c)r%!&zg+8bu%l8
z+T1W{c{;5UjDYwkiQY}Zz5q?(=g)!)ZWvNg)#!qW+)f5@b{E{z(n5DY5tuo-<}%E~
zGtqqoS0}003RC$@DxdJ)A#yx0>iMtCq;F=}9kW<HA#81!J4R}Z-5Dfh2`qUIA^5e~
zpA4yHZFO}H<Fra|E~9Lu!etPQh-Bv7@bK{6i%JIcefM9*Qqn^s?=4S%34SeKU*9<s
zCeZ}4Ik*(?RJh1f0%1Q7PN>VOc#&6|n}0pSez(&5`9L(J&q~k8s2GVWi)o{j4)uG=
z&ztpZd~z}tzF|ff<Bz#8I5;32Z)y^(4&7Zp`cw0^vG+DB=A$3(BodW0Pb$lA#nAg<
z_v%I*0oV$B$(L264GZ6LLO2n9-$RwP@3w@@L`jT<bjgPV2gHwbfRhkLW6Tn=Yb&Dx
z&C%m?Xb7wU+q)9IjX62~SR-|?BT<=*Df7EcHlqjIUEXMfxQsKgdIfNA`*5dSWfP5|
zu9NqnfgH6)VfU(2d|`@w*6+eXQd#)gRYJ(3U_FScrBA!#MnCJaR--X#Su(h6=R<t&
zj)!jr{{f!*Ap-5S+~2Wj_R5Fbm#t$jFO<IRa}X!-{^2OxncjtUAZzLRh9WLaV`Dtl
z`##YtG_hlKLWl3bFirl4b*?SQvOJEY_hri2Xf&hw2k9RoDE|Sv0%Ndg7p0e0X+BW~
z>1&z?ddd6WDDR_Mo$oMr${7Z@dy|eg)S;e6K_XcE$g3(24h{`a`|4y1I|8&m5s5Ts
zxqA2{DBt>d0I&B-sh#WMa0l!+($XVn=F{XcAfxE%4lmv19m}_C4qKB3-NiePa*3Gf
ze9<q0*j9gL_eX&Ktban;cHw?j)4ZZ!e4@<dR;PE;##`t`Fl#RP%qnw6qj~^uTn3w|
z1b6SCPM)2kqveLT0T0eeDx)?{pPi2k3l^kUF^+sol#xK4e>t=VaV)wA=W+i?_?Q%G
zsWCYfm8hA%!$J{quOHl~DHOAT_YU9*d}__Bzz9OMU8EJ<P$D}oy;7-ro1a4aKxN9i
zwAeRI6UcJ99t$XCj@@}vOlL_=AgOct>^W+EU?;~p^IG^Ni<1}?78WMO_B~zd{&%oJ
z^&6K&zWaj%?!+x|+>-WyN5O@b=sP(gL<bblllgQ-J@nv9o`K24uOWLd$Rz}O^&Epb
z+Up>ymFyemuSe$*fIm^b>DLHZTBWX&LAW}_MPG(dn6#||8vCiISS&BuPXX~*s}52N
zhzHS+Q&rZF=TXs~67F>TKL3pn{&TW|-mzw6+%Mwiw@fn7eyREBPvzrP@#LMev|nmC
z9tdfI$RFNOOl-V*@jsUV^s}rfW9mF6W@A+|5Byu~ISYf<{X(9Vy)S)QdPZM2q+f|O
zi7R@I&+2HA6I-tH4ynwMZ|<}!vUsPXJZan#0#yry!>Bn3jX)A`^2Ce0^Z^gjlP~#L
zOKS}c4f2$`8#rX&RIuU)22pPhkD)>7`UFdzw5yY?I~v%K?99Qv?uIZV4lF2W9B^TX
zcEwkEkID=1i{g*I@NdO3+;y?^h*|Q|^EXdBK|$nCmuh#RL2s==Z$edafFgUst!-Qs
zA)9sJLdFII90;&)NWctG!tAbzii$c3ld$3$ly*4oc<#$Opr%s$iPz#OEf#4?|IUw`
z7gA_alOmF<CyM{M+zxeLyq_EwsY$L@d3U_8@7&!yC$1ws)Z)02lqgt;%~ga=!d+sj
zKSY@d`GQ0JJ?+&HhAU<yCj|IK5~ozCqy3|wmROLoqlFglWQ!9{W4&KMc*CTPa$43e
z2XOh<PwfM5#p%|tF3<Vvh&6~`Lwjla$83==Ua!bB_3N^bhrh-oa5y(NH1Jzkv2v{P
zPaKIX=*|cwv2@R1Jr|lya7tAPKLx})B|NBmPAkkhM~5Nh^9i(Jik%TjtbrGCRNiy5
zf|rW2JiCoL)4`F0(RkL6?B18`b|}$7VqeO9TS>yFW`P2&Fq*+?-?X<oKOK#?MJ@bU
z#1Qu}oq0&KY1SL!ZvCN-gv<cp{LTvre$T&)LKndoeOBmw-%Ubc)gK%h$Bhm<W!Mk~
zgIf^g5WzBK$lL_=1u;|TM1s;s$VuH7BzQF}rgMLCn5Wp@V-H2pd^@4J=Et9rf%JxC
z1^<>Y6Hpb6vs~EW)!@`(%Bf+CUAIa|RxoOhT>2s|uBS(EFeL&mGE5>ceg)y<9J574
z+C}1c_$M)-7C&+h7%KTe1ONJgH;Tem%Uu^puiwE4@rpt=1#LkPo;c{4kIDu2l9ew*
zuW>7sFL=>v?pLm}hAQ$*27^#t{nwn{1uSg`V)-m_xK#p4iM<XTNs>23I;PEfKu&ZW
z&kZ7cX3YPx9fTLw{4p4bdIghTFk5y#gri_m#G8E6vX+#Ur3W4#mdZCnQFu-apT(K<
zrQdJO6xwhUAbaIC$2oq~c<@-Q-#Sg9%4Rv9!hs1hN~MA^t?XbQtyZz)R@m6cId9+(
zJ1mYF{$4B_-!uFnv^dcI+&_4mH|bT!x~ZiEidpfM4O)S0PjeR_UPapLI=K4!yrRz$
zB@BbUtFr9Y41>~($11IXJGlnVwuMoafrqn4Aok<E)I9V0F29t0-PX}bJ%20vhNLUR
zqhP7r3&umc-S7J;gk}kB48XBba#K=L;tw-7w_()CKD86ozR2#r0XIKT=8Ma|#|zk&
zMl@tZh4E`8_RM_}`ubg2X&tJ{$BOzdnbg2mH)dN#1tOSsFZ=?Y4dKoCi7cyo5eXIk
zPMQ0FO6g91MsW$!Ipr`UJE4%W5(~LBCAPpeBb$dsSMkaMNA!o_(&ekKS2?@hfNYAc
z*dc`h2lTGnX>458v*IBBrQD0%O4NTtFHnKirq8HgdDs6n^+9vZbRBkG)e|!ip(;Vt
zT+d<&Z(B3fh!@bJsiTt-9Jk71rd5@35EBZ2`u=kUYh}X`PxF0iy5oMM#(iQdJcO(%
z^<n(DeVF1;HS98S$OD5$iT88^%JQ&)WI(~iBGHSa0Mxx9IofQ#6i4YpS|~SR`Eb8x
zLA&ptp>YPQn7g+8UF!wXH1LR<+@T?<gV$_Ih`<c|Jx7LKhF}|Kpt>DCG{5LUDh${A
zJ4m!Q%u564`966yxqThX()otFCIKrYJ5C07;UX?}g$)phxDHWR1|<QvQ5{?ms%7aZ
zLX4jH^=O67YC6I%UO)81FTzB;p`%fJ#}C`2P-!K6m@wV*kQWcmg_(JKV1auDTMLKC
znv7WnkkHFCLjJ;&_r=Ms3rx<<C73N&O5WQ$=5EH*B<yBV)Ivx&5Q(eV2cNICUw*N?
z*Z33f`?$S2aEe~kJDS9!20gildTejCX1$KZK;%kAQD%2FxU8Y@k%nxBl1fs*H3@@?
zs%m_A5`Yz7hR$~*@S)PUo2s^}5NA3*>heg>#<(Le-e7E)HcVA)r;z6kh(KAA;jVX}
z|0AK>3m!DHae=!bu5{~1^|e9kH*Z$;i=|M`^HGAnGt$uzebyBwg4cq=?EU#>cwZg~
z)q%U`(9RLtUmw&*MTQV^=yw=!x){jj--)(5PN;R*1JF<471%`HL==J(g6VQNVRcG~
z3N2GZ#!5H*TXt%m^=eeu>`#)X{cEIiVfaM=;*ikXDB+J9u~xV<)O5$M1Z)<a6z9nE
zDp&$)E-TgSyOXEhuMbYFCiKrHA|XF;j5lPLpS!=7W9laG4<)O+7OnFEJh(qNf9|ww
zICWN|!o<IyoSabeWz+sv2`%$*+S%<8d_#Dbq_sik(JLV!69wzHkj*gMyiC!u#M#8y
zP7F`}YK=*pS+KZ52nF^iIv!PAcs(3ZnC<RVR8(aC64}K6D=0d*ti<l-DNynwFW`2p
zrT4Y6x5$3t`>qJJdOJ?rk6BPBP(G@g9+R_|+~TeZ+8KRbUs?<y*a>bEH@34AcP}T~
zqjLPX$e~i2uXk+9ezEWVfdP`~Exq=k7f0?Uw3)%a>6KO|y*+|?D0aL9EWr+nqV)<)
zM#k<yF9RNHR@2bkJ<>Wz+MFTWTX+S#S}g##KC`M;&66htNMfqbvupxg_vfD^vmOb(
z;MZ=Zq+pTY`3GBvcmHd$_pS|7bEMjriGKz%y~Lsl=9aABsgtb08547L{Kb{rbSkvX
zKlQ<B?wM~GrXVs8vPd-uW2mF{{Nh4+-wz&k6=%aEl;oDonnH}uDi%BUB-pcKhYB;a
zu0=J(kR<^XHHL)^?|g!LuY}aiH6O;$OXqFHpx#@@`nt^MTGEQ*Um%)DTlijR5c@FN
zrwaTMffIoYEa+Lucrx@J!%>Bf`QT{VPd4qLI>!p_HBA7h!JKP8FH7So=$)FfQ)Pxq
zhtlVmJC3`t^hkWHtfuy3_u_O7(g>@<dWC%xyMk3``dBlWw#^-+uFGj1tN!qX-Dc6K
z<`UbEz-Q^tNrutrSzOx~){dN@3>{~^wMHvYRe;aBH1!yZoZb4St=)=p*0s7JkUo0E
z3#6lp4j|kPyeK*H6V()&RH+)4q2-0s5mmJ5HN4&6ba=BCeOn9IB>BeNBcS?b+AaNU
z7CtAJ$59XEZI3}XZGM`3zN-GC%Sk(+7d~?m!9zIV%7Xte*Oz$3Xhc$8I|?m><yt(_
z0{E$Bs$RDEwcYLx!kPEGWOgMJYGSm6T4m%vgE;H!@FgAZlq^Ul#(FfB(u0A1<Fj7b
zl5Z>^HQ8L7u{Gy{fKCg~+K2tH>Kj0}p4M@;DHilhAjWFcv0YNySUwbJ6)GWcY<O*u
z2KeWD!{iJ3x^+3OV7x8260ZYw#ZZ_)7prxM8FTp0ziT_SGD~E7sY!JR|N8Ps2qaPb
zT0mac;Y^9$6AHVxXtoTyy9+4gSPOu~&svCcxxD4JyQ1^e5Z;)_t7?GkPOcU}X;5)T
z)|bP_{?b9E-b6`<zBldq(onjUAxniFD$5Ui<>BrY;W{LQswHqUqQC#R3KAuNs*Cgd
zefeQykG_Fa=YP}(;S2<lj7m;w^cRU(+EsfFypGuWngu%}*8hXIDvgr$tyH-0<_!mp
zT+uc^qvbo?$%Y}?Y->3yp(~rCY_IX6jwlgb^7JhXngC57g`8>qdSnO#Z28iav&I?|
z>CqYay(imT>^gfJf1l?LivSykQq=tBV`oWSKz&&mtuA}`DaJm^pt4{l{EJU*H<mK~
zcLj=xAR+t)@&eLHOEv{0Wx`nsk0{sEMw7C~XOUgu99A6iE#~l$ltK)LM3Zt<@4>PV
z@dtRFakr(b?@N0fV)RE(3u0JUdNF%zTIR<qqt8}g7p%SSZqQ6-h)t!Wo{Ov?5?{xL
zpG!Zcs~Zt|P2@IQau=B%A1+#VShU-PO>t(mQA3n;I(}EDxknb}qv}6?@sLeXJ8<4B
zI&Qj1i@|=tMMl;LxsEw2JdQo`jk?o0Xq+4qz379IDrhK~Eb&jg8jpFUdy63g<~k1{
zFJ{FlZN}Nq$pa?rqSFg6RCZ^A>GIIgWCW>GYFp3Ogyt?e(PVhre3U^zn?XHVHDcj3
zMw%H2{W#c4V#&gqEr-@k6Hg32E9mKkVze4>iPNp07A_^&3l-0gEnnn{Q{wq{dw?aB
z@F|t92?3#-n!^)S^jow5CrlEOHpVAtopy#po-SidZ3liSEm*qP`%G{|$sAviCQ{r(
zg$?BIwsXJySkQtnPg#n|<dQ2zG%Mm|@$C_5e(9twmKj>XIGLYkjaVeVs!>+XpY>@6
zz=OI-4P#o30LH{W|HBsA5v_)XosN+DIfrtNs!n7k8h(?Grg_YB&%hTqfo#8x%OX`Q
zYlMvcpS|Z=YEGBWo8)#N*NFrKGobt@g`1IZ9H~w7b{9t`%HK4E=2U~U5sg+-$9sbh
z>5w;7e|M%juAKzov&|6Z1-_o*!6#RG_+XO>{X+Mpf=QUWJL&7|TT>NzIV`FIsp{S2
zf4$`UKga%;k**E@W&7{e_GIRf8IOEK7^BTW6I?X(tC$-;v>jZBL3WrsA-DF4VIdZC
zr2@NHwT>bF+Eg2lqj(8M!Sc?~jW>l%DWF5cRKY*5dZDi64SHc2XA%&lt+$oUwcc`+
zAMD)ob>mAXBx{6_I!{|zZjiqeX+~Bc2OZB_ao0-q@6|k2#Ls<0hq)U_@>oXc3d{k&
z6l{$Y&fvy-xTWp~>0(jJ^9cb3C@VQfQSE=hwufJ|t-?IgUpq<Ypdq_|b#)0Xg}Fcj
z>8e<DGdya-WyE@MeL^q#Ut6?5eFUTj6V}Zpm+AURw%g`zPGN)y(gMsqWLxu3SOrmQ
zoByitl{xU@{$n&YWCLV1AH{}nvJreJo)G?_PVsc0FIj-Mx{I>O>k@tw==JnQ+q0e=
zkE;2l!y9YEtOwFEpA@*hh=WBT+6~NMqMia^%)=Uj5Eil_z%$;2cXHg<B{%kd<c-~}
zseJW(TsXIeUVVu8v@ZPjBZ>D+t>*{ao=ozpPROf%7dfn!x%-sKkMI$T7+zoJi}hy3
z=bp+R9H4SYNBYz{`vH@cimS?xOWe7gozKVC8M8?d3*V1ztDK4rA4>e1eI?l5UL<&3
zYd@db&9e!{aXp;d6DBH_pR!KDO1C9CjEC@gi_2i^9esf?XVX0x$p8iQFRgYSYdeZ$
zM+AzEDPTC;bO1G8Z!*c{liS|xkZ&LQo_i15O2#VPLo+Vl7;?lAN@WGI`^1qh&NT&m
zi+wQCR)yejgo~3wvNnH)b(w@;XvP8&5GvZqQ!2Yhao8KV5N%pfQ4utzzJ!_*MMed|
zNJR0?6T8ugL&zb<F#`Ba9s#T}GjZM#oS1~qU`M)$*b)tev#kg4uyWOr#1`HuRdGnC
zy(R51A@ije%)bp3Zpcu|d96F`-1)fVZzH^`IYbpkLl!y?wwZAYz^z#a>7@U4xZ!k)
zr|hwVcV<s$o*QbWwIixb1S@T<uXly%LLWgZ-(lUwd1L5C!W*pNXMe~H^%Zp{SZ#GC
z#b4<Rm}2Zg(vx<F1(L*Ic<07HESc8B34@ad)r?S#5q{T>MmZp9_JoNv!hS>R>$qyh
zPsRany(vD<VhlX?=6TYu&Av~7K#@!=7_wg=_+$1_r?xy_P4wg5YpCp!Vj;Z2c*TCN
zlvGCS3K1x4!xmc=Zp%M*H$ok8&4yQ?<)fYYIVk!*$Yc+v4d#=jEx6xhOVfMs*e(N&
zUkhmlN3!ff&GcxgZgLFEHX_ZWdWODZ8AWtvmMckKaPWH+P-I;>`3ZxNp{$p72byfd
z-?Q8s2-j3O=8FPk@WCxZ=E}8bn_h-^ZLl2>T@og-Jaw)?I#ezUD0)XU1O?zK^cb$^
zYjvc>gqf!97uU9=^r~trzY64E(!?dI3$2;eRfw5QRg35Fl7c}Ffai{-o2Uth2jBg-
zcVJHxq3b3b2Xm1j58WA<?TKm)elh6{p(@z(2DAr7x*ta4+Ng<Y!%{}d-3;6{$v+&b
z<_Yl|ca8a&qzI*uq)^<W1WAZal%GrPL*{E3erx-?<_B$*J$(tMe|g@P?~Y(V3X=lp
z+DW>mk%p|}fV<BCcNd%{dP<)z8J4X<5&U&Do_^gq?l>I$hN!X3uZu0UIs+s-U&(D<
z|2_>zVus^CbVvgsEtis~>YWZ2c89qRcPf??>g&Z+azGHp#is0;wZT+9#Q@muv&L)W
z@(y#iCHz$dQ7FJzxk`f`21T|_O;x9|v=z7Gnubtjg5mi*%l8D;rk$L5ZpIepQFn$K
z0u4Rs(L5K$xOD92{*9!hcOwAqgm-}2f#>Y|ng@}B*QF~fxRpO73X7HIv|4v~(35x2
z=vfg9_-L?^31<RaELeno;T48vsrHqD8F7`OC(jEZFPtH)ut8f=Ft(qlKGF&Pa9s~6
z8H>W{^0a_5VB|tW>ovCxwkH&DovIDA*W~g}K7xtIBAXk1T_5*DE(Ss|*6y08$8%>|
zV8u8{I&xFmpWT2#-IEA<jCGZ<|HWom4>Z2$nAB1IGd?~(_g-S7d>$Ec>=?TTvDx1d
zQSah3Z<KvsJSwxM^0)+dx{f%-V!0a!94I0jL%`MB>~n6?9i?g;)IKw~^wbv%Sy5%@
z+_v8#fyqJ!+=n7vY#aond)C#|(IRW?t8RAZSGIGK3rU*@>Hm4w4|iAHgMPLvukIyC
zb`(iDs4OWEz8^qjT_hmik{+BSNH^i8Y7dHUbAK0t`gp^?z9Hi)^;M*}f60iy6?LB%
z-9o6>4|r7|M=uZA?aYC|pI9>lVQuarY}I(${GEiTZCYsoveZ)d8$@oxBiT5x2tTfx
zZ{0(1<9NcSu!hiPhFUW_Y~*annAQf+iW52rJrydl?G;vp;*GUH6NIWD@*7NLQ*oB=
zxQ-H6w|yKkheH^ExG=l^`rv4rs)XW}z-Kc^)da&BkQ339tOh|)jF2eA_{+0&l`fod
znZl#gO&vjA!i+dbzRgj~*u}rvbFk1h&V16Wi}!9CWkkRm)*>&L`=leT@(H^X`VL0-
z(Swg|nrDM8n!Lkw3ocRQe96@>>ko->y-LW{RwpN*oN*kayK@`SOJG#Rwg((H9KFla
zOo(lXCKeUR9;h*yT2&CL84myMly&@|;^5$j1i&2zx*BJhUpnemU_gjbK$W+Eam_z%
z=mx2|An9NcUbuYWD^@p>4lg65I*aEVOMR+1E2OaYJnw6Ej1fq~E=T54c@Os~IfJ1a
zN`Y%V)X1uj8uBH`Di+%j2H=hl8UDxT>v)99lVy7|2u$86MWPb9d-m!I35{4CCJS&_
z!K4SB0`y&-alKEh1~i#PX)xdO^g}Y<`}0=jGuJufsdq0|U6tK;i6gCah%cAj2#f(;
zC}c)>Kbl=l^zq*SVn6F<x>Q#S-kkFNwZBG#-epU=aFjB1A!|1VmC=$9M}jm@rrEzh
zU2zRl%Su;_+BHg?{8XE0v$(8AN)uXCF8XYqbBN&Nfp>l1o7-^?81%N5mvQ8KpeJ`p
z4jnR(k9!x|7GT+&?12zIo=y8({N?7Xl4ylF(Oma)BIlqiHV{U2<l1xOJC9P$A~fRn
zP)$D4Di&Jp?DgOjQsmGA`Fj`9Ye=|L&w$aRgaixPIzls34RHQl43>z3AdV;>>F#e#
zn6Cy`#qYWcqLJ*2V`+dk43H{gSk;QOyJM~Uu`uv@!|ItrBHw$0@t7f-giv0r$hEiC
zLbx^wB0$ug``dJ=iNe{nOSfpm*ntU_17}xsLdg^Az>z=y)+KT5j+RNgq8uB?DcMZx
zx>~uYGC48(aS61vfU0-jL_)we9PaX+Ih;HJqLE#zPmS51YOLupcOgehxQONU9Pt~P
zQn_T}s{x=BayYQ2vwf;N+qSgV^ep`ik<$o~Rdaa{a`w`}Q&J3;eGkUymzQ1vH=%08
zOgi)yn<@`Fx&)0W7xN*;Y`U&Ec<zkONb{qGxOHxxdBGGB2;)ar?7@i`(@B7e8KO)a
z&RjeYRroqM2_0vZERebnz56O!bx*518PMp9YQ?(JvVPJR%oY)?2*Vfz%JoJ$sf&XB
zaq|XKXO;N}WS=L2*b@{ToA%deti|EJ7B1rw`QHl5?O8t_i_j4~4o;k~+x@u!{^wpz
z3%@<|V5!R;-bP^YFm};<eU~j_(yrjC1DE^OJt1_q<bCGS9OZ`otVSk>ch{VU=J)-$
zzJ;>-L4D8x@Uyb!*k$^oYJrL_=}`S5`%~{Upnx6Z*8US$4=_}+VZXw!?1x~zqh9q(
zVD1j7AYESQ=KL4A$Y{$tojzHI2h6H%7sO_r)>+pZih-Q3)AmI{-c4p^=9&{7a;&|e
zZF5pAiEQ8xD4O=B!(chM1d-Z-_tvko-p@0nf3|zq?(=8H>zRaSYL=U_0g&vIrI9vU
zDy~nT<n3)tZOb`+6RadNe~jZbm^8p9)EWOOExVJcW+Wu<1kGM8J*lN5&w_anSqEFk
z^zV*e+?1R69zx-1{&C|<@l@*WH=92`{bK^bY;DpZb)G>_YA;jW4ujA^wa+aM<|SMq
zRm-LkdEOARK6kK^TZe`o*ldaQ_XUj2ek<2Pl|X`f_uh75+CMMn5c>mREZHQ^#epvq
z#?vsy8%(-xu*a6H1K`FSpa8br)jU4eea+d4Q1#U%u{`_l25$@fI&`DC!u~uc;LlT8
zlNRg_ibQ6LaCoVveNZ~f`-x=zI^4;td1sjPVWy@m?5<Wjugk4^UgxXz>hczs-sG8x
z@Jb=;HXo;|L9YbxK+?DuxlTCCF2V*JT8Zbs>0WHpkY{8iPn0)mMH4q_#*jUy)QaxJ
zb*t2izE+u9*~X2o;cMTB?F@~WN;Uh|!JhuA$2m8r`?Y|*nK0Kg7liJ5YLI;=oN{aK
z@MI&0^1h@EJ$2xa__gmyD6~N>+3f%1WJT%xZHv(b)v}M>00WDn8yx=ou`N>?oINm<
zZ|IS%f1fgT1_g>~!uz@AFWbY?Ytg9ePom873@6Ej{a`HnPsop4hLUon97H*Z*bb&5
zkt@NgYj+u@NV2W-`kqMt=q-+!Jhk~Yfu5_f?aO~(X6Dan(%~FZcBS59`kf}rDmcGK
z_6Y%-6?9}JMa7t4#BB#SxoqalM#YSH$Qc~LLcr(f+!w!d@wwhyhSx_vIF-)mq-oI1
z0g~eZ$izAH<)k>?Xi)gzs0(N5RLCw)w(DkuF#JAYpjo83NklbYB==6+A$dj*=KF4y
zq7`vf3^`+YC(v>AzNn7ju$EMb4`g_8dwiPAeIq=SRGZ2c*w_^&h4~SmjEVRf!`h=m
zU-_gZnkND4xV`OCrvHZJdf_0t;e-7zk7ZAF7~swx(OhEuY-cSv`z?7zm$Zu~d*YrL
ze|sfxn(ua!P)e;oLwa3gsFpM~f3cNN?&Xa49h`MAWOV!T^C{@d8-6gD`p@U~Q%@@$
zH#axzp2V-~!0HkFC9+y>OdfMAd>Z2!mTP%f(XaPA+=`(Tb~^^4OoXGcrpfvIhra2Y
zoE+&C>5!Zm6cg8sSySttrBPhElF(Mi)IAH@5o_L9G}go#=mYw2!3$$>;~wK}<cGR>
zUPZpv?(}N#%UXzUsi~=F>|@GiVEe&3ur7vZ%jACP@O`zvf9pklW=UC11mm^Ra)cpr
z$^fPhM~bNN2t7JDs!$<oy!=dnYnT6251N1+OqqLy&0tyWE4BIqDmuosY<TBXENc$y
z?;5;2w0>0bOcU4~OX2Vy`0dc?LRoR4^hIKyZB+@3eN&GaBoIoRnjP!AHM>DWi?~>Z
z_{fwM&iBhuO}k<Q*nR}#u=uqUS0A=yzeckb#nY`&hfxRru%%b|U$GoyqYcPD%S{^@
zU&?*ooq-Z2Kf|_lAmb)XZ5CS<qmOX^@p@m=Tti?C@mg;_N<)86E~68NcNkaq*AZ$V
zZP0hmI%EfvsklKU5iF~o8VtO9YQ|4*yu{_xU}%bS8S!Y3@pbF-ujW?(qg*v8KHlw0
zzV4iDtx5Q}OMRxLuBu{2rBg-Bn~$Nnz@|HF6U@<rGJ+Za?*vPi4AhSF^uqe0QFA9H
zkZ$~P-<DZBiWrg8QN@y6JxT(8qSP6~^UAK{TW`TMqAPHZ`10x|dq8Ja2!||RaN92@
ztO*M{tkxOI74Trn)6%jjNh*2dAP4@ibg$|CeC_UiU8*$_FTKxf<M6~iIk0UjVlmhM
zd(|BbcSch(1$UY^z;CXGEu+Iy$cbtbym7qeGTJk-L5-mM+n(85z4i$Pb{p?!pR1_n
z-|s{xm`~y|XnDCeezc*UeV9}~Jv(y3n8z)p!Wx72-Ek113(xbDm>6e59t)6=vw5BK
zB>64so)QWi8>3$y8$dsr%F=ZH#(}Ca?-?5XMKTEKklLII{xD0+={CPQg<V?Q2n(9d
zU9g!CiVUoB8eVQ!Rs$f$U6(3O{MGK;kaZ3*_J{69k#D=EE_<{^jU%i<24Ng6f5hrA
zg=U>LQzWTJxOx6|{A9UBo8C!uF@ik{K*R=XS{5vl?O?qUF(J5$9BRh1<dgdCkulO<
zKIejh9C6Cp*gP<<7j`H<Vuoi=GnE_d5oG#1-hR#c(GcIdEVDA%pr_U&_1K(~Wuh!*
z{UC^LpnbogQkqXo%wP9MrTcQI*u0yOUGz>x3&JUR&We^vKbZ~-O=-?7=tyL|wZo*)
zH`&`&qF<x7n*lnNF$#J)*@ZKiE7ivfga)79M!TSC%mVdD6L>ZHoAda;T@nwfNk%AS
zin&b)8IRTn;lPMrRjHo8FFjf;%`fkQ>9@hW$Jg{U@(vQGkxrVnVIk7Ed$%Ap!QoEg
z`Zgpu287+C=Za;m=aQU0BV1Gt8)C-=hTZNvC(?l^(o=XBp~E6*u((e!#=4%u`SRDb
zH`=V3EB_=Gg#s+tFTY(@)5ER8Rxw}Mz&MMWX3MMdab{l>I)XC?=oFzK;#po-$Cmhp
z#(ug`D90iHx{kjx>B7y>dw<Do`kl)=qoGDeG-q^D+f2v{pHZ8vb%o6LF9w@dUTdfa
zZ2z^ef;hf^l6MYoaNsv^4|I5+H?NCz>6tcHIC>^c!f5eW4UWFElzBW9X=g#5pq57D
zmSLW?maqp|NW9v7F*tf|iH=B@9u0_ZMZ*&en6bUoveM7t9&n^W+Q|X4Y!iA2{-m_?
zgxDS(=m(2T5xqQOi#Nno@iPC6gv4xbq7%jBN6h?7om}~zci~^sZhY4ftEiC?omji@
zRqOE~;gg<M_GPj5<vrz*f(CccG;bl;X64<`cx83)Z8tTX__N&&Ve3TsQvy8kpf4M#
zf2W?%JV!B*H_x;~;evgM^T&3K`s+yM4uh{9Y1b=_S^{d4ao!b%dQwu-^7uUI*^7`)
zpdJ8#&7XZtrmdD-6DSNut(Y}sf0Y-3>(}G+!_CK-OP(*NpIfhlDM%IU+n*#NNULTQ
zL@x0m)^(V5NoBgFolzj2m9umM#`ey%bc;l`PY)sH>HdHLm3U(YfE(ZXfzZiK|IctL
zswD<SQLvJY$IzyL3vV(6`H<6v{J4(3t(hmE71|`POIHe?6J#&t&9_X2$q(0_2>}Tk
z@qAu5w=SCi_ka{<S8=gk7#~D!N9oYEMSZ9V8(B2h2$$`?5_t=z&_7I~Ro&gW9>w1Q
zMZ7~lyZxUy>X&!}hF274qxP>P+%PYHDR$xJT!EvK!`v~q8xZt2VR`$|uC~3HP+q6;
z3O#MQb80#_!D=03H5j^?-XW$C?+P0Itw0hJbG}wPwgg|J$&n2L!RyM<pF*Tbw4L7D
zi$7h-H|PKaO*p1P*<bShHjgxXeV<|dqnCbKwygtqlKT(-i!f1K?@eOCf_fhBg&H=;
zQ}>=cZZcPp3hc#e_%sj>VZ95-c_NK@p&(<OJbl}YP6);Yae$9nj1XGUfh(1a0Oa`J
z@fPEtpf8Jm1zC(tWEm<Lg>sv|3xd5b-J%BX*4SnR@V%)AOs+5ov^Z|?Y#S}qZXmbN
z(*$;7RpxrVmDG`RoCZP|NHw&3mnvnd2i)ur;~jhT6L%FSv^{`f{f0I$=JEZFm*Xw(
z($6&9VInbU4Xm#sB*7kbh;eLiOz}+zz?*cbfJK@vF<Sb<gEpk~?}ne4TXNvrT`HUj
zD8N6&@#1NMX{jN*Il^G@$|7pgbM#AfBi)(}f<3!=^n6Z75~doDZ*hB{hp}>amDN?G
z5$Si$z0y)s6IoyH#9w`h`2H}WFO51&p578Xo*2>N?<)g}z<t#)j^}-j=Lxz7t0eJ`
z6sSu^e^)~lK!Cc+=FB;6uNGrQqj1t7kl|j&iWC88cS}f<uJGaS!jq^WH>|T)nfAWh
zMCX19D+@Ydq(cl2MabHoBdbb3-r=yyqV<ZY`66*=1!g|mdhrh*S$J=o`P3%4wb}F1
z#9(nGo?aPCp`!*q0E&fszi}ixhm?wnqdj`qK)+en&IvgLSuKs1KpPT^=^Jb~n+e!B
z5shU91tvW_*^cRd^YZe>qWpb_hq}TQ_(oxbXh!(Zb$;|BG7r2choH|R>dII$zSKaH
zZ^8FhsC`ut>>$`@s#QtCpC*%CGeI@lUalBGF~K1Sgzds;!XobyaisBtyW8iy`@o?P
zHCj@XA9W4tN6R|hw+2e@7CoM?Ve;C}mk5z~G@*zKt}$B)q<IbknxOZ{4;*7HS=h!9
z|A48i=-m>fd+K~aepk~Co}BYW&)W%oA^i_;dQ>s7+9w~{Z&BPq{wBeSQIZ%rVo>)q
zq7O{pd&j9JTl)vRhhl(%u``<aSd}7YfQ`24H;n|0&!i#WZ{<h2>*EWYj52&SD=Px=
zz|lVlKCDDqO7CwN1f3;^#UM3?r_culHl4+VVYNer43P?N(U6J5{{QZWjQqevVunFp
zB|kH2N6oq3GdvWj-3@v-2L|X+qP`CszYi;xaOSb^2@Rqx7{oR1r*vU5ST6m*#q6B*
z%1@Dngz)7tI8qdA#2I5`QK%{B-Mjq4^~uFZxg!*7Jj}(r#$%QoxmnsJZAdILFS9v$
z_$~OuQT(MH&Z9vJ=|Ka08`lVdD6F{}j2&bkDafSNDxJn@Dx8vz?6QzA{>ipEz(0zz
z4WaK@%2MB)Ho#i4L;1=vPZM#r#W>I&^jXP|1k<NoaKflvz9Zo-13`TqxV5vjB|LoJ
zKheVg{y4~CgrX=kQ$46|R%#>4jAkZQNnp|+hcxnRIbbVfvOgg^yMb_DYOs7AzP?Zi
z48#*Qi$hZo2vL(r8{_WPv~28irtgivc(H@9Y7b+;|55b!>`Vh~U6UfvrX`P;Ur6$@
zy1ZO&)>p6Bc}GN_S2B@?hl`6TA<}|=d1QZfPM-ZU>)rOhvzu1r=;G{EWR~+rEL>?~
zWjF(B!)4mq+O^UK;;9*jHnf<hCZ^L`8)szY-|JOr{L5ecJflVrx!Vl6^qL;BeIBwn
z_(jZQ1))ZAZm?-j6kxsgxeRV7c?DL&?}{zn7eRb>6IBE4AZ&+OiyCYEv?al#DoFP6
z$1o2NRM~VDqvDHzv*ngiq~04Q`e-K@PJ*7Tc(bfy@Ku$JkT-Fooud2`HtdHV4dGV_
zQ%o9R;8g~@xzhQ8He`YL)5&WdZfaDSf$p?+#k514r`1O6&N%q?H4IySqMmG?d2+DM
zj@9EB*m6U9IWz6^+AETi=~Td|qOfJpF}gxxdVz=C9Y@fea2=bR&$79rIev;z@K6Eo
ztFG)F^ZKg=+3-OgKtr`P53+&bxyBp)ukF81aE3p8CwJd3RI6-i(mXA6N#@z|?bM2=
zn*T;cbLeAa^3M5aB+T^ofWfl>4;PS%@mTY?dbhCqLz@zY1JXwJMD`+CRb(xyL#0kd
z&y{L&3oTaBmFya1c*yV{+n{WwcNb3PM!Im41D%;nPFm#2EqPyuMMzy7)czNhSBsN6
z>6g!fP1Ra<Ya%yzhhh8<eioLWO$iyKWQInFxHC%z=>_?2k=St-gbs=_2oHOI!6a`m
zN{BW}*}{n^!SpvG_8ZdJ?*p8p!tO4L-Uxv<Pb>4Yh=n~a(qd1$z1RFm5U*2*p#^A_
z>F73j(l1H`{#-mfUa2Q%n0=aQ0PI>Jfep`a&zR*)vqsRvmmXCB6@U~IX-28l*GIrB
znH6?gtYW?5%oZ7dH&$oqv-REHcm}hY@%@;$*EUYN7hwh14NvV_osx4G=JP&HysMap
zt6VQ=%n3YIDG*Tlvso>M1$E+cZIC_uG;`=4Y5k}8T1rn*FY2xk-u<<~a6MkCwKmxM
zK-WmX`*PDK;Hj?G+rZnuOb{DS$2|8Z<gq|AS_GR20Ks6~s!Fe!Azu44;Z&=aV1TTE
zx4ar#?yq{<zZm({x%1%!^H$Phh+A5Z2i_WY<o0H4aak1p*n=vMW`uIxb3v<%b9K*{
zZI9~$Mfg%;Kws<njZc&U+={;rFGe_@9h5iF&P$$aDgB;S`IB-a>GHN~`Woh}qd|XL
z8hYuhYXc)%*v>O*vUN{_%Xn<rF7V^bSc8xBU{O&*Du@9k4ko74f~5}DDRo%=774W=
zZ?ryWB$%BU+dQ|rV_%ndqi#XF`>TUj44f_Q!4E&bN>${AD)~Z8+>?0O?srYhI(5#7
zcjn1~wl2)&%jtha{|X#&NO6;;(m6RZsJi;)lE}$&IYCx_oX`SnapdXSc|FIr3>fv{
zEMVazbr19HgR#h{d8962%+&8zerkfY>HYCQP@XsCb;kbm@YdL*GeatfqoZG_HoXy7
zusaw#s7CYD=LpC{_uC8kQ!iiiM36Dmj&yN7@|{f-s%GRMGY9D^#qCa{VT`4((*i{`
zj##jT3s1lX3-5DxF?UYDibZ!7N5Qr}9*HSmToo^l^NiT?*g3IPswiX0*Xco?7r1$O
z5lc%N9_B=-%PV1$RDi@dCz5{^$wDh`tAS(`0RdoiuS04esnOUlZe%yQ^Rz0}b9<g_
zZ0i0Otw;Q3eA~j^b*|ffMl+$VEWe~ZKZlzh3vTt=Z;M_&Cn267;}lktE0J`V?;)p`
zxQq|uyf(<dWP}zlzEMz8HZGE$_2`w>RmU-WwM0&0A+8%na*IUzFI)9qiW|kqV~wY>
z7D<webee35Ru2^#^F|2x>i?<-kfVn@IgNp@RBTQ>6JrNhz0$Q8%ICEXo?qYdRqFpz
ziuQ<FE1&qC(ghwBR_MxZ1NqjuSur5P-$}=dMQ)J7Ws$<``{O7~wo51oTmf<#!2bLv
zz%Ss{hQq_@ikq9;p1e+T%+S0L64A6A8*xy1AS{#9nuC&yS3wmI>Yn7kgu(Mx(6h-0
zb-+f?!0>y1U#}76WM31kO@ds{{HD!-xfa`L!5f{a__>8S?Tt<!y;dERbnWkNZ*N~>
zz{x1RxD6w|d7{ksL|NjDg>FFO{q(f4zD}_gp?If<Duo>4*I09&=DikU0kJ}4W@UeL
z!LgG6mM~7m`2ycutt57+1s7VwqK7n~Y1=MP1-1%J&=88BuAGxrb=2zCLq#MsY<(?M
z2-7x$L5R<|QaApz?a%r%%`bav9Ad;DlIf>LbD+?u4Y!kC0Z>Q}cAq_7=iJIrzA4*M
zjVJNSr=n~qzZHSgQ?lTL^~QuRYMGKN!rou_(Yz2tQDzNXxUdfmD@nJnCu;6BgLQ;5
zJX5KkDFMBAxD3cLgGQJES)$nPX0*0|4PjE5=PPs%i8h;ko+ru_BW%8z%XqqdQBAD9
zt{Ku$R78OwK}#;@eV4_5X}B9`agv9$KKzj#uXAtF@%W;k6n^KE+g1E`Z=O&7&4%I>
zEMwHD!;SL0V@qz;?4E~p)6dM9Jo}hn1Ulv{vdxz91xrLV8;ml7bv&F;>DVLG!0XVp
zjCiNE)%2dP&$pPlUV)1^>h+k$@bDr@-MWqyN-B%F>%GBBldaQ{?V#kQ?P30Gdsq8h
z^A{&>WDZHHoE=B>Y5!oPsWk`1FRfrB=MX}-mazC2$uVyMG$bFdcQJmbY5l^ho_G!n
zwVp)?KVmfc#c=aHOYHot>}-1|$AdU~rc-H*Sk~X7d2bBsI#W~Y*f_x`4tBP-y2|nE
zCd<f7Act1P_enc)!qp#PT~k`Dgm{c)!OrCzw^^Ugz#EtLZ;x~Pibwg6Sr6wdCMPH_
z?1CSX1CsMw7P@U^;2NQQNXy{VcR{*e2-E>F);VS%)i#%ElBYcygFQpg+%lztfwIUW
zv6b&=+myCHs*bQsW^1%(pJ0D*z~SZvfB|2s%^OQRRD%L}gh)BeM1G&GiSW(-uk7MK
z+RfNAw+e~)mZx|Uuqc+>zUomW%ggabAaR`HJ<}%Z38@T-!p{JrSnw5whK37xjk0Xw
z=v84ACaa#Pzc<}IE-*LgviB}=PLGL;qOuuNt{b4j7-u3~E>-1bFdH>E9M_v9mWh)f
zC9b_pR++DE1A?IOMh~4#l<cX(Y@Pj}^Qi}?TJ$7#CY(UHWaQHCj|w&pic_AtVtIJS
z2T|$78GNq$G~4ZCP%htp?*`8345=K6vt3!^6HB$r{DIvvHEXk2CYDNXpiYRtgTm{f
z|C<luV^$A1W{<%RO7pFm$?S8w$OP{;ua<=92>P`iNgDXfGg5-^)|iCj;v=0BqHFch
zuJkT}%yAv1EOm>3^N0!i2L{d~yP%vA`oNgedAo}PKDze#R^|1)apt)TC&L!9k;(x}
z!B&%2S}|wJ@0dWjqt`+rOc@;DL&gqrYM$Ugn{NB`N7ragQ_8|}h^I6~J9U7yVPoFE
zMY(C;DRZrMiB~(ZhB3=s!YrgKx^5*NhYg1b;|gAwG%+FO#C>CvMW`VtFx)0MW5QY0
zeSAA4;&R(17!#;E5aF2m!y^hwE|3{@RU_fXs~0*BH#>wdTg0H#qmD0&FjKBukPo;P
z`dtFSjpU943DbEU!`cJa>EZ+rdy?Ri2P0K!%t>`ZWjZ;Ti4C1TukH)p^jWAoTaS`w
z31J+L&jpSwK{=~@MPLN!GaT}{g94r%Cb^eoedHGf;ix=Ja`-}QEwpjZ54}3}2_=V!
z3+`)cYrBSE*ujwT0dBb!SmV3ID;ovF(Z_jygT`SD9&bo~)t9+0*}nU~PQEfI>No6K
zLb_H$I+gAY31LA5>FyFyKtfoSWtR}??rxASMVbX^q#NmwC1vTZck%zsykDMA&-Ha@
zuKS8}&VBCLnI8vdg+XO|O2@6<+Dis8O`JNaU?0AKO$d(A+8(BDS`94yH>OM#0cf&Q
zC!*z-wE6m&fE*d2s(RJbmqULjKXxd;s&F##k#7J6kDsOt<5HD|c+_a{Qba_=EGINO
z;YqqgrDhWs%(A$ALPcGkhaamtE?hp`CWwod9f{9lAx8h`CkB(_E?Vtj>D*1f?ZLH$
zB{$(hvI9?QYkV!B*$@82Umd^q)GWFG*>2`kxx(SEk>=lRRcKne7jokJ)hroMyzR+H
z^2+;i6g|vSu4Mzf`dUktX8Rrk!SCU1JrIA^TH{r^nin?E6SF5FKdju%(JS#{t(~bc
zck~#i(|KP1neIn4>-g_ktpvCjE`!i)Db5EF5z-yEqg>Am=G2S7oR^$t;k0bvj(-28
zjsE7`|C7LtfY%0LxL)DHK#fGhs|L<$ph6fMd5n7iwu8X52f2J?9^%6Ro2V<-ayR{-
zmziH2($*?IrieE^dVE49YSTv`M9S9{mYd>)QG-D?N>h%<Pji9AuYD0T1df!iWYR=4
zGeGKg>N)4OC@J~eU2gJOdlUT}s(kw9(y=q)qGZ>K)8HrYWUPYj`VP|ghfrbsW#v>F
zjZ=e_-^ZY(+esN%WNK$h?`58MnOev1M>Bg7Bu1kwD~0sY(k+PwuCEWgDO1)vk8S;@
z*Vx%-rP`~VVw*nzLpB{>!(gzwtFx)xYHltDwL)wQ34G-O`7iV@*A@I$m6XA}4S@`U
zSVYdB8~Ye0IBkLc)oAi^_?^=Y`mO7(TL2=Y1ejp)h9od%%vSgPK)mprwQZ7hpDa(;
zU3Bo8JKe5O<4b?CQ79;C_#nqjHP>_hkYw~#2R@_Ot*9;4y9tDrld$IxYGNOOrG%R1
zN(JxDR?o$2zuwG)0e*3ra0pXXBhinqA$zNwO%Tn5GZp$d*485VVhpbff~*`9E7P=g
zFe5`ORz)`@=eMO%`&uV4c|O5Mm7i<N<U1Bxm`F!&g<8*pax<Z`)3$}5VUH{0g#b29
zMoE2$?_`Br`+_+t>^iArNm{d}4gJYnT7&QFH}%rFcTTn^Tv2}4jOlg}yetA**xf*D
zAIz+lr0_x?bY{wiEFBL@*}xMKb~-X$DkTO~8bsldfN?wO$DaYitM*1;CA?+2a1AOA
z=)=1Ye)5M_to>+>aL|wCHnY7c-=D6I=r24nGjHo@^0%e6;&DRwKI!i@M(ercsU=4U
z7Geyv13hLn?H4MV)rt;@Ae=9)UJfMIiZClj^tRE`Pr4xN-!6`mE#sOZqN5m1?$~i~
zE38%WIwAr|%%w2gUHKA3B$#R0IXF@WL_an(y!zSSuHehEk&m;AU)AaG42z(h7l$?2
zvsdK2D)Lq&3y*(eX+^};vWMbxDhxyaw{{&`_h=mn{?;kc#*}LV@iMmA$C=hTWaDfH
z0nKe{aFc1RUv(upm;N*U5xkVmC7mMmZtc%`hs}zc%Pc>YSFH{KV(HLS*GO~b)K9EM
z@&woN(^MT2;aH{SzPG1p8(AkVN8ED^x4IjpvaxBMi9fEiavh~>ggvwlB(;blWyR6l
zxkp4iwGz_PG5Ke@3BVmSVuSlPf}NMro5S%vXwKuif#3p#_Z3f~<8U+?!*OCLdOG7P
zG^dwozAQ4VET(<!OGUmsj$3)MwB8=QgnQKvN_608#<9B;&Db6>JR4(Q#1dXs!5=&=
z^nhq$i&0ZXSkm1QWD<=;nV@{)a@-?&Y+XO_@^h9kQ7Ti;*r$cKV)+G?rj3FaFlExV
z2`TBHS}j6yOL`z-7UG|V!R`~@_29Q`JddobB0f}gDxBjjM1Q>$V#IUm!gVoXa{U$W
zlEBCWqskq@^-Ajf4h!BdjVhB*;YS#;fjf@Prd*+v!VOs=)h03oZg+S*t|UFBh$D$t
zhJ;o>!a~V95gp2(sZ;zgnCvGC)jsqMnsDI1{yx3%gqVA#=1tOLowOdXQ7~you;(z$
zluf_XHRVwYUIf!hp73jmqYu1H+Wj$Kt6ip<uxfv9q)5jlC)djfAXROxW{EP;hv&LD
z;0_5aYAL~&qR#G~6h_@N>tp;TZO+CafRaX)K>z%XnbuVy{zenBnR%}0oEoN{54DNy
zbr`cV+?@@QjYlFTEw7^F-Lmro@(gAp6M@!mroliEU0ky*8fHZy3PU_U-J9LYz{U$C
zqprji-yDa~nCZ%E2&X|CWIz0>OzNY=;!FcZ8&y@dX1@GlQ((%>dRl#E+EFW4v;B4R
z+1r6$!$Ez+d3E-fuuh(W8tH{D?YxUQ2u1xFr^aeVHQx459^ags+4aR{Fq@w*YrI9V
ziErTrK)sUj=5;XxRw4fh=4@B;#PLMH@YTG5yzl^Hu48E95Gy4MV<IevO=K<c9qt=e
z6YP-<Mco4vc-Ba!lvrX%QSL+@Xs>B%+pA_jWzj6lFDPgjdoND$F8Y%?Lr7HA1|>!M
z0*zcr3nLeQ7NNL-5Km}p5z~oXPe&lWTdw2B$rOERXPYUlO7T;x<bmiGt<df~{#EB_
zYLU|2?-`?9GkUN^Q$_GvSLkzPn5G|h1;QngyR%&rDCf!X2Mp^GoGx4Kh)MV}=Q3?&
zV0iSn8PVx^(DtSfi=JeXK(Mg!$qeB~igqgUjdj_Xeru^zY+BPP1IAw(0&yz|Zi{2~
za&jg{J^OOv;YL{gm+r0k;40VO!4iGu#ssQf_yHT}xNaXP96tdbkJf!^k#emy#GS7j
zDDGH<BqJSoCsvoSTQL6Chk{R>GIw_D7Da6akSoUWKEWz)BdcPjJas~}LcUbveLJ@m
zr@Vb08S&oH(e#VJe#|>4L$z#zCJo%MZBI>2%>kWkG?+=rnYaA&kUJ#kxefN3rc|BN
zqJ0O^ov<>#2AR*j3j2>HL$=)>J;z9w+)M<!UC2EqbiRA?cjmly%e-CBU?@2zz4ZiD
zJ@FOA-zM($=DuIr!%Ypt3o1m|xiH#|w{jhqx+`;x@EB&Mf~tV@Jr8#3S2~t1v=h5@
zfpUpk-E2zA#6^q&$4r7uOje{VvZddWxMJ;c)13BNTi~=+hq+=NYG1C8v2ZX0^Dxqu
zxf)C@54|ADGi^$^#1|;Ia8#3D;Hb<!!Or3*P2I&yI<Y@^liog`_+{Ea=x++1oQ4cZ
z(tUDFa&fPGk*dqxe%D9Ik%Pv5>97pS?cdHrNq72|_hPpI{4L)g*MS6ZJ3%1fTY+s(
z&!`ua4eyLU-7OTjd0>(R?ry$Q5HNjYUaa3!NICTlcc!kQZgm@=Uw<xOT)m8=GhtwP
zpVPM7)p*s;o|t%cgLfxad#vG=I^P?_4_O_OTrnrIBy27JF4jDGMkhxd^YmT8w>`!L
zc|J6)T)!QkE@Wmd3><Qx`RF!XGT=7d+J-j!@aWESg(CR!1*>Pt`8Ej>vC11UA2N`~
z;6IP2>!!!UPth>FI(u76+4D-_FW}w597$s%@o`<$eF$7IG1K_kpg)MK;qT_I$I24p
z*_61=OR(i%N};|~pUC8+Xu|PmHtBvf`ho>B-|}2ilJ7cXX))+l&16_1=<A@=kHf4h
z6kLk}65a%S0WSb<xpLg{TlS$Hg6Y=Q)=yvuTnhVVK1L(`<c9lda1AG^&cNW)#XVnl
z>IJSP($426i;&c3E-mKh$G65{mH^ambh(xUdi%7D(ggmHrn#ohmp(zUOdZ}tGvj?O
zKZn!4c5Cn2y%o<Db*`pDQDOSpW$5^nH!yBtL{jgZzuD?_6!J}d^tD-`YqYqjodxmB
zsKhc6rI{w6(7u~wk!UaCj7gHgX(A)y5%Nwj;7z|u(P689ZYQFxN?GH@a10a%p)@AV
z#Um!NenGXWg+$7(T{cl0t@Ms$NlP)%r_UZ#X0Wy2xcZ8@{JH?83;20&N-Z1t9Tb}y
zFRiBX2aVkOLc!FudbM>{4HKD5vHZ_5adL3bR{8rFT}TKqnfl+5bcCWSr4FN!YXZB7
zCrMIeSfd%)axh77F#%K<DwJ=Pm<X8v#Pa)+bddN5Jj3*Cv-_%V(%d9JPar_I+gOSe
zM8c=%4zr#2Dx(5J{tM*xbybHZwl2-%g<kN_1SQak1l|5VpEBUTI3z8+>m#0}Kd556
zEaScm5Ba}DAN{2_iDVQ}-ceE;KQpuQda%Ey^dzRpgvi*8{|R-u_j|{y!M0pGO}7(M
znJHT7{A@%~wiyly>ik$hqt{pIBdM{WZ#Rzt0IFjDp?6RZlwR71q8_lPD0jvtC@APN
zFg7`|lh*X`Pmh3o#oU*OVE&Bc{rP(5{cPVmh^@Z<bTo6^TcUHReg*gAjiK`VSJCv;
zFFX%+cV@F>3rHUhI_xjS+9G;lMpSBl8P3T3Jy*N6wRNPYw|7KCRkfL&8NBei!mQe|
z9rjnxIsZ16e37`#=l1IK*Wlm+FLU#e3h@@0-eY!lRxF5s>Zs5s9@+k{^{sE8QpZ?c
z?nGWHW<@Nq?bzjC_qTPKxHB_Lm5RHp{Cvs5!BM2Res)`MwzdcMy%k-gSuCT5o+H&r
zVl%<2#9Q^p`JFjF*z39f(k<C2O1oqKDs!kod=1$3cPORR?bZ$f03iAOI)!g8#bl7)
zX24JM1vFw#1<@Fw4R3;NrmMY6jfYGUm$6>qmH$7L_pCPb9H9+9k=g#fniSyNz*DP)
zqyP;qeVpZ1Wl{vbRV43euP~_>R9r8VFda})QIQn#xw+WmF{z8-(l7u0BaEC&eSflK
zVus~a)I!V0`8pkYHTr8oHnd?Mg4vknx$wT|%5jXVp&ON3qCSiVIK{%}$D6i@;`&5V
zpR`;R3SaZSV%9e$HPu1nFsd>Ef$Pi7;Qc$#-Aq<yqm2WtV<FU%LwnPRM`^Y|78c7^
zTi(5G`KuMiwZ;b%8nT)0OVRa*Ggt(a{5TFOOkfa2>>lbsuOun86n?Ao^<VMl#SX|q
zRL7tHb?gZ_w7wo#R)K3l3kI4@#>JVW>JLuq3#MN+0~dwNjbBV?_mKdo6vN0MXA6yP
zY`|v|laY;Xo5*_O3YKq$H1<)(JzZU}&|E1x1`G`Y*+0hklLYVpnVY{gBOc4G-q%5w
z+#pe`E6auRmq;A2o7~7veq4OKPPIqE&hKwF96M=JCCg|KN@i3DnqL(XHASu`?h8H>
zXHwna9!c=x`d%<w>KJP1q35CjBGs!SV+I1rH-^%mvN2O_@Wb|d;9yO5FZ9Pb?>q;5
zX1`(a{xR0k#nV$9$!!<18(#PWGdDF|fSr~@DM#y@P=KWJojh3WB(1K4tK61XHpbf#
zYyna67RY@fpQr^DCffsQ^LZr)R1L)z38Y-}3~D0o7RYDpeXA9!m1-XozN{GgLqV(r
zT>n@zn`Yh$yDSg6*?2!wY1ZrN%<hGisEBnWUoqwFwv*Osmm|3K;RJcMHy@okW7hZy
zQSO!GRhW)(_0C`$?N-bnQ`~i}{0`^tPt(kli1)Is@139eLoGeB8?s>?qx>i2D)KY2
zDbT+|`vv<w^G_l^w+zR<ISg;hGP(VlIu5g~J26e?A-`fPt{pa0kQ#FoCaYCIaMSyR
z^f^x!YuNywEdpA9^YA=-w(fSzv+*^CP{Li?VX(xZuRZ8St7c_oW&48thWvPL3`Kpt
z5qCGa<#kT!oT6q<PELD?>BsrZO_m_FW$<jaRL(y+fbX_YDIam>%seTPd<z$*Z8n)3
zl*p9I02{gKQ1cBW#JRSm4#1~H1lYi8yR$_F?J3W2say1Kpx#_yiF+byXAVmsP>+<9
zR4lBI{FUdiH|C6m;^)4k-W6E4DgBpnwf)G27RWKvvGFPQ=^HWZ6T%0okh@)6+;*?8
zuTSjGRLRoP&@_+P*OuN#y^7U4XJ1-t3yWTHHm!GBw5rwL(}HHA9E<xuFm-GiYf;46
zD@<FFXvB?DwW%a4mW&Gts9xQ<3}m=lq_adOFOalVRU&4ERGpQhK@!8)>$M`O<B-+I
z9{_H;)KRk>dP_!Jo~&0#{$Kq701-(P(X9ZD>+c6s1j=0D0)j|EaSK6k^2=#C-{`0)
zb1U=k&lfTOHthAV;W+!bA0L_zdVJ_B)#S9^vxfmeT7Y#;phftkL*6K6twHAMS{qmP
zKMm0My;&1ET+@A@=oxDGN%6|D;S?H6S;unF^TH8S_u@-?3qjDq6N|#;@)y%D!e5&0
zL2#i{R2xF~hkLdh)Q5@1cGPr5zV;6T=BYAk_Amx&)75^Q_h|9i{R>KNmzc`9JM-WY
zJeDUvD4OX>n<=#`v1^jZsRAX^kLu`ISy>HyD`MF3GtOZx#yWDlS}eaZCWIFTE=_1;
zTHTr5FQ8>Us6|UzK+*wj`nPdU0sR}6rz(9n;p4OzXA|K<UB1J591_><oZ-1}++l6I
zpsuef-;y^&y^=I~w-1*PJJ-f|lmM;kjEoF7=D0O>M#dFFQ@*I9am5OOnOue7;^v#k
zV{0EDpW8u)!_C>@f7P))5SF7<HJSH$y~7Mr+O#aPpIKHHb2dgOy>+u_W?KJY2Fs@-
z45OhBT4v5s@C<7Mn5}N1jN9_MN=izRnx`qt<nZ^8>tSF0x&6(_RFDi<yjO@nqnG<z
zVujh{8d^w52Y*gBAoku(w@km{&dYCT0RN)r{O$2ojorxI6Ed=s7syNInpfx;7|_%i
zYdP8ctnCfIyUemlKPMR}5s_1prMvS{3&+hs({Trt+7A<jz1`tYiA#hA$p`<V^I2LZ
zdPLOyWjjtf?{nrqUzh&kkH24aZcMyb>-R?dwh*J}gq%b2hD9Y|NtRWU<ptdeB!F5C
z4GmddW;@!`tudQ`aIq~A79q7THpa*AkdY)FlP9iKg%dQpd8kd(y)zHCd%oOBb29b7
z7Am)=yr#eJZ;nUG+M6)1Sf0x(D0J#*YoC?Dhsl5cSCRQyTP<HCnimZgh~tto=)}k~
zp5B+IeocsXStrUHWS14cCcP0VIq_Z=Q~}r4iKZS74UvzgmB_=I)QOV0T#uJ%Fh@f?
zCjnI&)ol0@G04D1zikd@O0g|_?N*#;%Iui?>`a!p4cXa@`26EB<^vrP+%CqOGaC%(
zZew@sD<D;k=yd<BZJ>BLKV!_NpO;`~?cTUW_+qD2IHH+VV;Ih<Xv5p6cMI9JC&s$b
zg}s$gpEMG8%ZDrVJ_-+JVz&SB_E#Ja^QR6iZS8S=Q(ruz*c#?7+>^XTr|2vZhiOrm
zqqNbU6y#`~_jYgJ176?01c%nAjy!yLEeU!-aZdg)G2Bh{Y#Dg(ke#r$!w_@`^7G%2
zx|rZj=bvOg%Ff#TxYq3_G<;4d;8GT**1V-3b!vOfLk_|ko^z_2F8m=$10iG1<Gkp*
zdINav;l4XvVS{4y7niDMpWZ397C*sL#88lzw+15@QUmI@ehCN&u(%9a1V;B0qi)AD
zf#ca7qn%VIqOAe5uh=JVH|sv%3AqxP_nB-4(o#DwHi6P1RT9Kh|3)QVAV#N6{G~P_
zBqR~b!hEP=D?zvZ%G-OBQp=9g_ujx(gATQ-n+ko0{o~CM29nr&NlBESVoe{S_`5c+
zMnD!qorfb_;Jn=WN8rGxGzbiXLi<qaU!OMe=Jo4KMQK&(i6J!a(6ST1JB!V=wKWvZ
zWVBnfE)Nb4k~mLmS_>x){>`Z8KOiZU-4hDTy@~rI{imQk<HthS?X|y&#B?%J)MS!M
zsq7HMO&=E0C#S}yp3$HUj7l-7wV%>nfcAICI4L~WBqxh7&SfozSX-}s+B{}eOH0Vf
z3M4?m#ra_pEW?p>QXVo$J%!fdjlNhG*`8&K<$P_F!Qk<(nrm?VLy>ZxcAW5^FkxK%
z_NA`qRgG-tLX;SNXuMQccjfB6Ds|?i00oWNn;L`%Gc$kB3tL)R!tlL4osnk#)VF}L
zY80|ZeJo6dI@AyrJM%s{TTN+{qZ-D=#U02P(XuPEE4E`QL&>?=15TJWn?Spw<mXwX
zrj4J<Q1Id=kNr}3jXhIk(bl3AN&WF#;b&uqC5K_5|G9w7DTM%EYK0@_xCyPRl;Nq#
zU;Da_8>K}>^s;VlzN3U`h$^{Yq39{?`6=k{Wp-`e@J}NaZu*FjzNe4t-;Y@vdwE^?
z03<AtU7@5j!GtuT=nh#*iJBa`WaJ8HS9252ZMSDQ<%{brvO+>`vUf6<?zd+PPd}Sr
zTrofJ&ChLYY%Dy8n1TL_Y-8ObY?1cv%>q#^E9!C|Tb%a%pB2iCL>q^;8)s$yu%L#y
zzAjZ$P5h}bT$XL7|LzD<0*Ft8Rvb2DK9qL+ue7lKTwd?16QdBmP#uLUzTr$sko|<%
zGOUIX&+F{(?0uxz5@kqqNVIW-ixn-_F~iYZe0+Qj!|vofva<Na_oYt1yV?hMsrn!|
zFeD%DKJ1g|XzS{vm~A#h$q1hWTjJ9EGh1l4>TGLx!O^wJ(0RdKM0le^nz}US4ng+l
z76T7`R7fAT^k;KAe#@WMEvHLd%(Ak|o-}Rs236+vG04!;ZM%jt8zm*BmXHMz>74rv
zhLU?9kPN5XL&E`|=V%sz<1mUK<n~ivKAcX^l4QHECc8@AdP9hHSM)~h(ZV2E?`;_c
z@hjkZU|8PVR_*BMsL=(*Stj%MeIIRTy68tqQai;Rq40-uwu1<1C_?(oCNQpAZ{GJt
z(WRfhI>LW=rvWa1&>GRbPCeilU&n6&M88htq-<4%({eRLX@(cu$P6v*B1n(SeQ#hr
zuF^U4+>o_Svqq@D4WH^SqCa%de!~~EfLL<!84OUs(7S6&{cLHdKH&}&_2$xpR*M<?
z0$U1YCc==4r>OE=J@Ko84lf}n;z$!Wql^+;U|^t{ejCPrO~3tdQ=JdEiq+kgGAQ*;
zNVxW(y+>|E00iB0YIPIi4DAhO`=<`Y(uiropd3*kv_(7Bq3HA1-F<w|&W#6%<+%eN
zJ(QxLr^$X*l$<>C9;WN9g)-KxH{K_7A3l6&{$5i9E}I;|CZ=D=MV5%5-kbU7SWFh)
z8*n^1I(d2dLfHN7xrNPpeFaE{gmlinkKtm2E6az0B%XQWdM9ZGX`075=)ixP5)Rn6
zR8G#3vB16C-pT_UE%EQ;5vuIAFQl&0O}21P1Fb%$*;q{Zb|a9zsc@b^OhD|@qnEZ?
zbSLOukKutbK$|hlMZ=TW*7GoPlUMtvV|I(g7<!6)dEiqwR)m|)X^r{hh^Qvo14o%*
zj&MFK`?0SyLrZjfbSI`cH1|~-fdtUgx@c?7>;U*oY%wg27{8uO5D3&?4@cJmP2z~W
zs?d@$b2!OpH8881avov`NtP)Rw9m_)V9Iu=$ksEGoyR9<e8~RY;G@yxd(pR;)b^8q
zFB<3v;mA`&{OvDZJ(nIA;lb-MC7C37ltP9P=+ZvUk<(Rw8iIZztx)A{*m9EUb0vY$
z3WSxuz?&Qqd|@xn_K;Cw^K|9jITY_H^Zy!o_t<ZuHxVpwJ^zO<lfPC}2Uf~k`u{&k
Co9IaZ
index 521c6c3887b4ccd4ad96abb77d69a94551d76da1..c1868929dd8edf012a0f720931d163309cd9da11
GIT binary patch
literal 8670
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
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -417,17 +417,18 @@ toolbarbutton.bookmark-item > menupopup 
 
 @media (min-resolution: 2dppx) {
   /* Whitelist built-in buttons, instead of .toolbarbutton-1,
      to avoid potentially breaking add-on toolbar buttons. */
   :-moz-any(@primaryToolbarButtons@):not(#tabview-button) {
     list-style-image: url("chrome://browser/skin/Toolbar@2x.png");
   }
 
-  :-moz-any(@primaryToolbarButtons@):not(#tabview-button) > .toolbarbutton-icon {
+  :-moz-any(@primaryToolbarButtons@):not(#tabview-button) > .toolbarbutton-icon,
+  :-moz-any(@primaryToolbarButtons@):not(#tabview-button) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
     width: 20px;
   }
 }
 
 toolbar:not([mode="icons"]) .toolbarbutton-1:not([type="menu-button"]),
 toolbar:not([mode="icons"]) .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 toolbar:not([mode="icons"]) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
 toolbar:not([mode="icons"]) #restore-button {
@@ -453,39 +454,40 @@ toolbar:not([mode="icons"]) #restore-but
 
 .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
 .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   margin: 2px;
 }
 
 .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon,
 .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
-.toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
+.toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
 #restore-button[disabled="true"] > .toolbarbutton-icon {
   opacity: .4;
 }
 
 @media (-moz-mac-lion-theme) {
   .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon,
   .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
-  .toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
+  .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
   #restore-button[disabled="true"] > .toolbarbutton-icon,
   .toolbarbutton-1[disabled="true"] > .toolbarbutton-menu-dropmarker,
   .toolbarbutton-1[disabled="true"] > .toolbarbutton-menubutton-dropmarker,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-icon,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-badge-container > .toolbarbutton-icon,
   #restore-button:not(:hover):-moz-window-inactive > .toolbarbutton-icon,
   .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menu-dropmarker,
-  .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-dropmarker {
+  .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-dropmarker,
+  .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
     opacity: .5;
   }
 
   .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-icon,
   .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
-  .toolbarbutton-1:-moz-window-inactive[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
+  .toolbarbutton-1:-moz-window-inactive > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
   #restore-button:-moz-window-inactive[disabled="true"] > .toolbarbutton-icon {
     opacity: .25;
   }
 }
 
 .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   list-style-image: url(chrome://browser/skin/toolbarbutton-dropmarker.png);
@@ -503,18 +505,17 @@ toolbar:not([mode="icons"]) #restore-but
   }
 }
 
 .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
   -moz-margin-end: 1px;
 }
 
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-  width: 14px;
-  padding-top: 2px;
+  padding: 2px 4px 0;
   -moz-border-start: none !important;
 }
 
 toolbar:not([mode="icons"]) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   width: auto;
   padding-top: 0;
 }
 
@@ -895,52 +896,32 @@ toolbar[mode="icons"] #forward-button:-m
 
   #history-button[checked="true"] {
     -moz-image-region: rect(40px, 320px, 80px, 280px);
   }
 }
 
 /* bookmark sidebar & menu buttons */
 
-#bookmarks-button,
-#bookmarks-menu-button {
+#bookmarks-button {
   -moz-image-region: rect(0, 180px, 20px, 160px);
 }
 
 #bookmarks-button[checked="true"] {
   -moz-image-region: rect(20px, 180px, 40px, 160px);
 }
 
-#bookmarks-menu-button.bookmark-item {
-  -moz-image-region: rect(2px, 178px, 18px, 162px);
-  list-style-image: url("chrome://browser/skin/Toolbar.png");
-}
-
 @media (min-resolution: 2dppx) {
-  #bookmarks-button,
-  #bookmarks-menu-button {
+  #bookmarks-button {
     -moz-image-region: rect(0, 360px, 40px, 320px);
   }
 
   #bookmarks-button[checked="true"] {
     -moz-image-region: rect(40px, 360px, 80px, 320px);
   }
-
-  #bookmarks-menu-button.bookmark-item {
-    -moz-image-region: rect(4px, 356px, 36px, 324px);
-    list-style-image: url("chrome://browser/skin/Toolbar@2x.png");
-  }
-
-  #bookmarks-menu-button.bookmark-item > .toolbarbutton-icon {
-    width: 16px;
-  }
-}
-
-#bookmarks-menu-button.toolbarbutton-1 {
-  -moz-box-orient: horizontal;
 }
 
 /* print button */
 
 #print-button {
   -moz-image-region: rect(0, 200px, 20px, 180px);
 }
 
@@ -1715,45 +1696,59 @@ window[tabsontop="false"] richlistitem[t
 #unsharePopupText {
   height: 48px;
 }
 
 #unsharePopupBottomButtons {
   margin-top: 1em;
 }
 
-/* STAR BUTTON */
-#star-button {
+/* bookmarks menu-button */
+
+#bookmarks-menu-button {
+  -moz-image-region: rect(0px 500px 20px 480px);
+}
+
+#bookmarks-menu-button[starred] {
+  -moz-image-region: rect(20px 500px 40px 480px);
+}
+
+#bookmarks-menu-button.bookmark-item {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-#star-button:hover:active,
-#star-button[starred="true"] {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-#star-button:hover:active[starred="true"] {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#bookmarks-menu-button.bookmark-item[starred] {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button {
+  padding: 0;
 }
 
 @media (min-resolution: 2dppx) {
-  #star-button {
-    list-style-image: url("chrome://browser/skin/places/star-icons@2x.png");
-    -moz-image-region: rect(0, 32px, 32px, 0);
-    width: 22px;
+  #bookmarks-menu-button {
+    -moz-image-region: rect(0px, 1000px, 40px, 960px);
+  }
+
+  #bookmarks-menu-button[starred] {
+    -moz-image-region: rect(40px, 1000px, 80px, 960px);
   }
 
-  #star-button:hover:active,
-  #star-button[starred="true"] {
-    -moz-image-region: rect(0, 64px, 32px, 32px);
+  #bookmarks-menu-button.bookmark-item {
+    list-style-image: url("chrome://browser/skin/places/star-icons@2x.png");
+    -moz-image-region: rect(0px 32px 32px 0px);
   }
 
-  #star-button:hover:active[starred="true"] {
-    -moz-image-region: rect(0, 96px, 32px, 64px);
+  #bookmarks-menu-button.bookmark-item[starred] {
+    -moz-image-region: rect(0px 64px 32px 32px);
+  }
+
+  #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+    width: 16px;
   }
 }
 
 /* BOOKMARKING PANEL */
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
@@ -2703,22 +2698,26 @@ toolbarbutton.chevron > .toolbarbutton-m
   padding: 0 !important;
   border: none !important;
   border-radius: 0 !important;
   background: none !important;
   box-shadow: none !important;
 }
 
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]),
-:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button,
-:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
+:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   margin: 0;
   padding: 0 1px;
 }
 
+:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
+  padding-left: 4px;
+  padding-right: 4px;
+}
+
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]):not([disabled]):not([open]):hover,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled]):hover,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([disabled]):not([buttonover]):hover > .toolbarbutton-menubutton-dropmarker {
   background-image: linear-gradient(transparent, rgba(0,0,0,.15)) !important;
 }
 
--- a/browser/themes/osx/devtools/common.css
+++ b/browser/themes/osx/devtools/common.css
@@ -129,17 +129,16 @@
   padding: 0 1px;
   -moz-box-align: stretch;
 }
 
 .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
 .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none !important;
   list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
-  margin: 0 3px;
   border: 0;
 }
 
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
index d6efac70dad72618e29eb323ac33dd6ae817783b..2c3253fe80f94776c9a8320252eac24ffff46c9d
GIT binary patch
literal 4653
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
index 35f0caaaea2291f1f1e258144243c3abfa56d926..b84f6ddadf1a04005a46db5f396e5d29977d24a9
GIT binary patch
literal 9704
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
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -175,18 +175,21 @@
     background-color: transparent !important;
     color: black;
     text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
     border-left-style: none !important;
     border-right-style: none !important;
   }
 
   :-moz-any(#toolbar-menubar, #nav-bar[tabsontop=false]) :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
+  :-moz-any(#toolbar-menubar, #nav-bar[tabsontop=false]) :-moz-any(@primaryToolbarButtons@) > toolbarbutton > .toolbarbutton-icon:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true] :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
-  #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme) {
+  #TabsToolbar[tabsontop=true] :-moz-any(@primaryToolbarButtons@) > toolbarbutton > .toolbarbutton-icon:not(:-moz-lwtheme),
+  #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
+  #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@) > toolbarbutton > .toolbarbutton-icon:not(:-moz-lwtheme) {
     list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
   }
 
   :-moz-any(#toolbar-menubar, #TabsToolbar[tabsontop=true], #nav-bar[tabsontop=false]) .toolbarbutton-1 > .toolbarbutton-menu-dropmarker:not(:-moz-lwtheme),
   :-moz-any(#toolbar-menubar, #TabsToolbar[tabsontop=true], #nav-bar[tabsontop=false]) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:not(:-moz-lwtheme),
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child .toolbarbutton-1 > .toolbarbutton-menu-dropmarker:not(:-moz-lwtheme),
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:not(:-moz-lwtheme) {
     list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -602,17 +602,17 @@ toolbarbutton.bookmark-item {
 toolbarbutton.bookmark-item:hover:active:not([disabled="true"]),
 toolbarbutton.bookmark-item[open="true"] {
   padding-top: 3px;
   padding-bottom: 1px;
   -moz-padding-start: 4px;
   -moz-padding-end: 2px;
 }
 
-.bookmark-item:not(#bookmarks-menu-button) > .toolbarbutton-icon {
+.bookmark-item > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
 /* Prevent [mode="icons"] from hiding the label */
 .bookmark-item > .toolbarbutton-text {
   display: -moz-box !important;
 }
@@ -719,17 +719,18 @@ menuitem.bookmark-item {
 
 .toolbarbutton-1:-moz-lwtheme-brighttext {
   list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
 }
 
 .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
 .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
 .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
-.toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+.toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
+.toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled] > .toolbarbutton-icon {
   opacity: .4;
 }
 
 .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
 .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
 }
 
@@ -821,17 +822,17 @@ toolbar[mode=full] .toolbarbutton-1 > .t
   -moz-margin-start: -15px;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   -moz-border-end: none;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-  padding: 8px 3px 7px;
+  padding: 8px 5px 7px;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1:not(:hover):not(:active):not([open]) > .toolbarbutton-menubutton-dropmarker::before,
 @navbarLargeIcons@ > #social-toolbar-item > .toolbarbutton-1:not(:hover) + .social-notification-container:not(:hover) > .toolbarbutton-1::before,
 @navbarLargeIcons@ > #social-toolbar-item > .social-notification-container:not(:hover) + .social-notification-container:not(:hover) > .toolbarbutton-1::before {
   content: "";
   display: -moz-box;
   width: 1px;
@@ -1070,26 +1071,19 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 #downloads-button {
   -moz-image-region: rect(0, 108px, 18px, 90px);
 }
 
 #history-button {
   -moz-image-region: rect(0, 126px, 18px, 108px);
 }
 
-#bookmarks-button,
-#bookmarks-menu-button {
+#bookmarks-button {
   -moz-image-region: rect(0, 144px, 18px, 126px);
 }
-#bookmarks-menu-button.bookmark-item {
-  list-style-image: url("chrome://browser/skin/Toolbar.png");
-}
-#bookmarks-menu-button.bookmark-item:-moz-lwtheme-brighttext {
-  list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
-}
 
 #print-button {
   -moz-image-region: rect(0, 162px, 18px, 144px);
 }
 
 #new-tab-button {
   -moz-image-region: rect(0, 180px, 18px, 162px);
 }
@@ -1539,18 +1533,18 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 /* autocomplete */
 
 #treecolAutoCompleteImage {
   max-width: 36px;
 }
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
-  list-style-image: url("chrome://browser/skin/places/editBookmark.png");
-  -moz-image-region: rect(0px 16px 16px 0px);
+  list-style-image: url("chrome://browser/skin/places/bookmark.png");
+  -moz-image-region: rect(0px 48px 16px 32px);
   width: 16px;
   height: 16px;
 }
 
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
   list-style-image: url(chrome://global/skin/icons/Search-glass.png);
   -moz-image-region: rect(0px 32px 16px 16px);
@@ -1751,38 +1745,41 @@ richlistitem[type~="action"][actiontype=
 #unsharePopupText {
   height: 48px;
 }
 
 #unsharePopupBottomButtons {
   margin-top: 1em;
 }
 
-/* star button */
-
-#star-button {
+/* bookmarks menu-button */
+
+#bookmarks-menu-button {
+  -moz-image-region: rect(0px 378px 18px 360px);
+}
+
+#bookmarks-menu-button[starred] {
+  -moz-image-region: rect(18px 378px 36px 360px);
+}
+
+#bookmarks-menu-button.bookmark-item {
   list-style-image: url("chrome://browser/skin/places/bookmark.png");
   -moz-image-region: rect(0px 16px 16px 0px);
 }
 
-#star-button:hover {
-  background-image: radial-gradient(circle closest-side, hsla(45,100%,73%,.3), hsla(45,100%,73%,0));
-  -moz-image-region: rect(0px 32px 16px 16px);
-}
-
-#star-button:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(45,100%,73%,.1), hsla(45,100%,73%,0));
+#bookmarks-menu-button.bookmark-item[starred] {
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
-#star-button[starred="true"] {
-  list-style-image: url("chrome://browser/skin/places/editBookmark.png");
+#bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button> .toolbarbutton-icon {
+  -moz-margin-start: 5px;
 }
 
 /* bookmarking panel */
+
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
 }
 
 #editBookmarkPanelStarIcon[unstarred] {
   list-style-image: url("chrome://browser/skin/places/unstarred48.png");
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -89,17 +89,16 @@ browser.jar:
         skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
         skin/classic/browser/feeds/subscribe-ui.css                  (feeds/subscribe-ui.css)
         skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
         skin/classic/browser/newtab/controls.png                     (newtab/controls.png)
         skin/classic/browser/newtab/noise.png                        (newtab/noise.png)
         skin/classic/browser/places/places.css                       (places/places.css)
 *       skin/classic/browser/places/organizer.css                    (places/organizer.css)
         skin/classic/browser/places/bookmark.png                     (places/bookmark.png)
-        skin/classic/browser/places/editBookmark.png                 (places/editBookmark.png)
         skin/classic/browser/places/query.png                        (places/query.png)
         skin/classic/browser/places/bookmarksMenu.png                (places/bookmarksMenu.png)
         skin/classic/browser/places/bookmarksToolbar.png             (places/bookmarksToolbar.png)
         skin/classic/browser/places/calendar.png                     (places/calendar.png)
         skin/classic/browser/places/toolbarDropMarker.png            (places/toolbarDropMarker.png)
         skin/classic/browser/places/editBookmarkOverlay.css          (places/editBookmarkOverlay.css)
         skin/classic/browser/places/libraryToolbar.png               (places/libraryToolbar.png)
         skin/classic/browser/places/starred48.png                    (places/starred48.png)
@@ -335,17 +334,16 @@ browser.jar:
         skin/classic/aero/browser/feeds/subscribe.css                (feeds/subscribe.css)
         skin/classic/aero/browser/feeds/subscribe-ui.css             (feeds/subscribe-ui.css)
         skin/classic/aero/browser/newtab/newTab.css                  (newtab/newTab.css)
         skin/classic/aero/browser/newtab/controls.png                (newtab/controls.png)
         skin/classic/aero/browser/newtab/noise.png                   (newtab/noise.png)
 *       skin/classic/aero/browser/places/places.css                  (places/places-aero.css)
 *       skin/classic/aero/browser/places/organizer.css               (places/organizer-aero.css)
         skin/classic/aero/browser/places/bookmark.png                (places/bookmark.png)
-        skin/classic/aero/browser/places/editBookmark.png            (places/editBookmark.png)
         skin/classic/aero/browser/places/query.png                   (places/query-aero.png)
         skin/classic/aero/browser/places/bookmarksMenu.png           (places/bookmarksMenu-aero.png)
         skin/classic/aero/browser/places/bookmarksToolbar.png        (places/bookmarksToolbar-aero.png)
         skin/classic/aero/browser/places/calendar.png                (places/calendar-aero.png)
         skin/classic/aero/browser/places/toolbarDropMarker.png       (places/toolbarDropMarker-aero.png)
         skin/classic/aero/browser/places/editBookmarkOverlay.css     (places/editBookmarkOverlay.css)
         skin/classic/aero/browser/places/libraryToolbar.png          (places/libraryToolbar-aero.png)
         skin/classic/aero/browser/places/starred48.png               (places/starred48-aero.png)
deleted file mode 100644
index fbca0523df169b6892d2b5050035152fb6b92606..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/content/base/test/file_x-frame-options_main.html
+++ b/content/base/test/file_x-frame-options_main.html
@@ -16,16 +16,18 @@ window.addEventListener('load', parent.t
 <iframe id="sameorigin2" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin2&xfo=sameorigin"></iframe><br>
 <iframe id="sameorigin5" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin5&xfo=sameorigin2"></iframe><br>
 <iframe id="sameorigin6" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin6&xfo=sameorigin2"></iframe><br>
 <iframe id="sameorigin7" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin7&xfo=sameorigin3"></iframe><br>
 <iframe id="sameorigin8" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin8&xfo=sameorigin3"></iframe><br>
 <iframe id="mixedpolicy" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=mixedpolicy&xfo=mixedpolicy"></iframe><br>
 <iframe id="allow-from-allow" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=allow-from-allow&xfo=afa"></iframe><br>
 <iframe id="allow-from-deny" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=allow-from-deny&xfo=afd"></iframe><br>
+<iframe id="sameorigin-multipart" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin-multipart&xfo=sameorigin&multipart=1"></iframe><br>
+<iframe id="sameorigin-multipart2" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin-multipart2&xfo=sameorigin&multipart=1"></iframe><br>
 
 <!-- added for bug 836132 -->
 <script type="text/javascript">
 
   function addFrame(test, xfo) {
     var baseurl = "http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs";
     var ifr = "<iframe id='" + test + "'" + "src='" + baseurl + "?testid=" + test + "&xfo=" + xfo + "' style='border:2px solid red;'></iframe>";
     document.write(ifr);
--- a/content/base/test/file_x-frame-options_page.sjs
+++ b/content/base/test/file_x-frame-options_page.sjs
@@ -1,19 +1,28 @@
 // SJS file for X-Frame-Options mochitests
 function handleRequest(request, response)
 {
   var query = {};
+  var BOUNDARY = "BOUNDARYOMG3984";
   request.queryString.split('&').forEach(function (val) {
     var [name, value] = val.split('=');
     query[name] = unescape(value);
   });
 
-  response.setHeader("Cache-Control", "no-cache", false);
-  response.setHeader("Content-Type", "text/html", false);
+  if (query['multipart'] == "1") {
+    response.setHeader("Content-Type", "multipart/x-mixed-replace;boundary=" + BOUNDARY, false);
+    response.setHeader("Cache-Control", "no-cache", false);
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.write("--" + BOUNDARY + "\r\n");
+    response.write("Content-Type: text/html\r\n\r\n");
+  } else {
+    response.setHeader("Content-Type", "text/html", false);
+    response.setHeader("Cache-Control", "no-cache", false);
+  }
 
   var testHeaders = {
     "deny": "DENY",
     "sameorigin": "SAMEORIGIN",
     "sameorigin2": "SAMEORIGIN, SAMEORIGIN",
     "sameorigin3": "SAMEORIGIN,SAMEORIGIN , SAMEORIGIN",
     "mixedpolicy": "DENY,SAMEORIGIN",
 
@@ -39,9 +48,13 @@ function handleRequest(request, response
 
   if (testHeaders.hasOwnProperty(query['xfo'])) {
     response.setHeader("X-Frame-Options", testHeaders[query['xfo']], false);
   }
 
   // from the test harness we'll be checking for the presence of this element
   // to test if the page loaded
   response.write("<h1 id=\"test\">" + query["testid"] + "</h1>");
+
+  if (query['multipart'] == "1") {
+    response.write("\r\n--" + BOUNDARY + "\r\n");
+  }
 }
--- a/content/base/test/test_x-frame-options.html
+++ b/content/base/test/test_x-frame-options.html
@@ -113,16 +113,27 @@ var testFramesLoaded = function() {
   var test11 = frame.contentDocument.getElementById("test").textContent;
   is(test11, "allow-from-allow", "test allow-from-allow");
 
   // iframe from different origin, with allow-from: other - should not load
   frame = harness.contentDocument.getElementById("allow-from-deny");
   var test12 = frame.contentDocument.getElementById("test");
   is(test12, null, "test allow-from-deny");
 
+  // iframe from different origin, X-F-O: SAMEORIGIN, multipart - should not load
+  frame = harness.contentDocument.getElementById("sameorigin-multipart");
+  var test13 = frame.contentDocument.getElementById("test");
+  is(test13, null, "test sameorigin-multipart");
+
+  // iframe from same origin, X-F-O: SAMEORIGIN, multipart - should load
+  frame = harness.contentDocument.getElementById("sameorigin-multipart2");
+  var test14 = frame.contentDocument.getElementById("test").textContent;
+  is(test14, "sameorigin-multipart2", "test sameorigin-multipart2");
+
+
   // frames from bug 836132 tests
   {
     frame = harness.contentDocument.getElementById("allow-from-allow-1");
     var theTestResult = frame.contentDocument.getElementById("test");
     isnot(theTestResult, null, "test afa1 should have been allowed");
     if(theTestResult) {
       is(theTestResult.textContent, "allow-from-allow-1", "test allow-from-allow-1");
     }
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -1272,16 +1272,23 @@ HTMLInputElement::SetValue(const nsAStri
       //
       // NOTE: this is currently quite expensive work (too much string
       // manipulation). We should probably optimize that.
       nsAutoString currentValue;
       GetValueInternal(currentValue);
 
       SetValueInternal(aValue, false, true);
 
+      if (mType == NS_FORM_INPUT_RANGE) {
+        nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
+        if (frame) {
+          frame->UpdateForValueChange();
+        }
+      }
+
       if (mFocusedValue.Equals(currentValue)) {
         GetValueInternal(mFocusedValue);
       }
     } else {
       SetValueInternal(aValue, false, true);
     }
   }
 }
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -402,16 +402,17 @@ NS_IMPL_ADDREF_INHERITED(HTMLMediaElemen
 NS_IMPL_RELEASE_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourcePointer)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceLoadCandidate)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream);
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
@@ -419,16 +420,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
     // Need to EndMediaStreamPlayback to clear mStream and make sure everything
     // gets unhooked correctly.
     tmp->EndSrcMediaStreamPlayback();
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcAttrStream)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourcePointer)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceLoadCandidate)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
--- a/content/media/AudioNodeEngine.cpp
+++ b/content/media/AudioNodeEngine.cpp
@@ -51,19 +51,19 @@ AudioBlockAddChannelWithScale(const floa
   } else {
     for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
       aOutput[i] += aInput[i]*aScale;
     }
   }
 }
 
 void
-AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
+AudioBlockCopyChannelWithScale(const float* aInput,
                                float aScale,
-                               float aOutput[WEBAUDIO_BLOCK_SIZE])
+                               float* aOutput)
 {
   if (aScale == 1.0f) {
     memcpy(aOutput, aInput, WEBAUDIO_BLOCK_SIZE*sizeof(float));
   } else {
     for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
       aOutput[i] = aInput[i]*aScale;
     }
   }
--- a/content/media/AudioNodeEngine.h
+++ b/content/media/AudioNodeEngine.h
@@ -95,20 +95,22 @@ void WriteZeroesToAudioBlock(AudioChunk*
  * Pointwise multiply-add operation. aScale == 1.0f should be optimized.
  */
 void AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
                                    float aScale,
                                    float aOutput[WEBAUDIO_BLOCK_SIZE]);
 
 /**
  * Pointwise copy-scaled operation. aScale == 1.0f should be optimized.
+ *
+ * Buffer size is implicitly assumed to be WEBAUDIO_BLOCK_SIZE.
  */
-void AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
+void AudioBlockCopyChannelWithScale(const float* aInput,
                                     float aScale,
-                                    float aOutput[WEBAUDIO_BLOCK_SIZE]);
+                                    float* aOutput);
 
 /**
  * Vector copy-scaled operation.
  */
 void AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
                                     const float aScale[WEBAUDIO_BLOCK_SIZE],
                                     float aOutput[WEBAUDIO_BLOCK_SIZE]);
 
--- a/content/media/webaudio/DelayNode.cpp
+++ b/content/media/webaudio/DelayNode.cpp
@@ -195,17 +195,17 @@ public:
           // If the simple value has changed, smoothly approach it
           currentDelayTime += (delayTime - currentDelayTime) * smoothingRate;
         } else {
           currentDelayTime = computedDelay[i];
         }
 
         // Write the input sample to the correct location in our buffer
         if (input) {
-          buffer[writeIndex] = input[i];
+          buffer[writeIndex] = input[i] * aInput.mVolume;
         }
 
         // Now, determine the correct read position.  We adjust the read position to be
         // from currentDelayTime seconds in the past.  We also interpolate the two input
         // frames in case the read position does not match an integer index.
         double readPosition = writeIndex + bufferLength -
                               (currentDelayTime * IdealAudioRate());
         if (readPosition >= bufferLength) {
--- a/content/media/webaudio/GainNode.cpp
+++ b/content/media/webaudio/GainNode.cpp
@@ -72,17 +72,17 @@ public:
       // timeline at hand, and then for each channel, multiply the values
       // in the buffer with the gain vector.
 
       // Compute the gain values for the duration of the input AudioChunk
       // XXX we need to add a method to AudioEventTimeline to compute this buffer directly.
       float computedGain[WEBAUDIO_BLOCK_SIZE];
       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
         TrackTicks tick = aStream->GetCurrentPosition() + counter;
-        computedGain[counter] = mGain.GetValueAtTime<TrackTicks>(tick);
+        computedGain[counter] = mGain.GetValueAtTime<TrackTicks>(tick) * aInput.mVolume;
       }
 
       // Apply the gain to the output buffer
       for (size_t channel = 0; channel < aOutput->mChannelData.Length(); ++channel) {
         float* buffer = static_cast<float*> (const_cast<void*>
                           (aOutput->mChannelData[channel]));
         AudioBlockCopyChannelWithScale(buffer, computedGain, buffer);
       }
--- a/content/media/webaudio/PannerNode.cpp
+++ b/content/media/webaudio/PannerNode.cpp
@@ -273,18 +273,18 @@ PannerNodeEngine::EqualPowerPanningFunct
   }
 
   // Compute how much the distance contributes to the gain reduction.
   distanceVec = mPosition - mListenerPosition;
   distance = sqrt(distanceVec.DotProduct(distanceVec));
   distanceGain = (this->*mDistanceModelFunction)(distance);
 
   // Actually compute the left and right gain.
-  gainL = cos(0.5 * M_PI * normalizedAzimuth);
-  gainR = sin(0.5 * M_PI * normalizedAzimuth);
+  gainL = cos(0.5 * M_PI * normalizedAzimuth) * aInput.mVolume;
+  gainR = sin(0.5 * M_PI * normalizedAzimuth) * aInput.mVolume;
 
   // Compute the output.
   if (inputChannels == 1) {
     GainMonoToStereo(aInput, aOutput, gainL, gainR);
   } else {
     GainStereoToStereo(aInput, aOutput, gainL, gainR, azimuth);
   }
 
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -196,19 +196,20 @@ public:
 
     // First, record our input buffer
     for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
       if (aInput.IsNull()) {
         PodZero(mInputChannels[i] + mInputWriteIndex,
                 aInput.GetDuration());
       } else {
         mSeenNonSilenceInput = true;
-        PodCopy(mInputChannels[i] + mInputWriteIndex,
-                static_cast<const float*>(aInput.mChannelData[i]),
-                aInput.GetDuration());
+        MOZ_ASSERT(aInput.GetDuration() == WEBAUDIO_BLOCK_SIZE, "sanity check");
+        AudioBlockCopyChannelWithScale(static_cast<const float*>(aInput.mChannelData[i]),
+                                       aInput.mVolume,
+                                       mInputChannels[i] + mInputWriteIndex);
       }
     }
     mInputWriteIndex += aInput.GetDuration();
 
     // Now, see if we have data to output
     // Note that we need to do this before sending the buffer to the main
     // thread so that our delay time is updated.
     *aOutput = mSharedBuffers->GetOutputBuffer();
--- a/content/media/webaudio/blink/DynamicsCompressor.cpp
+++ b/content/media/webaudio/blink/DynamicsCompressor.cpp
@@ -29,16 +29,17 @@
 #include "DynamicsCompressor.h"
 #include "AudioSegment.h"
 
 #include <cmath>
 #include "AudioNodeEngine.h"
 #include "nsDebug.h"
 
 using mozilla::WEBAUDIO_BLOCK_SIZE;
+using mozilla::AudioBlockCopyChannelWithScale;
 
 namespace WebCore {
 
 DynamicsCompressor::DynamicsCompressor(float sampleRate, unsigned numberOfChannels)
     : m_numberOfChannels(numberOfChannels)
     , m_sampleRate(sampleRate)
     , m_compressor(sampleRate, numberOfChannels)
 {
@@ -169,20 +170,32 @@ void DynamicsCompressor::process(const A
     if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
         m_lastFilterStageGain = filterStageGain;
         m_lastFilterStageRatio = filterStageRatio;
         m_lastAnchor = anchor;
 
         setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
     }
 
+    float sourceWithVolume[WEBAUDIO_BLOCK_SIZE];
+
     // Apply pre-emphasis filter.
     // Note that the final three stages are computed in-place in the destination buffer.
     for (unsigned i = 0; i < numberOfChannels; ++i) {
-        const float* sourceData = m_sourceChannels[i];
+        const float* sourceData;
+        if (sourceChunk->mVolume == 1.0f) {
+          // Fast path, the volume scale doesn't need to get taken into account
+          sourceData = m_sourceChannels[i];
+        } else {
+          AudioBlockCopyChannelWithScale(m_sourceChannels[i],
+                                         sourceChunk->mVolume,
+                                         sourceWithVolume);
+          sourceData = sourceWithVolume;
+        }
+
         float* destinationData = m_destinationChannels[i];
         ZeroPole* preFilters = m_preFilterPacks[i]->filters;
 
         preFilters[0].process(sourceData, destinationData, framesToProcess);
         preFilters[1].process(destinationData, destinationData, framesToProcess);
         preFilters[2].process(destinationData, destinationData, framesToProcess);
         preFilters[3].process(destinationData, destinationData, framesToProcess);
     }
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -24,16 +24,17 @@ MOCHITEST_FILES := \
   test_AudioParam.html \
   test_audioBufferSourceNode.html \
   test_audioBufferSourceNodeLoop.html \
   test_audioBufferSourceNodeLoopStartEnd.html \
   test_badConnect.html \
   test_biquadFilterNode.html \
   test_currentTime.html \
   test_delayNode.html \
+  test_delayNodeWithGain.html \
   test_decodeAudioData.html \
   test_dynamicsCompressorNode.html \
   test_gainNode.html \
   test_pannerNode.html \
   test_scriptProcessorNode.html \
   test_singleSourceDest.html \
   ting.ogg \
   ting-expected.wav \
--- a/content/media/webaudio/test/test_delayNode.html
+++ b/content/media/webaudio/test/test_delayNode.html
@@ -14,30 +14,36 @@ SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = context.createBuffer(1, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
+  var expectedBuffer = context.createBuffer(1, 2048 * 2, context.sampleRate);
+  for (var i = 2048; i < 2048 * 2; ++i) {
+    expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i - 2048];
+  }
 
   var destination = context.destination;
 
   var source = context.createBufferSource();
 
   var delay = context.createDelay();
 
   var delay2 = context.createDelayNode();
   isnot(delay, delay2, "createDelayNode should create a different delay node");
 
   source.buffer = buffer;
 
   source.connect(delay);
-  delay.connect(destination);
+  var sp = context.createScriptProcessor(2048 * 2, 1);
+  delay.connect(sp);
+  sp.connect(destination);
 
   ok(delay.delayTime, "The audioparam member must exist");
   is(delay.delayTime.value, 0, "Correct initial value");
   is(delay.delayTime.defaultValue, 0, "Correct default value");
   delay.delayTime.value = 0.5;
   is(delay.delayTime.value, 0.5, "Correct initial value");
   is(delay.delayTime.defaultValue, 0, "Correct default value");
 
@@ -57,23 +63,27 @@ addLoadEvent(function() {
   expectTypeError(function() {
     context.createDelay(NaN);
   }, DOMException.NOT_SUPPORTED_ERR);
   expectException(function() {
     context.createDelay(-1);
   }, DOMException.NOT_SUPPORTED_ERR);
   context.createDelay(1); // should not throw
 
+  // Delay the source stream by 2048 frames
+  delay.delayTime.value = 2048 / context.sampleRate;
+
   source.start(0);
-  SimpleTest.executeSoon(function() {
-    source.stop(0);
-    source.disconnect();
-    delay.disconnect();
+  sp.onaudioprocess = function(e) {
+    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
+    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+
+    sp.onaudioprocess = null;
 
     SpecialPowers.clearUserPref("media.webaudio.enabled");
     SimpleTest.finish();
-  });
+  };
 });
 
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_delayNodeWithGain.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test DelayNode with a GainNode</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script src="webaudio.js" type="text/javascript"></script>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var context = new AudioContext();
+  var buffer = context.createBuffer(1, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+  }
+  var expectedBuffer = context.createBuffer(1, 2048 * 2, context.sampleRate);
+  for (var i = 2048; i < 2048 * 2; ++i) {
+    expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i - 2048] / 2;
+  }
+
+  var destination = context.destination;
+
+  var source = context.createBufferSource();
+
+  var delay = context.createDelay();
+
+  source.buffer = buffer;
+
+  var gain = context.createGain();
+  gain.gain.value = 0.5;
+
+  source.connect(gain);
+  gain.connect(delay);
+  var sp = context.createScriptProcessor(2048 * 2, 1);
+  delay.connect(sp);
+  sp.connect(destination);
+
+  // Delay the source stream by 2048 frames
+  delay.delayTime.value = 2048 / context.sampleRate;
+
+  source.start(0);
+  sp.onaudioprocess = function(e) {
+    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
+    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+
+    sp.onaudioprocess = null;
+
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  };
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/webaudio/test/test_gainNode.html
+++ b/content/media/webaudio/test/test_gainNode.html
@@ -1,57 +1,65 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test GainNode</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = context.createBuffer(1, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
+  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i] / 2;
+  }
 
   var destination = context.destination;
 
   var source = context.createBufferSource();
 
   var gain = context.createGain();
 
   var gain2 = context.createGainNode();
   isnot(gain, gain2, "createGainNode should create a different gain node");
 
   source.buffer = buffer;
 
   source.connect(gain);
-  gain.connect(destination);
+  var sp = context.createScriptProcessor(2048, 1);
+  gain.connect(sp);
+  sp.connect(destination);
 
   ok(gain.gain, "The audioparam member must exist");
   is(gain.gain.value, 1.0, "Correct initial value");
   is(gain.gain.defaultValue, 1.0, "Correct default value");
   gain.gain.value = 0.5;
   is(gain.gain.value, 0.5, "Correct initial value");
   is(gain.gain.defaultValue, 1.0, "Correct default value");
 
   source.start(0);
-  SimpleTest.executeSoon(function() {
-    source.stop(0);
-    source.disconnect();
-    gain.disconnect();
+  sp.onaudioprocess = function(e) {
+    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
+    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+
+    sp.onaudioprocess = null;
 
     SpecialPowers.clearUserPref("media.webaudio.enabled");
     SimpleTest.finish();
-  });
+  };
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/webaudio.js
+++ b/content/media/webaudio/test/webaudio.js
@@ -19,17 +19,17 @@ function expectTypeError(func) {
   } catch (ex) {
     threw = true;
     ok(ex instanceof TypeError, "Expect a TypeError");
   }
   ok(threw, "The exception was thrown");
 }
 
 function fuzzyCompare(a, b) {
-  return Math.abs(a - b) < 1e-5;
+  return Math.abs(a - b) < 5e-5;
 }
 
 function compareBuffers(buf1, buf2,
                         /*optional*/ offset,
                         /*optional*/ length,
                         /*optional*/ sourceOffset,
                         /*optional*/ destOffset) {
   is(buf1.length, buf2.length, "Buffers must have the same length");
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -255,34 +255,29 @@ nsDSURIContentListener::SetParentContent
     else
     {
         mWeakParentContentListener = nullptr;
         mParentContentListener = nullptr;
     }
     return NS_OK;
 }
 
-bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request,
+bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel *httpChannel,
                                                         const nsAString& policy) {
     static const char allowFrom[] = "allow-from";
     const uint32_t allowFromLen = ArrayLength(allowFrom) - 1;
     bool isAllowFrom =
         StringHead(policy, allowFromLen).LowerCaseEqualsLiteral(allowFrom);
 
     // return early if header does not have one of the values with meaning
     if (!policy.LowerCaseEqualsLiteral("deny") &&
         !policy.LowerCaseEqualsLiteral("sameorigin") &&
         !isAllowFrom)
         return true;
 
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
-    if (!httpChannel) {
-        return true;
-    }
-
     nsCOMPtr<nsIURI> uri;
     httpChannel->GetURI(getter_AddRefs(uri));
 
     // XXXkhuey when does this happen?  Is returning true safe here?
     if (!mDocShell) {
         return true;
     }
 
@@ -397,17 +392,30 @@ bool nsDSURIContentListener::CheckOneFra
     return true;
 }
 
 // Check if X-Frame-Options permits this document to be loaded as a subdocument.
 // This will iterate through and check any number of X-Frame-Options policies
 // in the request (comma-separated in a header, multiple headers, etc).
 bool nsDSURIContentListener::CheckFrameOptions(nsIRequest *request)
 {
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
+    nsresult rv;
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(request);
+    if (!chan) {
+      return true;
+    }
+
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(chan);
+    if (!httpChannel) {
+      // check if it is hiding in a multipart channel
+      rv = mDocShell->GetHttpChannel(chan, getter_AddRefs(httpChannel));
+      if (NS_FAILED(rv))
+        return false;
+    }
+
     if (!httpChannel) {
         return true;
     }
 
     nsAutoCString xfoHeaderCValue;
     httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-Frame-Options"),
                                    xfoHeaderCValue);
     NS_ConvertUTF8toUTF16 xfoHeaderValue(xfoHeaderCValue);
@@ -416,17 +424,17 @@ bool nsDSURIContentListener::CheckFrameO
     if (xfoHeaderValue.IsEmpty())
         return true;
 
     // iterate through all the header values (usually there's only one, but can
     // be many.  If any want to deny the load, deny the load.
     nsCharSeparatedTokenizer tokenizer(xfoHeaderValue, ',');
     while (tokenizer.hasMoreTokens()) {
         const nsSubstring& tok = tokenizer.nextToken();
-        if (!CheckOneFrameOptionsPolicy(request, tok)) {
+        if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) {
             // cancel the load and display about:blank
             httpChannel->Cancel(NS_BINDING_ABORTED);
             if (mDocShell) {
                 nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
                 if (webNav) {
                     webNav->LoadURI(NS_LITERAL_STRING("about:blank").get(),
                                     0, nullptr, nullptr, nullptr);
                 }
--- a/docshell/base/nsDSURIContentListener.h
+++ b/docshell/base/nsDSURIContentListener.h
@@ -9,16 +9,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIURIContentListener.h"
 #include "nsWeakReference.h"
 
 class nsDocShell;
 class nsIWebNavigationInfo;
+class nsIHttpChannel;
 
 class nsDSURIContentListener :
     public nsIURIContentListener,
     public nsSupportsWeakReference
 
 {
 friend class nsDocShell;
 public:
@@ -33,17 +34,17 @@ protected:
 
     void DropDocShellreference() {
         mDocShell = nullptr;
     }
 
     // Determine if X-Frame-Options allows content to be framed
     // as a subdocument
     bool CheckFrameOptions(nsIRequest* request);
-    bool CheckOneFrameOptionsPolicy(nsIRequest* request,
+    bool CheckOneFrameOptionsPolicy(nsIHttpChannel* httpChannel,
                                     const nsAString& policy);
 
     enum XFOHeader {
       eDENY,
       eSAMEORIGIN,
       eALLOWFROM
     };
 
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -3,17 +3,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioChannelAgent.h"
 #include "AudioChannelCommon.h"
 #include "AudioChannelService.h"
 
 using namespace mozilla::dom;
 
-NS_IMPL_ISUPPORTS1(AudioChannelAgent, nsIAudioChannelAgent)
+NS_IMPL_CYCLE_COLLECTION_1(AudioChannelAgent, mCallback)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent)
+  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
 
 AudioChannelAgent::AudioChannelAgent()
   : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
   , mIsRegToService(false)
   , mVisible(true)
 {
 }
 
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -3,34 +3,37 @@
 /* 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 mozilla_dom_audio_channel_agent_h__
 #define mozilla_dom_audio_channel_agent_h__
 
 #include "nsIAudioChannelAgent.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "nsWeakPtr.h"
 
 #define NS_AUDIOCHANNELAGENT_CONTRACTID "@mozilla.org/audiochannelagent;1"
 // f27688e2-3dd7-11e2-904e-10bf48d64bd4
 #define NS_AUDIOCHANNELAGENT_CID {0xf27688e2, 0x3dd7, 0x11e2, \
       {0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}}
 
 namespace mozilla {
 namespace dom {
 
 /* Header file */
 class AudioChannelAgent : public nsIAudioChannelAgent
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIAUDIOCHANNELAGENT
 
+  NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
+
   AudioChannelAgent();
   virtual void NotifyAudioChannelStateChanged();
 
 private:
   virtual ~AudioChannelAgent();
 
   // Returns mCallback if that's non-null, or otherwise tries to get an
   // nsIAudioChannelAgentCallback out of mWeakCallback.
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -780,19 +780,18 @@ OSFileConstantsService::~OSFileConstants
 NS_IMETHODIMP
 OSFileConstantsService::Init(JSContext *aCx)
 {
   nsresult rv = mozilla::InitOSFileConstants();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  JSObject *targetObj = nullptr;
-
   mozJSComponentLoader* loader = mozJSComponentLoader::Get();
+  JS::Rooted<JSObject*> targetObj(aCx);
   rv = loader->FindTargetObject(aCx, &targetObj);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!mozilla::DefineOSFileConstants(aCx, targetObj)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -382,18 +382,18 @@ gfxPlatformMac::CreateThebesSurfaceAlias
 {
   if (aTarget->GetType() == BACKEND_COREGRAPHICS) {
     CGContextRef cg = static_cast<CGContextRef>(aTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT));
     unsigned char* data = (unsigned char*)CGBitmapContextGetData(cg);
     size_t bpp = CGBitmapContextGetBitsPerPixel(cg);
     size_t stride = CGBitmapContextGetBytesPerRow(cg);
     gfxIntSize size(aTarget->GetSize().width, aTarget->GetSize().height);
     nsRefPtr<gfxImageSurface> imageSurface = new gfxImageSurface(data, size, stride, bpp == 2
-                                                                                     ? gfxImageFormat::ImageFormatRGB16_565
-                                                                                     : gfxImageFormat::ImageFormatARGB32);
+                                                                                     ? gfxASurface::ImageFormatRGB16_565
+                                                                                     : gfxASurface::ImageFormatARGB32);
     // Here we should return a gfxQuartzImageSurface but quartz will assumes that image surfaces
     // don't change which wont create a proper alias to the draw target, therefore we have to
     // return a plain image surface.
     return imageSurface.forget();
   } else {
     return GetThebesSurfaceForDrawTarget(aTarget);
   }
 }
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -148,17 +148,17 @@ private:
   // vibrator thread.
   static bool sShuttingDown;
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS2(VibratorRunnable, nsIRunnable, nsIObserver);
 
 bool VibratorRunnable::sShuttingDown = false;
 
-static nsRefPtr<VibratorRunnable> sVibratorRunnable;
+static StaticRefPtr<VibratorRunnable> sVibratorRunnable;
 
 NS_IMETHODIMP
 VibratorRunnable::Run()
 {
   MonitorAutoLock lock(mMonitor);
 
   // We currently assume that mMonitor.Wait(X) waits for X milliseconds.  But in
   // reality, the kernel might not switch to this thread for some time after the
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -3517,22 +3517,26 @@ RasterImage::DecodePool::Singleton()
 
 RasterImage::DecodePool::DecodePool()
  : mThreadPoolMutex("Thread Pool")
 {
   if (gMultithreadedDecoding) {
     mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
     if (mThreadPool) {
       mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
+      uint32_t limit;
       if (gDecodingThreadLimit <= 0) {
-        mThreadPool->SetThreadLimit(std::max(PR_GetNumberOfProcessors() - 1, 1));
+        limit = std::max(PR_GetNumberOfProcessors(), 2) - 1;
       } else {
-        mThreadPool->SetThreadLimit(static_cast<uint32_t>(gDecodingThreadLimit));
+        limit = static_cast<uint32_t>(gDecodingThreadLimit);
       }
 
+      mThreadPool->SetThreadLimit(limit);
+      mThreadPool->SetIdleThreadLimit(limit);
+
       nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
       if (obsSvc) {
         obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
       }
     }
   }
 }
 
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -295,17 +295,18 @@ private:
 
 // imgStatusTracker methods
 
 imgStatusTracker::imgStatusTracker(Image* aImage)
   : mImage(aImage),
     mState(0),
     mImageStatus(imgIRequest::STATUS_NONE),
     mIsMultipart(false),
-    mHadLastPart(false)
+    mHadLastPart(false),
+    mHasBeenDecoded(false)
 {
   mTrackerObserver = new imgStatusTrackerObserver(this);
 }
 
 // Private, used only by CloneForRecording.
 imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther)
   : mImage(aOther.mImage),
     mState(aOther.mState),
@@ -529,39 +530,49 @@ imgStatusTracker::CalculateAndApplyDiffe
   diff.mDiffState = ~mState & other->mState & ~stateRequestStarted;
   diff.mUnblockedOnload = mState & stateBlockingOnload && !(other->mState & stateBlockingOnload);
   diff.mFoundError = (mImageStatus != imgIRequest::STATUS_ERROR) && (other->mImageStatus == imgIRequest::STATUS_ERROR);
 
   // Now that we've calculated the difference in state, synchronize our state
   // with the other tracker.
 
   // First, actually synchronize our state.
-  diff.mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
   mState |= diff.mDiffState | loadState;
   if (diff.mUnblockedOnload) {
     mState &= ~stateBlockingOnload;
   }
   mImageStatus = other->mImageStatus;
   mIsMultipart = other->mIsMultipart;
   mHadLastPart = other->mHadLastPart;
   mImageStatus |= other->mImageStatus;
+  mHasBeenDecoded = mHasBeenDecoded || other->mHasBeenDecoded;
 
   // The error state is sticky and overrides all other bits.
   if (mImageStatus & imgIRequest::STATUS_ERROR) {
     mImageStatus = imgIRequest::STATUS_ERROR;
   } else {
     // Unset the bits that can get unset as part of the decoding process.
     if (!(other->mImageStatus & imgIRequest::STATUS_DECODE_STARTED)) {
       mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
     }
   }
 
-  // Reset the invalid rectangles for another go.
-  other->mInvalidRect.SetEmpty();
-  mInvalidRect.SetEmpty();
+  // Only record partial invalidations if we haven't been decoded before.
+  // When images are re-decoded after discarding, we don't want to display
+  // partially decoded versions to the user.
+  bool doInvalidations  = !mHasBeenDecoded
+                       || mImageStatus & imgIRequest::STATUS_ERROR
+                       || mImageStatus & imgIRequest::STATUS_DECODE_COMPLETE;
+
+  // Record the invalid rectangles and reset them for another go.
+  if (doInvalidations) {
+    diff.mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
+    other->mInvalidRect.SetEmpty();
+    mInvalidRect.SetEmpty();
+  }
 
   return diff;
 }
 
 void
 imgStatusTracker::SyncNotifyDifference(imgStatusTracker::StatusDiff diff)
 {
   LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference");
@@ -763,16 +774,17 @@ imgStatusTracker::RecordStopDecode(nsres
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordStopDecode called before we have an Image");
   mState |= stateDecodeStopped;
 
   if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) {
     mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE;
     mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
+    mHasBeenDecoded = true;
   // If we weren't successful, clear all success status bits and set error.
   } else {
     mImageStatus = imgIRequest::STATUS_ERROR;
   }
 }
 
 void
 imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy,
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -241,11 +241,12 @@ private:
   nsTObserverArray<imgRequestProxy*> mConsumers;
 
   mozilla::RefPtr<imgDecoderObserver> mTrackerObserver;
 
   uint32_t mState;
   uint32_t mImageStatus;
   bool mIsMultipart    : 1;
   bool mHadLastPart    : 1;
+  bool mHasBeenDecoded : 1;
 };
 
 #endif
--- a/ipc/chromium/Makefile.in
+++ b/ipc/chromium/Makefile.in
@@ -333,20 +333,16 @@ ifneq (86,$(findstring 86,$(OS_TEST))) #
 ifneq (arm,$(findstring arm,$(OS_TEST))) # {
 ifneq (mips,$(findstring mips,$(OS_TEST))) # {
 # Use mutex-backed atomics
 CPPSRCS += atomicops_internals_mutex.cc
 endif # }
 endif # }
 endif # }
 
-ifdef MOZ_DEBUG
-OS_CFLAGS += -DUSE_DEBUG
-endif
-
 OS_CXXFLAGS += $(TK_CFLAGS)
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef MOZ_NATIVE_LIBEVENT # {
 
 export-preqs = \
   $(call mkdir_deps,$(DIST)/third_party/libevent) \
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -2493,17 +2493,18 @@ DoBinaryArithFallback(JSContext *cx, Bas
           default:
             break;
         }
     }
 
     // TODO: unlink previous !allowDouble stub.
     if (lhs.isInt32() && rhs.isInt32()) {
         bool allowDouble = ret.isDouble();
-        IonSpew(IonSpew_BaselineIC, "  Generating %s(Int32, Int32) stub", js_CodeName[op]);
+        IonSpew(IonSpew_BaselineIC, "  Generating %s(Int32, Int32%s) stub", js_CodeName[op],
+                allowDouble ? " => Double" : "");
         ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble);
         ICStub *int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script));
         if (!int32Stub)
             return false;
         stub->addNewStub(int32Stub);
         return true;
     }
 
@@ -5110,17 +5111,17 @@ TryAttachNativeGetPropStub(JSContext *cx
     if (!isListBase && IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(holder, shape->slot(), &isFixedSlot, &offset);
 
         ICStub::Kind kind = (obj == holder) ? ICStub::GetProp_Native
                                             : ICStub::GetProp_NativePrototype;
 
-        IonSpew(IonSpew_BaselineIC, "  Generating GetProp(%s %s%s) stub",
+        IonSpew(IonSpew_BaselineIC, "  Generating GetProp(%s %s) stub",
                     isListBase ? "ListBase" : "Native",
                     (obj == holder) ? "direct" : "prototype");
         ICGetPropNativeCompiler compiler(cx, kind, monitorStub, obj, holder, isFixedSlot, offset);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
--- a/js/src/ion/arm/BaselineIC-arm.cpp
+++ b/js/src/ion/arm/BaselineIC-arm.cpp
@@ -173,19 +173,32 @@ ICBinaryArith_Int32::Compiler::generateS
       case JSOP_RSH:
         masm.ma_and(Imm32(0x1F), R1.payloadReg(), R1.payloadReg());
         masm.ma_asr(R1.payloadReg(), R0.payloadReg(), R0.payloadReg());
         break;
       case JSOP_URSH:
         masm.ma_and(Imm32(0x1F), R1.payloadReg(), scratchReg);
         masm.ma_lsr(scratchReg, R0.payloadReg(), scratchReg);
         masm.ma_cmp(scratchReg, Imm32(0));
-        masm.j(Assembler::LessThan, &failure);
-        // Move result for return.
-        masm.mov(scratchReg, R0.payloadReg());
+        if (allowDouble_) {
+            Label toUint;
+            masm.j(Assembler::LessThan, &toUint);
+
+            // Move result and box for return.
+            masm.mov(scratchReg, R0.payloadReg());
+            EmitReturnFromIC(masm);
+
+            masm.bind(&toUint);
+            masm.convertUInt32ToDouble(scratchReg, ScratchFloatReg);
+            masm.boxDouble(ScratchFloatReg, R0);
+        } else {
+            masm.j(Assembler::LessThan, &failure);
+            // Move result for return.
+            masm.mov(scratchReg, R0.payloadReg());
+        }
         break;
       default:
         JS_NOT_REACHED("Unhandled op for BinaryArith_Int32.");
         return false;
     }
 
     EmitReturnFromIC(masm);
 
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -366,30 +366,32 @@ private:
     // Implementation for transferMultipleByRuns so we can use different
     // iterators for forward/backward traversals.
     // The sign argument should be 1 if we traverse forwards, -1 if we
     // traverse backwards.
     template<typename RegisterIterator> int32_t
     transferMultipleByRunsImpl(FloatRegisterSet set, LoadStore ls,
                                Register rm, DTMMode mode, int32_t sign)
     {
+        JS_ASSERT(sign == 1 || sign == -1);
+
         int32_t delta = sign * sizeof(double);
         int32_t offset = 0;
         RegisterIterator iter(set);
         while (iter.more()) {
             startFloatTransferM(ls, rm, mode, WriteBack);
             int32_t reg = (*iter).code_;
             do {
                 offset += delta;
                 transferFloatReg(*iter);
             } while ((++iter).more() && (*iter).code_ == (reg += sign));
             finishFloatTransfer();
         }
 
-        JS_ASSERT(offset == set.size() * sizeof(double) * sign);
+        JS_ASSERT(offset == static_cast<int32_t>(set.size() * sizeof(double)) * sign);
         return offset;
     }
 };
 
 class MacroAssemblerARMCompat : public MacroAssemblerARM
 {
     // Number of bytes the stack is adjusted inside a call to C. Calls to C may
     // not be nested.
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -358,17 +358,18 @@ js::NewContext(JSRuntime *rt, size_t sta
 
 void
 js::DestroyContext(JSContext *cx, DestroyContextMode mode)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
 #ifdef JS_THREADSAFE
-    JS_ASSERT(cx->outstandingRequests == 0);
+    if (cx->outstandingRequests != 0)
+        MOZ_CRASH();
 #endif
 
     if (mode != DCM_NEW_FAILED) {
         if (JSContextCallback cxCallback = rt->cxCallback) {
             /*
              * JSCONTEXT_DESTROY callback is not allowed to fail and must
              * return true.
              */
--- a/js/src/methodjit/NunboxAssembler.h
+++ b/js/src/methodjit/NunboxAssembler.h
@@ -184,82 +184,85 @@ class NunboxAssembler : public JSC::Macr
 
     /*
      * Store a (64b) js::Value from type |treg| and payload |dreg| into |address|, and
      * return a label which can be used by
      * ICRepatcher::patchAddressOffsetForValueStore to patch the address'
      * offset.
      */
     DataLabel32 storeValueWithAddressOffsetPatch(RegisterID treg, RegisterID dreg, Address address) {
-        DataLabel32 start = dataLabel32();
 #if defined JS_CPU_X86
         /*
          * On x86 there are two stores to patch and they both encode the offset
          * in-line.
          */
+        DataLabel32 start = dataLabel32();
         storeTypeTag(treg, address);
         DBGLABEL_NOMASM(endType);
         storePayload(dreg, address);
         DBGLABEL_NOMASM(endPayload);
         JS_ASSERT(differenceBetween(start, endType) == 6);
         JS_ASSERT(differenceBetween(endType, endPayload) == 6);
         return start;
 #elif defined JS_CPU_ARM || defined JS_CPU_SPARC
         return store64WithAddressOffsetPatch(treg, dreg, address);
 #elif defined JS_CPU_MIPS
         /*
          * On MIPS there are LUI/ORI to patch.
          */
+        DataLabel32 start = dataLabel32();
         store64WithPatch(address, treg, dreg, TAG_OFFSET, PAYLOAD_OFFSET);
         return start;
 #endif
     }
 
     /* Overloaded for storing a constant type. */
     DataLabel32 storeValueWithAddressOffsetPatch(ImmType type, RegisterID dreg, Address address) {
+#if defined JS_CPU_X86
         DataLabel32 start = dataLabel32();
-#if defined JS_CPU_X86
         storeTypeTag(type, address);
         DBGLABEL_NOMASM(endType);
         storePayload(dreg, address);
         DBGLABEL_NOMASM(endPayload);
         JS_ASSERT(differenceBetween(start, endType) == 10);
         JS_ASSERT(differenceBetween(endType, endPayload) == 6);
         return start;
 #elif defined JS_CPU_ARM || defined JS_CPU_SPARC
         return store64WithAddressOffsetPatch(type, dreg, address);
 #elif defined JS_CPU_MIPS
         /*
          * On MIPS there are LUI/ORI to patch.
          */
+        DataLabel32 start = dataLabel32();
         store64WithPatch(address, type, dreg, TAG_OFFSET, PAYLOAD_OFFSET);
         return start;
 #endif
     }
 
     /* Overloaded for storing constant type and data. */
     DataLabel32 storeValueWithAddressOffsetPatch(const Value &v, Address address) {
         jsval_layout jv = JSVAL_TO_IMPL(v);
         ImmTag type(jv.s.tag);
         Imm32 payload(jv.s.payload.u32);
+#if defined JS_CPU_X86
         DataLabel32 start = dataLabel32();
-#if defined JS_CPU_X86
         store32(type, tagOf(address));
         DBGLABEL_NOMASM(endType);
         store32(payload, payloadOf(address));
         DBGLABEL_NOMASM(endPayload);
         JS_ASSERT(differenceBetween(start, endType) == 10);
         JS_ASSERT(differenceBetween(endType, endPayload) == 10);
         return start;
 #elif defined JS_CPU_ARM || defined JS_CPU_SPARC
         return store64WithAddressOffsetPatch(type, payload, address);
 #elif defined JS_CPU_MIPS
         /*
          * On MIPS there are LUI/ORI to patch.
          */
+        DataLabel32 start = dataLabel32();
         store64WithPatch(address, type, payload, TAG_OFFSET, PAYLOAD_OFFSET);
         return start;
 #endif
     }
 
     /* Overloaded for store with value remat info. */
     DataLabel32 storeValueWithAddressOffsetPatch(const ValueRemat &vr, Address address) {
         JS_ASSERT(!vr.isFPRegister());
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -67,16 +67,17 @@
 #include "mozilla/Preferences.h"
 
 #include "jsdbgapi.h"
 
 
 using namespace mozilla;
 using namespace mozilla::scache;
 using namespace xpc;
+using namespace JS;
 
 // This JSClass exists to trick silly code that expects toString()ing the
 // global in a component scope to return something with "BackstagePass" in it
 // to continue working.
 static JSClass kFakeBackstagePassJSClass =
 {
     "FakeBackstagePass",
     0,
@@ -265,20 +266,20 @@ File(JSContext *cx, unsigned argc, jsval
     if (!xpc) {
         XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
         return false;
     }
 
     JSObject* glob = JS_GetGlobalForScopeChain(cx);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    jsval retval;
+    RootedValue retval(cx);
     rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
                                 &NS_GET_IID(nsISupports),
-                                true, &retval, nullptr);
+                                true, retval.address(), nullptr);
     if (NS_FAILED(rv)) {
         XPCThrower::Throw(rv, cx);
         return false;
     }
 
     JS_SET_RVAL(cx, vp, retval);
     return true;
 }
@@ -308,20 +309,20 @@ Blob(JSContext *cx, unsigned argc, jsval
     if (!xpc) {
         XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
         return false;
     }
 
     JSObject* glob = JS_GetGlobalForScopeChain(cx);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    jsval retval;
+    RootedValue retval(cx);
     rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
                                 &NS_GET_IID(nsISupports),
-                                true, &retval, nullptr);
+                                true, retval.address(), nullptr);
     if (NS_FAILED(rv)) {
         XPCThrower::Throw(rv, cx);
         return false;
     }
 
     JS_SET_RVAL(cx, vp, retval);
     return true;
 }
@@ -524,18 +525,19 @@ mozJSComponentLoader::LoadModule(FileLoc
     }
 
     ModuleEntry* mod;
     if (mModules.Get(spec, &mod))
     return mod;
 
     nsAutoPtr<ModuleEntry> entry(new ModuleEntry);
 
+    RootedValue dummy(mContext);
     rv = ObjectForLocation(file, uri, &entry->obj,
-                           &entry->location, nullptr);
+                           &entry->location, false, &dummy);
     if (NS_FAILED(rv)) {
         return NULL;
     }
 
     nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
                                                &rv);
     if (NS_FAILED(rv))
         return NULL;
@@ -582,33 +584,32 @@ mozJSComponentLoader::LoadModule(FileLoc
 
     rv = file_holder->GetJSObject(&file_jsobj);
     if (NS_FAILED(rv)) {
         return NULL;
     }
 
     JSCLAutoErrorReporterSetter aers(cx, mozJSLoaderErrorReporter);
 
-    jsval NSGetFactory_val;
-
-    if (!JS_GetProperty(cx, entry->obj, "NSGetFactory", &NSGetFactory_val) ||
+    RootedValue NSGetFactory_val(cx);
+    if (!JS_GetProperty(cx, entry->obj, "NSGetFactory", NSGetFactory_val.address()) ||
         JSVAL_IS_VOID(NSGetFactory_val)) {
         return NULL;
     }
 
     if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
         nsAutoCString spec;
         uri->GetSpec(spec);
         JS_ReportError(cx, "%s has NSGetFactory property that is not a function",
                        spec.get());
         return NULL;
     }
 
-    JSObject *jsGetFactoryObj;
-    if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
+    RootedObject jsGetFactoryObj(cx);
+    if (!JS_ValueToObject(cx, NSGetFactory_val, jsGetFactoryObj.address()) ||
         !jsGetFactoryObj) {
         /* XXX report error properly */
         return NULL;
     }
 
     rv = xpc->WrapJS(cx, jsGetFactoryObj,
                      NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
     if (NS_FAILED(rv)) {
@@ -629,21 +630,21 @@ mozJSComponentLoader::LoadModule(FileLoc
     }
 
     // The hash owns the ModuleEntry now, forget about it
     return entry.forget();
 }
 
 nsresult
 mozJSComponentLoader::FindTargetObject(JSContext* aCx,
-                                       JSObject** aTargetObject)
+                                       JS::MutableHandleObject aTargetObject)
 {
-    JSObject* targetObject = nullptr;
-    *aTargetObject = nullptr;
+    aTargetObject.set(nullptr);
 
+    RootedObject targetObject(aCx);
     if (mReuseLoaderGlobal) {
         JSScript* script =
             js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
         if (script) {
             targetObject = mThisObjects.Get(script);
         }
     }
 
@@ -660,26 +661,26 @@ mozJSComponentLoader::FindTargetObject(J
         nsAXPCNativeCallContext *cc = nullptr;
         rv = xpc->GetCurrentNativeCallContext(&cc);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCOMPtr<nsIXPConnectWrappedNative> wn;
         rv = cc->GetCalleeWrapper(getter_AddRefs(wn));
         NS_ENSURE_SUCCESS(rv, rv);
 
-        wn->GetJSObject(&targetObject);
+        wn->GetJSObject(targetObject.address());
         if (!targetObject) {
             NS_ERROR("null calling object");
             return NS_ERROR_FAILURE;
         }
 
         targetObject = JS_GetGlobalForObject(aCx, targetObject);
     }
 
-    *aTargetObject = targetObject;
+    aTargetObject.set(targetObject);
     return NS_OK;
 }
 
 void
 mozJSComponentLoader::NoteSubScript(JSScript* aScript, JSObject* aThisObject)
 {
   if (!mInitialized && NS_FAILED(ReallyInit())) {
       MOZ_NOT_REACHED();
@@ -743,47 +744,44 @@ mozJSComponentLoader::PrepareObjectForLo
         rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
                                                   static_cast<nsIGlobalObject *>(backstagePass),
                                                   mSystemPrincipal,
                                                   0,
                                                   JS::SystemZone,
                                                   getter_AddRefs(holder));
         NS_ENSURE_SUCCESS(rv, nullptr);
 
-        JSObject *global;
-        rv = holder->GetJSObject(&global);
+        RootedObject global(aCx);
+        rv = holder->GetJSObject(global.address());
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         backstagePass->SetGlobalObject(global);
 
         JSAutoCompartment ac(aCx, global);
         if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
             !JS_DefineProfilingFunctions(aCx, global)) {
             return nullptr;
         }
 
         if (aReuseLoaderGlobal) {
             mLoaderGlobal = holder;
         }
     }
 
-    JSObject* obj;
-    rv = holder->GetJSObject(&obj);
+    RootedObject obj(aCx);
+    rv = holder->GetJSObject(obj.address());
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     JSAutoCompartment ac(aCx, obj);
 
     if (aReuseLoaderGlobal) {
         // If we're reusing the loader global, we don't actually use the
         // global, but rather we use a different object as the 'this' object.
-        JSObject* newObj = JS_NewObject(aCx, &kFakeBackstagePassJSClass,
-                                        nullptr, nullptr);
-        NS_ENSURE_TRUE(newObj, nullptr);
-
-        obj = newObj;
+        obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass, nullptr, nullptr);
+        NS_ENSURE_TRUE(obj, nullptr);
     }
 
     *aRealFile = false;
 
     // need to be extra careful checking for URIs pointing to files
     // EnsureFile may not always get called, especially on resource URIs
     // so we need to call GetFile to make sure this is a valid file
     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
@@ -796,22 +794,22 @@ mozJSComponentLoader::PrepareObjectForLo
         *aRealFile = true;
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
         rv = xpc->WrapNative(aCx, obj, aComponentFile,
                              NS_GET_IID(nsIFile),
                              getter_AddRefs(locationHolder));
         NS_ENSURE_SUCCESS(rv, nullptr);
 
-        JSObject *locationObj;
-        rv = locationHolder->GetJSObject(&locationObj);
+        RootedObject locationObj(aCx);
+        rv = locationHolder->GetJSObject(locationObj.address());
         NS_ENSURE_SUCCESS(rv, nullptr);
 
         if (!JS_DefineProperty(aCx, obj, "__LOCATION__",
-                               OBJECT_TO_JSVAL(locationObj),
+                               JS::ObjectValue(*locationObj),
                                nullptr, nullptr, 0)) {
             return nullptr;
         }
     }
 
     nsAutoCString nativePath;
     rv = aURI->GetSpec(nativePath);
     NS_ENSURE_SUCCESS(rv, nullptr);
@@ -828,27 +826,28 @@ mozJSComponentLoader::PrepareObjectForLo
     return obj;
 }
 
 nsresult
 mozJSComponentLoader::ObjectForLocation(nsIFile *aComponentFile,
                                         nsIURI *aURI,
                                         JSObject **aObject,
                                         char **aLocation,
-                                        jsval *exception)
+                                        bool aPropagateExceptions,
+                                        JS::MutableHandleValue aException)
 {
     JSCLContextHelper cx(this);
 
     JS_AbortIfWrongThread(JS_GetRuntime(cx));
 
     JSCLAutoErrorReporterSetter aers(cx, mozJSLoaderErrorReporter);
 
     bool realFile = false;
-    JSObject *obj = PrepareObjectForLocation(cx, aComponentFile, aURI,
-                                             mReuseLoaderGlobal, &realFile);
+    RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aURI,
+                                                  mReuseLoaderGlobal, &realFile));
     NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
 
     JSAutoCompartment ac(cx, obj);
 
     JSScript *script = nullptr;
     JSFunction *function = nullptr;
 
     nsAutoCString nativePath;
@@ -884,31 +883,31 @@ mozJSComponentLoader::ObjectForLocation(
             writeToCache = true;
         }
     }
 
     if (!script && !function) {
         // The script wasn't in the cache , so compile it now.
         LOG(("Slow loading %s\n", nativePath.get()));
 
-        // If |exception| is non-null, then our caller wants us to propagate
+        // If aPropagateExceptions is true, then our caller wants us to propagate
         // any exceptions out to our caller. Ensure that the engine doesn't
         // eagerly report the exception.
         uint32_t oldopts = JS_GetOptions(cx);
-        if (exception)
+        if (aPropagateExceptions)
             JS_SetOptions(cx, oldopts | JSOPTION_DONT_REPORT_UNCAUGHT);
+
         JS::CompileOptions options(cx);
         options.setPrincipals(nsJSPrincipals::get(mSystemPrincipal))
                .setNoScriptRval(mReuseLoaderGlobal ? false : true)
                .setVersion(JSVERSION_LATEST)
                .setFileAndLine(nativePath.get(), 1)
                .setSourcePolicy(mReuseLoaderGlobal ?
                                 JS::CompileOptions::NO_SOURCE :
                                 JS::CompileOptions::LAZY_SOURCE);
-        JS::RootedObject rootedObject(cx, obj);
 
         if (realFile) {
 #ifdef HAVE_PR_MEMMAP
             int64_t fileSize;
             rv = aComponentFile->GetFileSize(&fileSize);
             if (NS_FAILED(rv)) {
                 JS_SetOptions(cx, oldopts);
                 return rv;
@@ -947,20 +946,20 @@ mozJSComponentLoader::ObjectForLocation(
             char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
             if (!buf) {
                 NS_WARNING("Failed to map file");
                 JS_SetOptions(cx, oldopts);
                 return NS_ERROR_FAILURE;
             }
 
             if (!mReuseLoaderGlobal) {
-                script = JS::Compile(cx, rootedObject, options, buf,
+                script = JS::Compile(cx, obj, options, buf,
                                      fileSize32);
             } else {
-                function = JS::CompileFunction(cx, rootedObject, options,
+                function = JS::CompileFunction(cx, obj, options,
                                                nullptr, 0, nullptr,
                                                buf, fileSize32);
             }
 
             PR_MemUnmap(buf, fileSize32);
 
 #else  /* HAVE_PR_MEMMAP */
 
@@ -997,20 +996,20 @@ mozJSComponentLoader::ObjectForLocation(
             if (rlen != (uint64_t)len) {
                 free(buf);
                 JS_SetOptions(cx, oldopts);
                 NS_WARNING("Failed to read file");
                 return NS_ERROR_FAILURE;
             }
 
             if (!mReuseLoaderGlobal) {
-                script = JS::Compile(cx, rootedObject, options, buf,
+                script = JS::Compile(cx, obj, options, buf,
                                      fileSize32);
             } else {
-                function = JS::CompileFunction(cx, rootedObject, options,
+                function = JS::CompileFunction(cx, obj, options,
                                                nullptr, 0, nullptr,
                                                buf, fileSize32);
             }
 
             free(buf);
 
 #endif /* HAVE_PR_MEMMAP */
         } else {
@@ -1043,29 +1042,28 @@ mozJSComponentLoader::ObjectForLocation(
             /* read the file in one swoop */
             rv = scriptStream->Read(buf, len, &bytesRead);
             if (bytesRead != len)
                 return NS_BASE_STREAM_OSERROR;
 
             buf[len] = '\0';
 
             if (!mReuseLoaderGlobal) {
-                script = JS::Compile(cx, rootedObject, options, buf,
-                                     bytesRead);
+                script = JS::Compile(cx, obj, options, buf, bytesRead);
             } else {
-                function = JS::CompileFunction(cx, rootedObject, options,
+                function = JS::CompileFunction(cx, obj, options,
                                                nullptr, 0, nullptr,
                                                buf, bytesRead);
             }
         }
         // Propagate the exception, if one exists. Also, don't leave the stale
         // exception on this context.
         JS_SetOptions(cx, oldopts);
-        if (!script && !function && exception) {
-            JS_GetPendingException(cx, exception);
+        if (!script && !function && aPropagateExceptions) {
+            JS_GetPendingException(cx, aException.address());
             JS_ClearPendingException(cx);
         }
     }
 
     if (!script && !function) {
         return NS_ERROR_FAILURE;
     }
 
@@ -1096,33 +1094,29 @@ mozJSComponentLoader::ObjectForLocation(
     if (!tableScript) {
         tableScript = JS_GetFunctionScript(cx, function);
         MOZ_ASSERT(tableScript);
     }
 
     mThisObjects.Put(tableScript, obj);
 
     uint32_t oldopts = JS_GetOptions(cx);
-    JS_SetOptions(cx, oldopts |
-                  (exception ? JSOPTION_DONT_REPORT_UNCAUGHT : 0));
+    JS_SetOptions(cx, oldopts | (aPropagateExceptions ? JSOPTION_DONT_REPORT_UNCAUGHT : 0));
     bool ok = false;
     if (script) {
         ok = JS_ExecuteScriptVersion(cx, obj, script, NULL, JSVERSION_LATEST);
     } else {
         jsval rval;
         ok = JS_CallFunction(cx, obj, function, 0, nullptr, &rval);
     }
     JS_SetOptions(cx, oldopts);
 
     if (!ok) {
-#ifdef DEBUG_shaver_off
-        fprintf(stderr, "mJCL: failed to execute %s\n", nativePath.get());
-#endif
-        if (exception) {
-            JS_GetPendingException(cx, exception);
+        if (aPropagateExceptions) {
+            JS_GetPendingException(cx, aException.address());
             JS_ClearPendingException(cx);
         }
         *aObject = nullptr;
         return NS_ERROR_FAILURE;
     }
 
     /* Freed when we remove from the table. */
     *aLocation = ToNewCString(nativePath);
@@ -1145,18 +1139,18 @@ mozJSComponentLoader::ClearModules(const
 void
 mozJSComponentLoader::UnloadModules()
 {
     mInitialized = false;
 
     if (mLoaderGlobal) {
         MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
 
-        JSObject* global;
-        if (NS_SUCCEEDED(mLoaderGlobal->GetJSObject(&global))) {
+        RootedObject global(mContext);
+        if (NS_SUCCEEDED(mLoaderGlobal->GetJSObject(global.address()))) {
             JSAutoRequest ar(mContext);
             JS_SetAllNonReservedSlotsToUndefined(mContext, global);
         } else {
             NS_WARNING("Going to leak!");
         }
 
         mLoaderGlobal = nullptr;
     }
@@ -1175,38 +1169,37 @@ mozJSComponentLoader::UnloadModules()
     mContextStack = nullptr;
 #ifdef DEBUG_shaver_off
     fprintf(stderr, "mJCL: UnloadAll(%d)\n", aWhen);
 #endif
 }
 
 NS_IMETHODIMP
 mozJSComponentLoader::Import(const nsACString& registryLocation,
-                             const JS::Value& targetVal_,
+                             const JS::Value& targetValArg,
                              JSContext* cx,
                              uint8_t optionalArgc,
                              JS::Value* retval)
 {
     JSAutoRequest ar(cx);
+    MOZ_ASSERT(nsContentUtils::IsCallerChrome());
 
-    JS::Value targetVal = targetVal_;
-    JSObject *targetObject = NULL;
-
-    MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+    RootedValue targetVal(cx, targetValArg);
+    RootedObject targetObject(cx, nullptr);
     if (optionalArgc) {
         // The caller passed in the optional second argument. Get it.
         if (targetVal.isObject()) {
             // If we're passing in something like a content DOM window, chances
             // are the caller expects the properties to end up on the object
             // proper and not on the Xray holder. This is dubious, but can be used
             // during testing. Given that dumb callers can already leak JSMs into
             // content by passing a raw content JS object (where Xrays aren't
             // possible), we aim for consistency here. Waive xray.
             if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
-                !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
+                !WrapperFactory::WaiveXrayAndWrap(cx, targetVal.address()))
             {
                 return NS_ERROR_FAILURE;
             }
             targetObject = &targetVal.toObject();
         } else if (!targetVal.isNull()) {
             // If targetVal isNull(), we actually want to leave targetObject null.
             // Not doing so breaks |make package|.
             return ReportOnCaller(cx, ERROR_SCOPE_OBJ,
@@ -1217,52 +1210,59 @@ mozJSComponentLoader::Import(const nsACS
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     Maybe<JSAutoCompartment> ac;
     if (targetObject) {
         ac.construct(cx, targetObject);
     }
 
-    JSObject *globalObj = nullptr;
-    nsresult rv = ImportInto(registryLocation, targetObject, cx, &globalObj);
+    RootedObject global(cx);
+    nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
 
-    if (globalObj && !JS_WrapObject(cx, &globalObj)) {
-        NS_ERROR("can't wrap return value");
-        return NS_ERROR_FAILURE;
+    if (global) {
+        if (!JS_WrapObject(cx, global.address())) {
+            NS_ERROR("can't wrap return value");
+            return NS_ERROR_FAILURE;
+        }
+
+        *retval = JS::ObjectValue(*global);
     }
-
-    *retval = OBJECT_TO_JSVAL(globalObj);
-
     return rv;
 }
 
 /* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
                                      in JSObjectPtr targetObj); */
 NS_IMETHODIMP
 mozJSComponentLoader::ImportInto(const nsACString & aLocation,
-                                 JSObject * targetObj,
+                                 JSObject *aTargetObj,
                                  nsAXPCNativeCallContext * cc,
-                                 JSObject * *_retval)
+                                 JSObject **_retval)
 {
     JSContext *callercx;
     nsresult rv = cc->GetJSContext(&callercx);
     NS_ENSURE_SUCCESS(rv, rv);
-    return ImportInto(aLocation, targetObj, callercx, _retval);
+
+    RootedObject targetObject(callercx, aTargetObj);
+    RootedObject global(callercx);
+    rv = ImportInto(aLocation, targetObject, callercx, &global);
+    NS_ENSURE_SUCCESS(rv, rv);
+    *_retval = global;
+    return NS_OK;
 }
 
 nsresult
-mozJSComponentLoader::ImportInto(const nsACString & aLocation,
-                                 JSObject * targetObj,
-                                 JSContext * callercx,
-                                 JSObject * *_retval)
+mozJSComponentLoader::ImportInto(const nsACString &aLocation,
+                                 JS::HandleObject targetObj,
+                                 JSContext *callercx,
+                                 JS::MutableHandleObject vp)
 {
+    vp.set(nullptr);
+
     nsresult rv;
-    *_retval = nullptr;
-
     if (!mInitialized) {
         rv = ReallyInit();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1312,31 +1312,29 @@ mozJSComponentLoader::ImportInto(const n
     ModuleEntry* mod;
     nsAutoPtr<ModuleEntry> newEntry;
     if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) {
         newEntry = new ModuleEntry;
         if (!newEntry)
             return NS_ERROR_OUT_OF_MEMORY;
         mInProgressImports.Put(key, newEntry);
 
-        JS::Anchor<jsval> exception(JSVAL_VOID);
+        RootedValue exception(callercx);
         rv = ObjectForLocation(sourceLocalFile, resURI, &newEntry->obj,
-                               &newEntry->location, &exception.get());
+                               &newEntry->location, true, &exception);
 
         mInProgressImports.Remove(key);
 
         if (NS_FAILED(rv)) {
-            *_retval = nullptr;
-
-            if (!JSVAL_IS_VOID(exception.get())) {
+            if (!exception.isUndefined()) {
                 // An exception was thrown during compilation. Propagate it
                 // out to our caller so they can report it.
-                if (!JS_WrapValue(callercx, &exception.get()))
+                if (!JS_WrapValue(callercx, exception.address()))
                     return NS_ERROR_OUT_OF_MEMORY;
-                JS_SetPendingException(callercx, exception.get());
+                JS_SetPendingException(callercx, exception);
                 return NS_OK;
             }
 
             // Something failed, but we don't know what it is, guess.
             return NS_ERROR_FILE_NOT_FOUND;
         }
 
         // Set the location information for the new global, so that tools like
@@ -1344,73 +1342,72 @@ mozJSComponentLoader::ImportInto(const n
         if (!mReuseLoaderGlobal) {
             xpc::SetLocationForGlobal(newEntry->obj, aLocation);
         }
 
         mod = newEntry;
     }
 
     NS_ASSERTION(mod->obj, "Import table contains entry with no object");
-    *_retval = mod->obj;
+    vp.set(mod->obj);
 
     if (targetObj) {
         JSCLContextHelper cxhelper(this);
         JSAutoCompartment ac(mContext, mod->obj);
 
-        JS::Value symbols;
+        RootedValue symbols(mContext);
         if (!JS_GetProperty(mContext, mod->obj,
-                            "EXPORTED_SYMBOLS", &symbols)) {
+                            "EXPORTED_SYMBOLS", symbols.address())) {
             return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
                                   PromiseFlatCString(aLocation).get());
         }
 
         if (!symbols.isObject() ||
             !JS_IsArrayObject(mContext, &symbols.toObject())) {
             return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
                                   PromiseFlatCString(aLocation).get());
         }
 
-        JSObject *symbolsObj = &symbols.toObject();
+        RootedObject symbolsObj(mContext, &symbols.toObject());
 
         // Iterate over symbols array, installing symbols on targetObj:
 
         uint32_t symbolCount = 0;
         if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) {
             return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
                                   PromiseFlatCString(aLocation).get());
         }
 
 #ifdef DEBUG
         nsAutoCString logBuffer;
 #endif
 
+        RootedValue value(mContext);
+        RootedId symbolId(mContext);
         for (uint32_t i = 0; i < symbolCount; ++i) {
-            jsval val;
-            jsid symbolId;
-
-            if (!JS_GetElement(mContext, symbolsObj, i, &val) ||
-                !JSVAL_IS_STRING(val) ||
-                !JS_ValueToId(mContext, val, &symbolId)) {
+            if (!JS_GetElement(mContext, symbolsObj, i, value.address()) ||
+                !value.isString() ||
+                !JS_ValueToId(mContext, value, symbolId.address())) {
                 return ReportOnCaller(cxhelper, ERROR_ARRAY_ELEMENT,
                                       PromiseFlatCString(aLocation).get(), i);
             }
 
-            if (!JS_GetPropertyById(mContext, mod->obj, symbolId, &val)) {
+            if (!JS_GetPropertyById(mContext, mod->obj, symbolId, value.address())) {
                 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
                 if (!bytes)
                     return NS_ERROR_FAILURE;
                 return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
                                       PromiseFlatCString(aLocation).get(),
                                       bytes.ptr());
             }
 
             JSAutoCompartment target_ac(mContext, targetObj);
 
-            if (!JS_WrapValue(mContext, &val) ||
-                !JS_SetPropertyById(mContext, targetObj, symbolId, &val)) {
+            if (!JS_WrapValue(mContext, value.address()) ||
+                !JS_SetPropertyById(mContext, targetObj, symbolId, value.address())) {
                 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
                 if (!bytes)
                     return NS_ERROR_FAILURE;
                 return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
                                       PromiseFlatCString(aLocation).get(),
                                       bytes.ptr());
             }
 #ifdef DEBUG
--- a/js/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/xpconnect/loader/mozJSComponentLoader.h
@@ -45,17 +45,18 @@ class mozJSComponentLoader : public mozi
     NS_DECL_NSIOBSERVER
 
     mozJSComponentLoader();
     virtual ~mozJSComponentLoader();
 
     // ModuleLoader
     const mozilla::Module* LoadModule(mozilla::FileLocation &aFile);
 
-    nsresult FindTargetObject(JSContext* aCx, JSObject** aTargetObject);
+    nsresult FindTargetObject(JSContext* aCx,
+                              JS::MutableHandleObject aTargetObject);
 
     static mozJSComponentLoader* Get() { return sSelf; }
 
     void NoteSubScript(JSScript* aScript, JSObject* aThisObject);
 
  protected:
     static mozJSComponentLoader* sSelf;
 
@@ -67,22 +68,23 @@ class mozJSComponentLoader : public mozi
                                        nsIURI *aComponent,
                                        bool aReuseLoaderGlobal,
                                        bool *aRealFile);
 
     nsresult ObjectForLocation(nsIFile* aComponentFile,
                                nsIURI *aComponent,
                                JSObject **aObject,
                                char **location,
-                               jsval *exception);
+                               bool aCatchException,
+                               JS::MutableHandleValue aException);
 
-    nsresult ImportInto(const nsACString & aLocation,
-                        JSObject * targetObj,
-                        JSContext * callercx,
-                        JSObject * *_retval);
+    nsresult ImportInto(const nsACString &aLocation,
+                        JS::HandleObject targetObj,
+                        JSContext *callercx,
+                        JS::MutableHandleObject vp);
 
     nsCOMPtr<nsIComponentManager> mCompMgr;
     nsCOMPtr<nsIJSRuntimeService> mRuntimeService;
     nsCOMPtr<nsIThreadJSContextStack> mContextStack;
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
     nsCOMPtr<nsIXPConnectJSObjectHolder> mLoaderGlobal;
     JSRuntime *mRuntime;
     JSContext *mContext;
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -193,17 +193,17 @@ mozJSSubScriptLoader::LoadSubScript(cons
 
         rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
         if (NS_FAILED(rv) || !mSystemPrincipal)
             return rv;
     }
 
     JSAutoRequest ar(cx);
 
-    JSObject* targetObj;
+    JS::RootedObject targetObj(cx);
     mozJSComponentLoader* loader = mozJSComponentLoader::Get();
     rv = loader->FindTargetObject(cx, &targetObj);
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool reusingGlobal = !JS_IsGlobalObject(targetObj);
 
     // We base reusingGlobal off of what the loader told us, but we may not
     // actually be using that object.
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -329,17 +329,17 @@ nsXPCComponents_Interfaces::NewResolve(n
 
     if (!mManager || !JSID_IS_STRING(id))
         return NS_OK;
 
     JSAutoByteString name;
     RootedString str(cx, JSID_TO_STRING(id));
 
     // we only allow interfaces by name here
-    if (name.encodeLatin1(cx, JSID_TO_STRING(id)) && name.ptr()[0] != '{') {
+    if (name.encodeLatin1(cx, str) && name.ptr()[0] != '{') {
         nsCOMPtr<nsIInterfaceInfo> info;
         mManager->GetInfoForName(name.ptr(), getter_AddRefs(info));
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid =
             dont_AddRef(static_cast<nsIJSIID*>(nsJSIID::NewID(info)));
 
@@ -632,21 +632,21 @@ nsXPCComponents_InterfacesByID::NewEnume
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::NewResolve(nsIXPConnectWrappedNative *wrapper,
                                            JSContext *cx, JSObject *objArg,
                                            jsid idArg, uint32_t flags,
                                            JSObject **objp, bool *_retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
-    RootedString str(cx, JSID_TO_STRING(id));
 
     if (!mManager || !JSID_IS_STRING(id))
         return NS_OK;
 
+    RootedString str(cx, JSID_TO_STRING(id));
     if (38 != JS_GetStringLength(str))
         return NS_OK;
 
     if (const jschar *name = JS_GetInternedStringChars(str)) {
         nsID iid;
         if (!iid.Parse(NS_ConvertUTF16toUTF8(name).get()))
             return NS_OK;
 
@@ -1180,42 +1180,47 @@ IsRegisteredCLSID(const char* str)
         return false;
 
     return registered;
 }
 
 /* bool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id, in uint32_t flags, out JSObjectPtr objp); */
 NS_IMETHODIMP
 nsXPCComponents_ClassesByID::NewResolve(nsIXPConnectWrappedNative *wrapper,
-                                        JSContext * cx, JSObject * obj,
-                                        jsid id, uint32_t flags,
-                                        JSObject * *objp, bool *_retval)
-{
+                                        JSContext *cx, JSObject *objArg,
+                                        jsid idArg, uint32_t flags,
+                                        JSObject **objp, bool *_retval)
+{
+    RootedObject obj(cx, objArg);
+    RootedId id(cx, idArg);
+
+    if (!JSID_IS_STRING(id))
+        return NS_OK;
+
     JSAutoByteString name;
-
-    if (JSID_IS_STRING(id) &&
-        name.encodeLatin1(cx, JSID_TO_STRING(id)) &&
-        name.ptr()[0] == '{' &&
-        IsRegisteredCLSID(name.ptr())) { // we only allow canonical CLSIDs here
+    RootedString str(cx, JSID_TO_STRING(id));
+    if (name.encodeLatin1(cx, str) && name.ptr()[0] == '{' &&
+        IsRegisteredCLSID(name.ptr())) // we only allow canonical CLSIDs here
+    {
         nsCOMPtr<nsIJSCID> nsid =
             dont_AddRef(static_cast<nsIJSCID*>(nsJSCID::NewID(name.ptr())));
         if (nsid) {
             nsCOMPtr<nsIXPConnect> xpc;
             wrapper->GetXPConnect(getter_AddRefs(xpc));
             if (xpc) {
                 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
                 if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
                                                  static_cast<nsIJSCID*>(nsid),
                                                  NS_GET_IID(nsIJSCID),
                                                  getter_AddRefs(holder)))) {
-                    JSObject* idobj;
-                    if (holder && NS_SUCCEEDED(holder->GetJSObject(&idobj))) {
+                    RootedObject idobj(cx);
+                    if (holder && NS_SUCCEEDED(holder->GetJSObject(idobj.address()))) {
                         *objp = obj;
                         *_retval = JS_DefinePropertyById(cx, obj, id,
-                                                         OBJECT_TO_JSVAL(idobj),
+                                                         ObjectValue(*idobj),
                                                          nullptr, nullptr,
                                                          JSPROP_ENUMERATE |
                                                          JSPROP_READONLY |
                                                          JSPROP_PERMANENT);
                     }
                 }
             }
         }
@@ -1873,25 +1878,34 @@ struct MOZ_STACK_CLASS ExceptionArgParse
          *
          *   result:    Result code (see argument 1).
          *   stack:     Call stack (see argument 2).
          *   data:      User data (see argument 3).
          */
         if (argc > 0 && !parseMessage(argv[0]))
             return false;
         if (argc > 1) {
-            if (argv[1].isObject())
-                return parseOptionsObject(argv[1].toObject());
-            if (!parseResult(argv[1]))
+            if (argv[1].isObject()) {
+                RootedObject obj(cx, &argv[1].toObject());
+                return parseOptionsObject(obj);
+            }
+            RootedValue v(cx, argv[1]);
+            if (!parseResult(v))
                 return false;
         }
-        if (argc > 2 && !parseStack(argv[2]))
-            return false;
-        if (argc > 3 && !parseData(argv[3]))
-            return false;
+        if (argc > 2) {
+            RootedValue stack(cx, argv[2]);
+            if (!parseStack(stack))
+                return false;
+        }
+        if (argc > 3) {
+            RootedValue data(cx, argv[3]);
+            if (!parseData(data))
+                return false;
+        }
         return true;
     }
 
   protected:
 
     /*
      * Parsing helpers.
      */
@@ -1899,76 +1913,76 @@ struct MOZ_STACK_CLASS ExceptionArgParse
     bool parseMessage(JS::Value &v) {
         JSString *str = JS_ValueToString(cx, v);
         if (!str)
            return false;
         eMsg = messageBytes.encodeLatin1(cx, str);
         return !!eMsg;
     }
 
-    bool parseResult(JS::Value &v) {
+    bool parseResult(HandleValue v) {
         return JS_ValueToECMAUint32(cx, v, (uint32_t*) &eResult);
     }
 
-    bool parseStack(JS::Value &v) {
+    bool parseStack(HandleValue v) {
         if (!v.isObject()) {
             // eStack has already been initialized to null, which is what we want
             // for any non-object values (including null).
             return true;
         }
 
-        return NS_SUCCEEDED(xpc->WrapJS(cx, JSVAL_TO_OBJECT(v),
+        return NS_SUCCEEDED(xpc->WrapJS(cx, &v.toObject(),
                                         NS_GET_IID(nsIStackFrame),
                                         getter_AddRefs(eStack)));
     }
 
-    bool parseData(JS::Value &v) {
+    bool parseData(HandleValue v) {
         if (!v.isObject()) {
             // eData has already been initialized to null, which is what we want
             // for any non-object values (including null).
             return true;
         }
 
         return NS_SUCCEEDED(xpc->WrapJS(cx, &v.toObject(),
                                         NS_GET_IID(nsISupports),
                                         getter_AddRefs(eData)));
     }
 
-    bool parseOptionsObject(JSObject &obj) {
-        JS::Value v;
+    bool parseOptionsObject(HandleObject obj) {
+        RootedValue v(cx);
 
         if (!getOption(obj, "result", &v) ||
             (!v.isUndefined() && !parseResult(v)))
             return false;
 
         if (!getOption(obj, "stack", &v) ||
             (!v.isUndefined() && !parseStack(v)))
             return false;
 
         if (!getOption(obj, "data", &v) ||
             (!v.isUndefined() && !parseData(v)))
             return false;
 
         return true;
     }
 
-    bool getOption(JSObject &obj, const char *name, JS::Value *rv) {
+    bool getOption(HandleObject obj, const char *name, MutableHandleValue rv) {
         // Look for the property.
         JSBool found;
-        if (!JS_HasProperty(cx, &obj, name, &found))
+        if (!JS_HasProperty(cx, obj, name, &found))
             return false;
 
         // If it wasn't found, indicate with undefined.
         if (!found) {
-            *rv = JSVAL_VOID;
+            rv.setUndefined();
             return true;
         }
 
         // Get the property.
-        return JS_GetProperty(cx, &obj, name, rv);
+        return JS_GetProperty(cx, obj, name, rv.address());
     }
 
     /*
      * Internal data members.
      */
 
     // If there's a non-default exception string, hold onto the allocated bytes.
     JSAutoByteString messageBytes;
@@ -2009,26 +2023,26 @@ nsXPCComponents_Exception::CallOrConstru
 
     nsCOMPtr<nsIException> e;
     nsXPCException::NewException(args.eMsg, args.eResult, args.eStack,
                                  args.eData, getter_AddRefs(e));
     if (!e)
         return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    JSObject* newObj = nullptr;
+    RootedObject newObj(cx);
 
     if (NS_FAILED(xpc->WrapNative(cx, obj, e, NS_GET_IID(nsIXPCException),
                                   getter_AddRefs(holder))) || !holder ||
-        NS_FAILED(holder->GetJSObject(&newObj)) || !newObj) {
+        NS_FAILED(holder->GetJSObject(newObj.address())) || !newObj) {
         return ThrowAndFail(NS_ERROR_XPC_CANT_CREATE_WN, cx, _retval);
     }
 
     if (vp)
-        *vp = OBJECT_TO_JSVAL(newObj);
+        *vp = ObjectValue(*newObj);
 
     return NS_OK;
 }
 
 /* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
 NS_IMETHODIMP
 nsXPCComponents_Exception::HasInstance(nsIXPConnectWrappedNative *wrapper,
                                        JSContext * cx, JSObject * obj,
@@ -2526,52 +2540,52 @@ nsXPCComponents_Constructor::CallOrConst
     // initialization params for the Constructor object we will create
     nsCOMPtr<nsIJSCID> cClassID;
     nsCOMPtr<nsIJSIID> cInterfaceID;
     const char*        cInitializer = nullptr;
     JSAutoByteString  cInitializerBytes;
 
     if (argc >= 3) {
         // argv[2] is an initializer function or property name
-        JSString* str = JS_ValueToString(cx, argv[2]);
+        RootedString str(cx, JS_ValueToString(cx, argv[2]));
         if (!str || !(cInitializer = cInitializerBytes.encodeLatin1(cx, str)))
             return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
     }
 
     if (argc >= 2) {
         // argv[1] is an iid name string
         // XXXjband support passing "Components.interfaces.foo"?
 
         nsCOMPtr<nsIXPCComponents_Interfaces> ifaces;
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-        JSObject* ifacesObj = nullptr;
+        RootedObject ifacesObj(cx);
 
         // we do the lookup by asking the Components.interfaces object
         // for the property with this name - i.e. we let its caching of these
         // nsIJSIID objects work for us.
 
         if (NS_FAILED(comp->GetInterfaces(getter_AddRefs(ifaces))) ||
             NS_FAILED(xpc->WrapNative(cx, obj, ifaces,
                                       NS_GET_IID(nsIXPCComponents_Interfaces),
                                       getter_AddRefs(holder))) || !holder ||
-            NS_FAILED(holder->GetJSObject(&ifacesObj)) || !ifacesObj) {
+            NS_FAILED(holder->GetJSObject(ifacesObj.address())) || !ifacesObj) {
             return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
         }
 
-        JSString* str = JS_ValueToString(cx, argv[1]);
-        jsid id;
-        if (!str || !JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
+        RootedString str(cx, JS_ValueToString(cx, argv[1]));
+        RootedId id(cx);
+        if (!str || !JS_ValueToId(cx, StringValue(str), id.address()))
             return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
 
-        jsval val;
-        if (!JS_GetPropertyById(cx, ifacesObj, id, &val) || JSVAL_IS_PRIMITIVE(val))
+        RootedValue val(cx);
+        if (!JS_GetPropertyById(cx, ifacesObj, id, val.address()) || val.isPrimitive())
             return ThrowAndFail(NS_ERROR_XPC_BAD_IID, cx, _retval);
 
         nsCOMPtr<nsIXPConnectWrappedNative> wn;
-        if (NS_FAILED(xpc->GetWrappedNativeOfJSObject(cx, JSVAL_TO_OBJECT(val),
+        if (NS_FAILED(xpc->GetWrappedNativeOfJSObject(cx, &val.toObject(),
                                                       getter_AddRefs(wn))) || !wn ||
             !(cInterfaceID = do_QueryWrappedNative(wn))) {
             return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
         }
     } else {
         nsCOMPtr<nsIInterfaceInfo> info;
         xpc->GetInfoForIID(&NS_GET_IID(nsISupports), getter_AddRefs(info));
 
@@ -2589,33 +2603,33 @@ nsXPCComponents_Constructor::CallOrConst
         // XXXjband support passing "Components.classes.foo"?
 
         // we do the lookup by asking the Components.classes object
         // for the property with this name - i.e. we let its caching of these
         // nsIJSCID objects work for us.
 
         nsCOMPtr<nsIXPCComponents_Classes> classes;
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-        JSObject* classesObj = nullptr;
+        RootedObject classesObj(cx);
 
         if (NS_FAILED(comp->GetClasses(getter_AddRefs(classes))) ||
             NS_FAILED(xpc->WrapNative(cx, obj, classes,
                                       NS_GET_IID(nsIXPCComponents_Classes),
                                       getter_AddRefs(holder))) || !holder ||
-            NS_FAILED(holder->GetJSObject(&classesObj)) || !classesObj) {
+            NS_FAILED(holder->GetJSObject(classesObj.address())) || !classesObj) {
             return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
         }
 
-        JSString* str = JS_ValueToString(cx, argv[0]);
-        jsid id;
-        if (!str || !JS_ValueToId(cx, STRING_TO_JSVAL(str), &id))
+        RootedString str(cx, JS_ValueToString(cx, argv[0]));
+        RootedId id(cx);
+        if (!str || !JS_ValueToId(cx, StringValue(str), id.address()))
             return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
 
-        jsval val;
-        if (!JS_GetPropertyById(cx, classesObj, id, &val) || JSVAL_IS_PRIMITIVE(val))
+        RootedValue val(cx);
+        if (!JS_GetPropertyById(cx, classesObj, id, val.address()) || val.isPrimitive())
             return ThrowAndFail(NS_ERROR_XPC_BAD_CID, cx, _retval);
 
         nsCOMPtr<nsIXPConnectWrappedNative> wn;
         if (NS_FAILED(xpc->GetWrappedNativeOfJSObject(cx, JSVAL_TO_OBJECT(val),
                                                       getter_AddRefs(wn))) || !wn ||
             !(cClassID = do_QueryWrappedNative(wn))) {
             return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
         }
@@ -2623,26 +2637,26 @@ nsXPCComponents_Constructor::CallOrConst
 
     nsCOMPtr<nsIXPCConstructor> ctor =
         static_cast<nsIXPCConstructor*>
                    (new nsXPCConstructor(cClassID, cInterfaceID, cInitializer));
     if (!ctor)
         return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder2;
-    JSObject* newObj = nullptr;
+    RootedObject newObj(cx);
 
     if (NS_FAILED(xpc->WrapNative(cx, obj, ctor, NS_GET_IID(nsIXPCConstructor),
                                   getter_AddRefs(holder2))) || !holder2 ||
-        NS_FAILED(holder2->GetJSObject(&newObj)) || !newObj) {
+        NS_FAILED(holder2->GetJSObject(newObj.address())) || !newObj) {
         return ThrowAndFail(NS_ERROR_XPC_CANT_CREATE_WN, cx, _retval);
     }
 
     if (vp)
-        *vp = OBJECT_TO_JSVAL(newObj);
+        *vp = ObjectValue(*newObj);
 
     return NS_OK;
 }
 
 /* bool hasInstance (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval val, out bool bp); */
 NS_IMETHODIMP
 nsXPCComponents_Constructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
                                          JSContext * cx, JSObject * obj,
@@ -2763,47 +2777,49 @@ nsXPCComponents_Utils::LookupMethod(cons
 
         // Now, try to create an Xray wrapper around the object. This won't work
         // if the object isn't Xray-able. In that case, we throw.
         JSObject *xray = WrapperFactory::WrapForSameCompartmentXray(cx, obj);
         if (!xray)
             return NS_ERROR_XPC_BAD_CONVERT_JS;
 
         // Alright, now do the lookup.
-        *retval = JSVAL_VOID;
+        *retval = UndefinedValue();
         Rooted<JSPropertyDescriptor> desc(cx);
         if (!JS_GetPropertyDescriptorById(cx, xray, methodId, 0, desc.address()))
             return NS_ERROR_FAILURE;
 
         // First look for a method value. If that's not there, try a getter,
         // since historically lookupMethod also works for getters.
         JSObject *methodObj = desc.value().isObject() ? &desc.value().toObject() : NULL;
         if (!methodObj && desc.hasGetterObject())
             methodObj = desc.getterObject();
 
         // Callers of this function seem to expect bound methods. Make it happen.
         // Note that this is unnecessary until bug 658909 is fixed.
         if (methodObj && JS_ObjectIsCallable(cx, methodObj))
             methodObj = JS_BindCallable(cx, methodObj, obj);
 
         // Set the return value if appropriate.
-        *retval = methodObj ? ObjectValue(*methodObj) : JSVAL_VOID;
+        *retval = methodObj ? ObjectValue(*methodObj) : UndefinedValue();
     }
 
     // Now that we've left the target compartment, wrap for the caller.
     if (!JS_WrapValue(cx, retval))
         return NS_ERROR_FAILURE;;
 
     return NS_OK;
 }
 
 /* void reportError (); */
 NS_IMETHODIMP
-nsXPCComponents_Utils::ReportError(const JS::Value &error, JSContext *cx)
-{
+nsXPCComponents_Utils::ReportError(const JS::Value &errorArg, JSContext *cx)
+{
+    RootedValue error(cx, errorArg);
+
     // This function shall never fail! Silently eat any failure conditions.
 
     nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
 
     nsCOMPtr<nsIScriptError> scripterr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
 
     if (!scripterr || !console)
         return NS_OK;
@@ -2833,20 +2849,19 @@ nsXPCComponents_Utils::ReportError(const
                 column, err->flags, "XPConnect JavaScript", innerWindowID);
         NS_ENSURE_SUCCESS(rv, NS_OK);
 
         console->LogMessage(scripterr);
         return NS_OK;
     }
 
     // It's not a JS Error object, so we synthesize as best we're able.
-    JSString *msgstr = JS_ValueToString(cx, error);
-    if (!msgstr) {
+    RootedString msgstr(cx, JS_ValueToString(cx, error));
+    if (!msgstr)
         return NS_OK;
-    }
 
     nsCOMPtr<nsIStackFrame> frame;
     nsXPConnect *xpc = nsXPConnect::GetXPConnect();
     if (xpc)
         xpc->GetCurrentJSStack(getter_AddRefs(frame));
 
     nsXPIDLCString fileName;
     int32_t lineNo = 0;
@@ -2990,17 +3005,17 @@ CreateXMLHttpRequest(JSContext *cx, unsi
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (!ssm)
         return false;
 
     nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
     if (!subjectPrincipal)
         return false;
 
-    JSObject *global = JS_GetGlobalForScopeChain(cx);
+    RootedObject global(cx, JS_GetGlobalForScopeChain(cx));
     MOZ_ASSERT(global);
 
     nsIScriptObjectPrincipal *sop =
         static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(global));
     nsCOMPtr<nsIGlobalObject> iglobal = do_QueryInterface(sop);
 
     nsCOMPtr<nsIXMLHttpRequest> xhr = new nsXMLHttpRequest();
     nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr);
@@ -4285,37 +4300,37 @@ nsXPCComponents_Utils::CreateArrayIn(con
         return NS_ERROR_FAILURE;
 
     *rval = ObjectValue(*obj);
     return NS_OK;
 }
 
 /* jsval createDateIn(in jsval vobj, in long long msec); */
 NS_IMETHODIMP
-nsXPCComponents_Utils::CreateDateIn(const Value &vobj, int64_t msec,JSContext *cx, Value *rval)
+nsXPCComponents_Utils::CreateDateIn(const Value &vobj, int64_t msec, JSContext *cx, Value *rval)
 {
     if (!cx)
         return NS_ERROR_FAILURE;
 
     // first argument must be an object
-    if (JSVAL_IS_PRIMITIVE(vobj))
+    if (!vobj.isObject())
         return NS_ERROR_XPC_BAD_CONVERT_JS;
 
-    JSObject *scope = js::UncheckedUnwrap(JSVAL_TO_OBJECT(vobj));
-    JSObject *obj;
+    RootedObject obj(cx);
     {
+        JSObject *scope = js::UncheckedUnwrap(&vobj.toObject());
         JSAutoCompartment ac(cx, scope);
         obj =  JS_NewDateObjectMsec(cx, msec);
         if (!obj)
             return NS_ERROR_FAILURE;
     }
 
-    if (!JS_WrapObject(cx, &obj))
+    if (!JS_WrapObject(cx, obj.address()))
         return NS_ERROR_FAILURE;
-    *rval = OBJECT_TO_JSVAL(obj);
+    *rval = ObjectValue(*obj);
     return NS_OK;
 }
 
 JSBool
 FunctionWrapper(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -204,41 +204,56 @@ IsFrameId(JSContext *cx, JSObject *objAr
 
 static bool
 IsWindow(const char *name)
 {
     return name[0] == 'W' && !strcmp(name, "Window");
 }
 
 bool
-AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapper, jsid id,
+AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapperArg, jsid idArg,
                                           Wrapper::Action act)
 {
     if (!XPCWrapper::GetSecurityManager())
         return true;
 
     if (act == Wrapper::CALL)
         return true;
 
-    JSObject *obj = Wrapper::wrappedObject(wrapper);
+    RootedId id(cx, idArg);
+    RootedObject wrapper(cx, wrapperArg);
+    RootedObject obj(cx, Wrapper::wrappedObject(wrapper));
 
     const char *name;
     js::Class *clasp = js::GetObjectClass(obj);
     NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here");
     if (clasp->ext.innerObject)
         name = "Window";
     else
         name = clasp->name;
 
     if (JSID_IS_STRING(id)) {
         if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
             return true;
     }
 
-    return IsWindow(name) && IsFrameId(cx, obj, id);
+    // Check for frame IDs. If we're resolving named frames, make sure to only
+    // resolve ones that don't shadow native properties. See bug 860494.
+    if (IsWindow(name)) {
+        if (JSID_IS_STRING(id) && !XrayUtils::IsXrayResolving(cx, wrapper, id)) {
+            bool wouldShadow = false;
+            if (!XrayUtils::HasNativeProperty(cx, wrapper, id, &wouldShadow) ||
+                wouldShadow)
+            {
+                return false;
+            }
+        }
+        return IsFrameId(cx, obj, id);
+    }
+    return false;
 }
 
 bool
 AccessCheck::isSystemOnlyAccessPermitted(JSContext *cx)
 {
     MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
     return nsContentUtils::CanAccessNativeAnon();
 }
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -22,16 +22,17 @@
 #include "jsapi.h"
 #include "nsJSUtils.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "nsGlobalWindow.h"
 
 using namespace mozilla::dom;
 using namespace JS;
+using namespace mozilla;
 
 using js::PropertyDescriptor;
 using js::Wrapper;
 
 namespace xpc {
 
 static const uint32_t JSSLOT_RESOLVING = 0;
 
@@ -129,16 +130,23 @@ public:
 
 class XrayTraits
 {
 public:
     static JSObject* getTargetObject(JSObject *wrapper) {
         return js::UncheckedUnwrap(wrapper, /* stopAtOuter = */ false);
     }
 
+    virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
+                                       HandleObject holder, HandleId id,
+                                       JSPropertyDescriptor *desc, unsigned flags) = 0;
+    // NB: resolveOwnProperty may decide whether or not to cache what it finds
+    // on the holder. If the result is not cached, the lookup will happen afresh
+    // for each access, which is the right thing for things like dynamic NodeList
+    // properties.
     virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper,
                                     HandleObject wrapper, HandleObject holder,
                                     HandleId id, JSPropertyDescriptor *desc, unsigned flags);
 
     static bool call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args)
     {
         MOZ_NOT_REACHED("Call trap currently implemented only for XPCWNs");
     }
@@ -174,19 +182,19 @@ private:
                                   HandleObject exclusiveGlobal);
 };
 
 class XPCWrappedNativeXrayTraits : public XrayTraits
 {
 public:
     static const XrayType Type = XrayForWrappedNative;
 
-    static bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
-                                      HandleObject holder, HandleId id,
-                                      JSPropertyDescriptor *desc, unsigned flags);
+    virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
+                                       HandleObject holder, HandleId id,
+                                       JSPropertyDescriptor *desc, unsigned flags);
     virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper,
                                     HandleObject holder, HandleId id,
                                     JSPropertyDescriptor *desc, unsigned flags);
     static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                PropertyDescriptor *desc, PropertyDescriptor &existingDesc,
                                bool *defined);
     static bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                                AutoIdVector &props);
@@ -218,19 +226,19 @@ public:
     static XPCWrappedNativeXrayTraits singleton;
 };
 
 class DOMXrayTraits : public XrayTraits
 {
 public:
     static const XrayType Type = XrayForDOMObject;
 
-    static bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
-                                      HandleObject holder, HandleId id,
-                                      JSPropertyDescriptor *desc, unsigned flags);
+    virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper,
+                                       HandleObject holder, HandleId id,
+                                       JSPropertyDescriptor *desc, unsigned flags);
     virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper,
                                     HandleObject holder, HandleId id,
                                     JSPropertyDescriptor *desc, unsigned flags);
     static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                PropertyDescriptor *desc, PropertyDescriptor &existingDesc,
                                bool *defined);
     static bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                                AutoIdVector &props);
@@ -1031,17 +1039,29 @@ XPCWrappedNativeXrayTraits::resolveOwnPr
         }
 
 #ifdef DEBUG
         NS_ASSERTION(!pobj || (JS_HasPropertyById(cx, holder, id, &hasProp) &&
                      hasProp), "id got defined somewhere else?");
 #endif
     }
 
-    return true;
+    // resolveOwnProperty must return a non-empty |desc| if and only if an |own|
+    // property was found on the object. However, given how the NewResolve setup
+    // works, we can't run the resolve hook if the holder already has a property
+    // of the same name. So if there was a pre-existing property on the holder,
+    // we have to use it. But we have no way of knowing if it corresponded to an
+    // |own| or non-|own| property, since both get cached on the holder and the
+    // |own|-ness information is lost.
+    //
+    // So we just over-zealously call things |own| here. This can cause us to
+    // return non-|own| properties from Object.getOwnPropertyDescriptor if
+    // lookups are performed in a certain order, but we can probably live with
+    // that until XPCWN Xrays go away with the new DOM bindings.
+    return JS_GetPropertyDescriptorById(cx, holder, id, 0, desc);
 }
 
 bool
 XPCWrappedNativeXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                            PropertyDescriptor *desc,
                                            PropertyDescriptor &existingDesc,
                                            bool *defined)
 {
@@ -1326,16 +1346,55 @@ IsXrayResolving(JSContext *cx, HandleObj
     {
         return false;
     }
     JSObject *holder =
       XPCWrappedNativeXrayTraits::singleton.ensureHolder(cx, wrapper);
     return XPCWrappedNativeXrayTraits::isResolving(cx, holder, id);
 }
 
+bool
+HasNativeProperty(JSContext *cx, HandleObject wrapper, HandleId id, bool *hasProp)
+{
+    MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
+    XrayTraits *traits = GetXrayTraits(wrapper);
+    MOZ_ASSERT(traits);
+    RootedObject holder(cx, traits->ensureHolder(cx, wrapper));
+    NS_ENSURE_TRUE(holder, false);
+    *hasProp = false;
+    JSPropertyDescriptor desc;
+    Wrapper *handler = Wrapper::wrapperHandler(wrapper);
+
+    // Try resolveOwnProperty.
+    Maybe<ResolvingId> resolvingId;
+    if (traits == &XPCWrappedNativeXrayTraits::singleton)
+        resolvingId.construct(wrapper, id);
+    if (!traits->resolveOwnProperty(cx, *handler, wrapper, holder, id, &desc, 0))
+        return false;
+    if (desc.obj) {
+        *hasProp = true;
+        return true;
+    }
+
+    // Try the holder.
+    JSBool found = false;
+    if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
+        return false;
+    if (found) {
+        *hasProp = true;
+        return true;
+    }
+
+    // Try resolveNativeProperty.
+    if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc, 0))
+        return false;
+    *hasProp = !!desc.obj;
+    return true;
+}
+
 } // namespace XrayUtils
 
 static JSBool
 XrayToString(JSContext *cx, unsigned argc, jsval *vp)
 {
     RootedObject  wrapper(cx, JS_THIS_OBJECT(cx, vp));
     if (!wrapper)
         return false;
@@ -1465,58 +1524,74 @@ XrayWrapper<Base, Traits>::getPropertyDe
         desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED;
         desc->getter = wrappedJSObject_getter;
         desc->setter = NULL;
         desc->shortid = 0;
         desc->value = JSVAL_VOID;
         return true;
     }
 
+    // Ordering is important here.
+    //
+    // We first need to call resolveOwnProperty, even before checking the holder,
+    // because there might be a new dynamic |own| property that appears and
+    // shadows a previously-resolved non-own property that we cached on the
+    // holder. This can happen with indexed properties on NodeLists, for example,
+    // which are |own| value props.
+    //
+    // resolveOwnProperty may or may not cache what it finds on the holder,
+    // depending on how ephemeral it decides the property is. XPCWN |own|
+    // properties generally end up on the holder via NewResolve, whereas
+    // NodeList |own| properties don't get defined on the holder, since they're
+    // supposed to be dynamic. This means that we have to first check the result
+    // of resolveOwnProperty, and _then_, if that comes up blank, check the
+    // holder for any cached native properties.
+    //
+    // Finally, we call resolveNativeProperty, which checks non-own properties,
+    // and unconditionally caches what it finds on the holder.
+
+    // Check resolveOwnProperty.
     if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc, flags))
         return false;
 
+    // Check the holder.
+    if (!desc->obj && !JS_GetPropertyDescriptorById(cx, holder, id, 0, desc))
+        return false;
     if (desc->obj) {
         desc->obj = wrapper;
         return true;
     }
 
+    // Nothing in the cache. Call through, and cache the result.
+    if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc, flags))
+        return false;
+
     // We need to handle named access on the Window somewhere other than
     // Traits::resolveOwnProperty, because per spec it happens on the Global
     // Scope Polluter and thus the resulting properties are non-|own|. However,
-    // we're set up (below) to cache (on the holder) anything that comes out of
+    // we're set up (above) to cache (on the holder) anything that comes out of
     // resolveNativeProperty, which we don't want for something dynamic like
-    // named access. So we just handle it here.
+    // named access. So we just handle it separately here.
     nsGlobalWindow *win;
-    if (Traits::Type == XrayForWrappedNative && JSID_IS_STRING(id) &&
+    if (!desc->obj && Traits::Type == XrayForWrappedNative && JSID_IS_STRING(id) &&
         (win = static_cast<nsGlobalWindow*>(As<nsPIDOMWindow>(wrapper))))
     {
         nsCOMPtr<nsIDOMWindow> childDOMWin = win->GetChildWindow(id);
         if (childDOMWin) {
             nsGlobalWindow *cwin = static_cast<nsGlobalWindow*>(childDOMWin.get());
             JSObject *childObj = cwin->FastGetGlobalJSObject();
             if (MOZ_UNLIKELY(!childObj))
                 return xpc::Throw(cx, NS_ERROR_FAILURE);
             mozilla::dom::FillPropertyDescriptor(desc, wrapper,
                                                  ObjectValue(*childObj),
                                                  /* readOnly = */ true);
             return JS_WrapPropertyDescriptor(cx, desc);
         }
     }
 
-    if (!JS_GetPropertyDescriptorById(cx, holder, id, 0, desc))
-        return false;
-    if (desc->obj) {
-        desc->obj = wrapper;
-        return true;
-    }
-
-    // Nothing in the cache. Call through, and cache the result.
-    if (!Traits::resolveNativeProperty(cx, wrapper, holder, id, desc, flags))
-        return false;
-
     if (!desc->obj &&
         id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING))
     {
 
         JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, holder, "toString");
         if (!toString)
             return false;
 
@@ -1590,29 +1665,18 @@ XrayWrapper<Base, Traits>::getOwnPropert
             return false;
         if (desc->obj != waived)
             desc->obj = nullptr;
         return true;
     }
 
     if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc, flags))
         return false;
-
-    if (desc->obj) {
-        desc->obj = wrapper;
-        return true;
-    }
-
-    if (!JS_GetPropertyDescriptorById(cx, holder, id, flags, desc))
-        return false;
-
-    // Pretend we found the property on the wrapper, not the holder.
     if (desc->obj)
         desc->obj = wrapper;
-
     return true;
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, HandleObject wrapper,
                                           HandleId id, PropertyDescriptor *desc)
 {
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -36,16 +36,19 @@ bool
 IsTransparent(JSContext *cx, JSHandleObject wrapper, JSHandleId id);
 
 JSObject *
 GetNativePropertiesObject(JSContext *cx, JSObject *wrapper);
 
 bool
 IsXrayResolving(JSContext *cx, JSHandleObject wrapper, JSHandleId id);
 
+bool
+HasNativeProperty(JSContext *cx, JSHandleObject wrapper, JSHandleId id,
+                  bool *hasProp);
 }
 
 class XrayTraits;
 class XPCWrappedNativeXrayTraits;
 class ProxyXrayTraits;
 class DOMXrayTraits;
 
 
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/862947-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+div { display: flex; }
+div:after { display: -moz-deck; content: counter(b); }
+
+</style>
+</head>
+<body>
+
+<div></div>
+
+</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -454,8 +454,9 @@ load 842132-1.html
 test-pref(layout.css.flexbox.enabled,true) load 844529-1.html
 load 847130.xhtml
 load 847208.html
 asserts(4) load 847209.html # bug 847368
 test-pref(layout.css.flexbox.enabled,true) load 847211-1.html
 load 849603.html
 test-pref(layout.css.flexbox.enabled,true) load 851396-1.html
 test-pref(layout.css.flexbox.enabled,true) load 854263-1.html
+test-pref(layout.css.flexbox.enabled,true) load 862947-1.html
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -1788,44 +1788,59 @@ GetFlexContainer(nsIFrame* aFrame)
       parent->GetType() != nsGkAtoms::flexContainerFrame) {
     return nullptr;
   }
 
   return static_cast<nsFlexContainerFrame*>(parent);
 }
 #endif // MOZ_FLEXBOX
 
+// Flex items resolve percentage margin & padding against the flex
+// container's height (which is the containing block height).
+// For everything else: the CSS21 spec requires that margin and padding
+// percentage values are calculated with respect to the *width* of the
+// containing block, even for margin & padding in the vertical axis.
+static nscoord
+VerticalOffsetPercentBasis(const nsIFrame* aFrame,
+                           nscoord aContainingBlockWidth,
+                           nscoord aContainingBlockHeight)
+{
+  if (!aFrame->IsFlexItem()) {
+    return aContainingBlockWidth;
+  }
+
+  if (aContainingBlockHeight == NS_AUTOHEIGHT) {
+    return 0;
+  }
+
+  return aContainingBlockHeight;
+}
+
 // XXX refactor this code to have methods for each set of properties
 // we are computing: width,height,line-height; margin; offsets
 
 void
 nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
                                    nscoord         aContainingBlockWidth,
                                    nscoord         aContainingBlockHeight,
                                    const nsMargin* aBorder,
                                    const nsMargin* aPadding,
                                    nsIAtom* aFrameType)
 {
   DISPLAY_INIT_CONSTRAINTS(frame, this,
                            aContainingBlockWidth, aContainingBlockHeight,
                            aBorder, aPadding);
 
-  // If this is the root frame, then set the computed width and
+  // If this is a reflow root, then set the computed width and
   // height equal to the available space
   if (nullptr == parentReflowState) {
-    MOZ_ASSERT(!frame->IsFlexItem(),
-               "the root frame can't be a flex item, since being a flex item "
-               "requires that you have a parent");
-    // Note that we pass the containing block width as the percent basis for
-    // both horizontal *and* vertical margins & padding, in our InitOffsets
-    // call here. This is correct per CSS 2.1; it'd be incorrect for e.g. flex
-    // items and grid items, but the root frame can't be either of those.
     // XXXldb This doesn't mean what it used to!
     InitOffsets(aContainingBlockWidth,
-                aContainingBlockWidth,
+                VerticalOffsetPercentBasis(frame, aContainingBlockWidth,
+                                           aContainingBlockHeight),
                 aFrameType, aBorder, aPadding);
     // Override mComputedMargin since reflow roots start from the
     // frame's boundary, which is inside the margin.
     mComputedMargin.SizeTo(0, 0, 0, 0);
     mComputedOffsets.SizeTo(0, 0, 0, 0);
 
     mComputedWidth = availableWidth - mComputedBorderPadding.LeftRight();
     if (mComputedWidth < 0)
@@ -1863,29 +1878,21 @@ nsHTMLReflowState::InitConstraints(nsPre
         fType = cbrs->frame->GetType();
         if (IS_TABLE_CELL(fType)) {
           // use the cell's computed height 
           aContainingBlockHeight = cbrs->mComputedHeight;
         }
       }
     }
 
-    // Flex containers resolve percentage margin & padding against the flex
-    // container's height (which is the containing block height).
-    // For everything else: the CSS21 spec requires that margin and padding
-    // percentage values are calculated with respect to the *width* of the
-    // containing block, even for margin & padding in the vertical axis.
     // XXX Might need to also pass the CB height (not width) for page boxes,
     // too, if we implement them.
-    nscoord verticalPercentBasis = aContainingBlockWidth;
-    if (frame->IsFlexItem()) {
-      verticalPercentBasis =
-        aContainingBlockHeight == NS_AUTOHEIGHT ? 0 : aContainingBlockHeight;
-    }
-    InitOffsets(aContainingBlockWidth, verticalPercentBasis,
+    InitOffsets(aContainingBlockWidth,
+                VerticalOffsetPercentBasis(frame, aContainingBlockWidth,
+                                           aContainingBlockHeight),
                 aFrameType, aBorder, aPadding);
 
     const nsStyleCoord &height = mStylePosition->mHeight;
     nsStyleUnit heightUnit = height.GetUnit();
 
     // Check for a percentage based height and a containing block height
     // that depends on the content height
     // XXX twiddling heightUnit doesn't help anymore
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-75pct-common-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <input type=range value=75>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-75pct-unthemed-common-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <input type=range value=75 style="-moz-appearance:none">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-stepDown-unthemed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').stepDown();
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=100 step=25 style='-moz-appearance:none'>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-stepDown.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').stepDown();
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=100 step=25>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-stepUp-unthemed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').stepUp();
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=50 step=25 style='-moz-appearance:none'>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-stepUp.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').stepUp();
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=50 step=25>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-value-prop-unthemed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').value = "75";
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=50 step=25 style='-moz-appearance:none'>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-value-prop.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').value = "75";
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=50 step=25>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-valueAsNumber-prop-unthemed.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').valueAsNumber = 75;
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=50 step=25 style='-moz-appearance:none'>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/range/input-valueAsNumber-prop.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <!-- Test: when changing the 'value' IDL property, the thumb of the range
+             should be moved to the appropriate position -->
+  <script type="text/javascript">
+    function setValue()
+    {
+      document.getElementById('i').valueAsNumber = 75;
+      document.documentElement.className = '';
+    }
+    document.addEventListener("MozReftestInvalidate", setValue);
+    setTimeout(setValue, 2000); // useful when not running under reftest suite
+  </script>
+  <body>
+    <input type=range id='i' value=50 step=25>
+  </body>
+</html>
--- a/layout/reftests/forms/input/range/reftest.list
+++ b/layout/reftests/forms/input/range/reftest.list
@@ -9,16 +9,26 @@ default-preferences pref(dom.experimenta
 # dynamic type changes:
 == input-to-range-from-other-type-unthemed-1.html input-to-range-from-other-type-unthemed-1-ref.html
 == input-from-range-to-other-type-unthemed-1.html input-from-range-to-other-type-unthemed-1-ref.html
 
 # for different values:
 != input-range-different-fraction-of-range-unthemed-1.html input-range-different-fraction-of-range-unthemed-1-notref.html
 == input-range-same-fraction-of-range-unthemed-1.html input-range-same-fraction-of-range-unthemed-1-ref.html
 
+# dynamic value changes:
+== input-value-prop-unthemed.html input-75pct-unthemed-common-ref.html
+== input-value-prop.html input-75pct-common-ref.html
+== input-valueAsNumber-prop-unthemed.html input-75pct-unthemed-common-ref.html
+== input-valueAsNumber-prop.html input-75pct-common-ref.html
+== input-stepDown-unthemed.html input-75pct-unthemed-common-ref.html
+== input-stepDown.html input-75pct-common-ref.html
+== input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html
+== input-stepUp.html input-75pct-common-ref.html
+
 # 'direction' property:
 == input-range-direction-unthemed-1.html input-range-direction-unthemed-1-ref.html
 
 # ::-moz-range-progress pseudo-element:
 == input-range-moz-range-progress-1.html input-range-moz-range-progress-1-ref.html
 == input-range-moz-range-progress-2.html input-range-moz-range-progress-2-ref.html
 == input-range-moz-range-progress-3.html input-range-moz-range-progress-3-ref.html
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GeckoJavaSampler.java
@@ -0,0 +1,184 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import android.util.Log;
+import java.lang.Thread;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class GeckoJavaSampler {
+    private static final String LOGTAG = "JavaSampler";
+    private static Thread sSamplingThread = null;
+    private static SamplingThread sSamplingRunnable = null;
+    private static Thread sMainThread = null;
+
+    // Use the same timer primitive as the profiler
+    // to get a perfect sample syncing.
+    private static native double getProfilerTime();
+
+    private static class Sample {
+        public Frame[] mFrames;
+        public double mTime;
+        public Sample(StackTraceElement[] aStack) {
+            mFrames = new Frame[aStack.length];
+            mTime = getProfilerTime();
+            for (int i = 0; i < aStack.length; i++) {
+                mFrames[aStack.length - 1 - i] = new Frame();
+                mFrames[aStack.length - 1 - i].fileName = aStack[i].getFileName();
+                mFrames[aStack.length - 1 - i].lineNo = aStack[i].getLineNumber();
+                mFrames[aStack.length - 1 - i].methodName = aStack[i].getMethodName();
+                mFrames[aStack.length - 1 - i].className = aStack[i].getClassName();
+            }
+        }
+    }
+    private static class Frame {
+        public String fileName;
+        public int lineNo;
+        public String methodName;
+        public String className;
+    }
+
+    private static class SamplingThread implements Runnable {
+        private final int mInterval;
+        private final int mSampleCount;
+
+        private boolean mPauseSampler = false;
+        private boolean mStopSampler = false;
+
+        private Map<Integer,Sample[]> mSamples = new HashMap<Integer,Sample[]>();
+        private int mSamplePos;
+
+        public SamplingThread(final int aInterval, final int aSampleCount) {
+            // If we sample faster then 10ms we get to many missed samples
+            mInterval = Math.max(10, aInterval);
+            mSampleCount = aSampleCount;
+        }
+
+        public void run() {
+            synchronized (GeckoJavaSampler.class) {
+                mSamples.put(0, new Sample[mSampleCount]);
+                mSamplePos = 0;
+
+                // Find the main thread
+                Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
+                for (Thread t : threadSet) {
+                    if (t.getName().compareToIgnoreCase("main") == 0) {
+                        sMainThread = t;
+                        break;
+                    }
+                }
+
+                if (sMainThread == null) {
+                    Log.e(LOGTAG, "Main thread not found");
+                    return;
+                }
+            }
+
+            while (true) {
+                try {
+                    Thread.sleep(mInterval);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+                synchronized (GeckoJavaSampler.class) {
+                    if (!mPauseSampler) {
+                        StackTraceElement[] bt = sMainThread.getStackTrace();
+                        mSamples.get(0)[mSamplePos] = new Sample(bt);
+                        mSamplePos = (mSamplePos+1) % mSamples.get(0).length;
+                    }
+                    if (mStopSampler) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        private Sample getSample(int aThreadId, int aSampleId) {
+            if (aThreadId < mSamples.size() && aSampleId < mSamples.get(aThreadId).length &&
+                mSamples.get(aThreadId)[aSampleId] != null) {
+                int startPos = 0;
+                if (mSamples.get(aThreadId)[mSamplePos] != null) {
+                    startPos = mSamplePos;
+                }
+                int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length;
+                return mSamples.get(aThreadId)[readPos];
+            }
+            return null;
+        }
+    }
+
+    public synchronized static String getThreadName(int aThreadId) {
+        if (aThreadId == 0 && sMainThread != null) {
+            return sMainThread.getName();
+        }
+        return null;
+    }
+
+    private synchronized static Sample getSample(int aThreadId, int aSampleId) {
+        return sSamplingRunnable.getSample(aThreadId, aSampleId);
+    }
+    public synchronized static double getSampleTime(int aThreadId, int aSampleId) {
+        Sample sample = getSample(aThreadId, aSampleId);
+        if (sample != null) {
+            System.out.println("Sample: " + sample.mTime);
+            return sample.mTime;
+        }
+        return 0;
+    }
+    public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) {
+        Sample sample = getSample(aThreadId, aSampleId);
+        if (sample != null && aFrameId < sample.mFrames.length) {
+            Frame frame = sample.mFrames[aFrameId];
+            if (frame == null) {
+                return null;
+            }
+            return frame.className + "." + frame.methodName + "()";
+        }
+        return null;
+    }
+
+    public static void start(int aInterval, int aSamples) {
+        synchronized (GeckoJavaSampler.class) {
+            sSamplingRunnable = new SamplingThread(aInterval, aSamples);
+            sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler");
+            sSamplingThread.start();
+        }
+    }
+
+    public static void pause() {
+        synchronized (GeckoJavaSampler.class) {
+            sSamplingRunnable.mPauseSampler = true;
+        }
+    }
+
+    public static void unpause() {
+        synchronized (GeckoJavaSampler.class) {
+            sSamplingRunnable.mPauseSampler = false;
+        }
+    }
+
+    public static void stop() {
+        synchronized (GeckoJavaSampler.class) {
+            if (sSamplingThread == null) {
+                return;
+            }
+
+            sSamplingRunnable.mStopSampler = true;
+            try {
+                sSamplingThread.join();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            sSamplingThread = null;
+            sSamplingRunnable = null;
+        }
+    }
+}
+
+
+
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -102,16 +102,17 @@ FENNEC_JAVA_FILES = \
   GeckoMessageReceiver.java \
   GeckoSubMenu.java \
   GeckoPreferences.java \
   GeckoPreferenceFragment.java \
   GeckoProfile.java \
   GeckoPopupMenu.java \
   GeckoSmsManager.java \
   GeckoThread.java \
+  GeckoJavaSampler.java \
   GlobalHistory.java \
   GeckoViewsFactory.java \
   HeightChangeAnimation.java \
   InputMethods.java \
   JavaAddonManager.java \
   LightweightTheme.java \
   LightweightThemeDrawable.java \
   LinkPreference.java \
@@ -1147,16 +1148,17 @@ jars/webrtc.jar: $(addprefix $(srcdir)/,
 endif
 
 jars:
 	@echo "MKDIR jars"
 	$(NSINSTALL) -D jars
 
 CLASSES_WITH_JNI= \
     org.mozilla.gecko.GeckoAppShell \
+		org.mozilla.gecko.GeckoJavaSampler \
     $(NULL)
 
 ifdef MOZ_WEBSMS_BACKEND
 # Note: if you are building with MOZ_WEBSMS_BACKEND turned on, then
 # you will get a build error because the generated jni-stubs.inc will
 # be different than the one checked in (i.e. it will have the sms-related
 # JNI stubs as well). Just copy the generated file to mozglue/android/
 # like the error message says and rebuild. All should be well after that.
--- a/mobile/android/base/background/BackgroundService.java
+++ b/mobile/android/base/background/BackgroundService.java
@@ -22,16 +22,23 @@ public abstract class BackgroundService 
   protected BackgroundService() {
     super(LOG_TAG);
   }
 
   protected BackgroundService(String threadName) {
     super(threadName);
   }
 
+  public static void runIntentInService(Context context, Intent intent, Class<? extends BackgroundService> serviceClass) {
+    Intent service = new Intent(context, serviceClass);
+    service.setAction(intent.getAction());
+    service.putExtras(intent);
+    context.startService(service);
+  }
+
   /**
    * Returns true if the OS will allow us to perform background
    * data operations. This logic varies by OS version.
    */
   protected boolean backgroundDataIsEnabled() {
     ConnectivityManager connectivity = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
       return connectivity.getBackgroundDataSetting();
--- a/mobile/android/base/background/announcements/AnnouncementsBroadcastReceiver.java
+++ b/mobile/android/base/background/announcements/AnnouncementsBroadcastReceiver.java
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.announcements;
 
+import org.mozilla.gecko.background.BackgroundService;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
 /**
  * Watch for notifications to start the announcements service.
  *
  * Some observations:
@@ -23,14 +25,11 @@ public class AnnouncementsBroadcastRecei
    * Forward the intent to an IntentService to do background processing.
    */
   @Override
   public void onReceive(Context context, Intent intent) {
     if (AnnouncementsConstants.DISABLED) {
       return;
     }
 
-    Intent service = new Intent(context, AnnouncementsBroadcastService.class);
-    service.putExtras(intent);
-    service.setAction(intent.getAction());
-    context.startService(service);
+    BackgroundService.runIntentInService(context, intent, AnnouncementsBroadcastService.class);
   }
 }
--- a/mobile/android/base/background/announcements/AnnouncementsStartReceiver.java
+++ b/mobile/android/base/background/announcements/AnnouncementsStartReceiver.java
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.background.announcements;
 
+import org.mozilla.gecko.background.BackgroundService;
 import org.mozilla.gecko.background.common.log.Logger;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 
 /**
  * Start the announcements service when instructed by the {@link android.app.AlarmManager}.
@@ -19,12 +20,11 @@ public class AnnouncementsStartReceiver 
 
   @Override
   public void onReceive(Context context, Intent intent) {
     if (AnnouncementsConstants.DISABLED) {
       return;
     }
 
     Logger.debug(LOG_TAG, "AnnouncementsStartReceiver.onReceive().");
-    Intent service = new Intent(context, AnnouncementsService.class);
-    context.startService(service);
+    BackgroundService.runIntentInService(context, intent, AnnouncementsService.class);
   }
 }
--- a/mobile/android/base/jni-generator.py
+++ b/mobile/android/base/jni-generator.py
@@ -64,17 +64,17 @@ class Generator:
             match = re.match(paramsRegex, line)
             if match:
                 paramTypes = re.split('\s*,\s*', match.group(1))
                 paramNames = ['arg%d' % i for i in range(0, len(paramTypes))]
                 if returnType == 'void':
                     returnValue = ''
                 elif returnType == 'jobject':
                     returnValue = 'NULL'
-                elif returnType in ('jint', 'jfloat'):
+                elif returnType in ('jint', 'jfloat', 'jdouble'):
                     returnValue = '0'
                 else:
                     raise Exception(('Unsupported JNI return type %s found; '
                                      + 'please update mobile/android/base/'
                                      + 'jni-generator.py to handle this case!')
                                     % returnType)
 
                 self.write('JNI_STUBS', STUB_TEMPLATE % {
--- a/mobile/android/chrome/content/WebAppRT.js
+++ b/mobile/android/chrome/content/WebAppRT.js
@@ -128,24 +128,26 @@ let WebAppRT = {
         Services.prefs.setIntPref(aPref.name, aPref.value);
         break;
     }
   },
 
   handleEvent: function(event) {
     let target = event.target;
   
-    if (!(target instanceof HTMLAnchorElement) ||
-        target.getAttribute("target") != "_blank") {
+    // walk up the tree to find the nearest link tag
+    while (target && !(target instanceof HTMLAnchorElement)) {
+      target = target.parentNode;
+    }
+
+    if (!target || target.getAttribute("target") != "_blank") {
       return;
     }
   
-    let uri = Services.io.newURI(target.href,
-                                 target.ownerDocument.characterSet,
-                                 null);
+    let uri = Services.io.newURI(target.href, target.ownerDocument.characterSet, null);
   
     // Direct the URL to the browser.
     Cc["@mozilla.org/uriloader/external-protocol-service;1"].
       getService(Ci.nsIExternalProtocolService).
       getProtocolHandlerInfo(uri.scheme).
       launchWithURI(uri);
   
     // Prevent the runtime from loading the URL.  We do this after directing it
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -145,16 +145,34 @@ function dump(a) {
 function getBridge() {
   return Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
 }
 
 function sendMessageToJava(aMessage) {
   return getBridge().handleGeckoMessage(JSON.stringify(aMessage));
 }
 
+function doChangeMaxLineBoxWidth(aWidth) {
+  gReflowPending = null;
+  let webNav = BrowserApp.selectedTab.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
+  let docShell = webNav.QueryInterface(Ci.nsIDocShell);
+  let docViewer = docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
+
+  let range = null;
+  if (BrowserApp.selectedTab._mReflozPoint) {
+    range = BrowserApp.selectedTab._mReflozPoint.range;
+  }
+
+  docViewer.changeMaxLineBoxWidth(aWidth);
+
+  if (range) {
+    BrowserEventHandler._zoomInAndSnapToRange(range);
+  }
+}
+
 function fuzzyEquals(a, b) {
   return (Math.abs(a - b) < 1e-6);
 }
 
 #ifdef MOZ_CRASHREPORTER
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
   "@mozilla.org/xre/app-info;1", "nsICrashReporter");
@@ -2296,16 +2314,17 @@ nsBrowserAccess.prototype = {
   }
 };
 
 
 // track the last known screen size so that new tabs
 // get created with the right size rather than being 1x1
 let gScreenWidth = 1;
 let gScreenHeight = 1;
+let gReflowPending = null;
 
 // The margins that should be applied to the viewport for fixed position
 // children. This is used to avoid browser chrome permanently obscuring
 // fixed position content, and also to make sure window-sized pages take
 // into account said browser chrome.
 let gViewportMargins = { top: 0, right: 0, bottom: 0, left: 0};
 
 function Tab(aURL, aParams) {
@@ -2475,24 +2494,28 @@ Tab.prototype = {
         };
         sendMessageToJava(message);
         dump("Handled load error: " + e);
       }
     }
   },
 
   performReflowOnZoom: function(aViewport) {
-      let webNav = this.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
-      let docShell = webNav.QueryInterface(Ci.nsIDocShell);
-      let docViewer = docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
       let viewportWidth = gScreenWidth / aViewport.zoom;
+      let reflozTimeout = Services.prefs.getIntPref("browser.zoom.reflowZoom.reflowTimeout");
+
+      if (gReflowPending) {
+        clearTimeout(gReflowPending);
+      }
 
       // We add in a bit of fudge just so that the end characters don't accidentally
       // get clipped. 15px is an arbitrary choice.
-      docViewer.changeMaxLineBoxWidth(viewportWidth - 15);
+      gReflowPending = setTimeout(doChangeMaxLineBoxWidth,
+                                  reflozTimeout,
+                                  viewportWidth - 15);
   },
 
   /** 
    * Reloads the tab with the desktop mode setting.
    */
   reloadWithMode: function (aDesktopMode) {
     // Set desktop mode for tab and send change to Java
     if (this.desktopMode != aDesktopMode) {
@@ -3885,22 +3908,16 @@ var BrowserEventHandler = {
         this.onDoubleTap(aData);
         break;
 
       case "MozMagnifyGestureStart":
       case "MozMagnifyGestureUpdate":
         this.onPinch(aData);
         break;
 
-      case "MozMagnifyGesture":
-        if (BrowserEventHandler.mReflozPref) {
-          this.onPinchFinish(aData, BrowserApp.selectedTab._mReflozPoint.x, BrowserApp.selectedTab._mReflozPoint.y);
-        }
-        break;
-
       default:
         dump('BrowserEventHandler.handleUserEvent: unexpected topic "' + aTopic + '"');
         break;
     }
   },
 
   _zoomOut: function() {
     BrowserEventHandler.resetMaxLineBoxWidth();
@@ -4034,41 +4051,33 @@ var BrowserEventHandler = {
 
     rect.type = "Browser:ZoomToRect";
     rect.x = Math.max(viewport.cssPageLeft, rect.x  - fudge + leftAdjustment);
     rect.y = Math.max(topPos, viewport.cssPageTop);
     rect.w = viewport.cssWidth;
     rect.h = viewport.cssHeight;
 
     sendMessageToJava(rect);
+    BrowserApp.selectedTab._mReflozPoint = null;
    },
 
    onPinch: function(aData) {
      // We only want to do this if reflow-on-zoom is enabled.
      if (BrowserEventHandler.mReflozPref &&
          !BrowserApp.selectedTab._mReflozPoint) {
        let data = JSON.parse(aData);
        let zoomPointX = data.x;
        let zoomPointY = data.y;
 
        BrowserApp.selectedTab._mReflozPoint = { x: zoomPointX, y: zoomPointY,
          range: BrowserApp.selectedBrowser.contentDocument.caretPositionFromPoint(zoomPointX, zoomPointY) };
          BrowserApp.selectedTab.probablyNeedRefloz = true;
      }
    },
 
-   onPinchFinish: function(aData, aX, aY) {
-     // We only want to do this if reflow-on-zoom is enabled.
-     if (BrowserEventHandler.mReflozPref) {
-       let range = BrowserApp.selectedTab._mReflozPoint.range;
-       this._zoomInAndSnapToRange(range);
-       BrowserApp.selectedTab._mReflozPoint = null;
-     }
-   },
-
   _shouldZoomToElement: function(aElement) {
     let win = aElement.ownerDocument.defaultView;
     if (win.getComputedStyle(aElement, null).display == "inline")
       return false;
     if (aElement instanceof Ci.nsIDOMHTMLLIElement)
       return false;
     if (aElement instanceof Ci.nsIDOMHTMLQuoteElement)
       return false;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3832,16 +3832,23 @@ pref("toolkit.zoomManager.zoomValues", "
  * If enabled, this will limit the max line box width of all text on a page to
  * the viewport width (also generating a reflow), after a zoom event occurs.
  *
  * By default, this is not enabled.
  */
 pref("browser.zoom.reflowOnZoom", false);
 
 /**
+ * Specifies the number of milliseconds to wait after a given reflow-on-zoom
+ * operation has completed before allowing another one to be triggered. This
+ * is to prevent a buildup of reflow-zoom events.
+ */
+pref("browser.zoom.reflowZoom.reflowTimeout", 500);
+
+/**
  * Controls whether or not the reflow-on-zoom behavior happens on page load.
  * This can be enabled in conjunction with the above preference (reflowOnZoom),
  * but has no effect if browser.zoom.reflowOnZoom is disabled.
  *
  * Note that this should be turned off only in cases where debugging of the
  * reflow-on-zoom feature is necessary, and enabling the feature during
  * a page load inhbits this debugging.
  */
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -374,8 +374,27 @@ Java_org_mozilla_gecko_GeckoAppShell_not
      f_Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult(arg0, arg1, arg2, arg3);
 }
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult", &f_Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult);
 #endif
 
+#ifdef JNI_STUBS
+
+typedef jdouble (*Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime_t)(JNIEnv *, jclass);
+static Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime_t f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime;
+extern "C" NS_EXPORT jdouble JNICALL
+Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv * arg0, jclass arg1) {
+    if (!f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime) {
+        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
+                       "JNI Function called before it was loaded");
+        return 0;
+    }
+    return f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(arg0, arg1);
+}
+#endif
+
+#ifdef JNI_BINDINGS
+  xul_dlsym("Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime", &f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime);
+#endif
+
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -66,17 +66,17 @@ class XPCShellRemote(xpcshell.XPCShellTe
         self.appRoot = None
         packageName = None
         if self.options.localAPK:
             try:
                 packageName = subprocess.check_output(["unzip", "-p", self.options.localAPK, "package-name.txt"])
                 if packageName:
                     self.appRoot = self.device.getAppRoot(packageName.strip())
             except Exception as detail:
-                print "unable to determine app root: " + detail
+                print "unable to determine app root: " + str(detail)
                 pass
         return None
 
     def remoteJoin(self, path1, path2):
         joined = os.path.join(path1, path2)
         joined = joined.replace('\\', '/')
         return joined
 
--- a/toolkit/components/ctypes/ctypes.cpp
+++ b/toolkit/components/ctypes/ctypes.cpp
@@ -115,19 +115,18 @@ NS_IMETHODIMP
 Module::Call(nsIXPConnectWrappedNative* wrapper,
              JSContext* cx,
              JSObject* obj,
              uint32_t argc,
              JS::Value* argv,
              JS::Value* vp,
              bool* _retval)
 {
-  JSObject* targetObj = nullptr;
-
   mozJSComponentLoader* loader = mozJSComponentLoader::Get();
+  JS::Rooted<JSObject*> targetObj(cx);
   nsresult rv = loader->FindTargetObject(cx, &targetObj);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *_retval = InitAndSealCTypesClass(cx, targetObj);
   return NS_OK;
 }
 
 }
--- a/toolkit/components/perf/PerfMeasurement.cpp
+++ b/toolkit/components/perf/PerfMeasurement.cpp
@@ -81,19 +81,19 @@ NS_IMETHODIMP
 Module::Call(nsIXPConnectWrappedNative* wrapper,
              JSContext* cx,
              JSObject* obj,
              uint32_t argc,
              JS::Value* argv,
              JS::Value* vp,
              bool* _retval)
 {
-  JSObject* targetObj = nullptr;
 
   mozJSComponentLoader* loader = mozJSComponentLoader::Get();
+  JS::Rooted<JSObject*> targetObj(cx);
   nsresult rv = loader->FindTargetObject(cx, &targetObj);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *_retval = InitAndSealPerfMeasurementClass(cx, targetObj);
   return NS_OK;
 }
 
 }
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -292,17 +292,17 @@ endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
 COMPONENT_LIBS += widget_gonk
 endif
 
 STATIC_LIBS += thebes gl ycbcr
 
 ifdef MOZ_ENABLE_GTEST
-COMPONENT_LIBS += gtest
+COMPONENT_LIBS += gtest xpcom_glue_gtest
 endif
 
 ifdef MOZ_ENABLE_PROFILER_SPS
 COMPONENT_LIBS += profiler
 endif
 
 ifeq (windows,$(MOZ_WIDGET_TOOLKIT))
 COMPONENT_LIBS += widget_windows
--- a/toolkit/themes/linux/global/toolbarbutton.css
+++ b/toolkit/themes/linux/global/toolbarbutton.css
@@ -103,13 +103,13 @@ toolbarbutton[type="menu-button"][disabl
   -moz-box-align: center;
   -moz-box-pack: center;
   -moz-box-orient: vertical;
 }
 
 /* .......... dropmarker .......... */
 
 .toolbarbutton-menubutton-dropmarker {
-  padding: 3px;
+  margin: 0 3px;
   -moz-appearance: toolbarbutton-dropdown !important;
   list-style-image: none;
   -moz-image-region: auto;
 }
--- a/toolkit/themes/osx/global/toolbarbutton.css
+++ b/toolkit/themes/osx/global/toolbarbutton.css
@@ -89,17 +89,17 @@ toolbarbutton[type="menu-button"][disabl
 /* .......... dropmarker .......... */
 
 .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none;
   border: none;
   background-color: transparent !important;
   list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
   width: auto;
-  padding: 0;
+  padding: 0 5px;
 }
 
 .toolbarbutton-menubutton-dropmarker[disabled="true"] {
   list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.png") !important;
   padding: 0 !important;
 }
 
 toolbarbutton.tabbable {
--- a/toolkit/themes/windows/global/toolbarbutton.css
+++ b/toolkit/themes/windows/global/toolbarbutton.css
@@ -136,13 +136,13 @@ toolbarbutton[type="menu-button"][disabl
   -moz-box-pack: center;
   -moz-box-orient: vertical;
 }
 
 /* .......... dropmarker .......... */
 
 .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none;
-  padding: 3px;
+  padding: 3px 7px;
   border: none;
   background-color: transparent;
   width: auto;
 }
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -125,16 +125,17 @@ if not CONFIG['MOZ_NATIVE_PNG']:
 
 add_tier_dir('platform', 'media/kiss_fft')
 
 if CONFIG['ENABLE_TESTS']:
     add_tier_dir('platform', 'testing/specialpowers')
 
 if CONFIG['MOZ_ENABLE_GTEST']:
     add_tier_dir('platform', 'testing/gtest')
+    add_tier_dir('platform', 'xpcom/glue/tests/gtest')
 
 add_tier_dir('platform', [
     'uriloader',
     'caps',
     'parser',
     'gfx',
     'image',
     'dom',
--- a/tools/profiler/BreakpadSampler.cpp
+++ b/tools/profiler/BreakpadSampler.cpp
@@ -240,17 +240,17 @@ void TableTicker::UnwinderTick(TickSampl
 
   // Add any extras
   if (!sLastTracerEvent.IsNull() && sample) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
     utb__addEntry( utb, ProfileEntry('r', delta.ToMilliseconds()) );
   }
 
   if (sample) {
-    TimeDuration delta = sample->timestamp - mStartTime;
+    TimeDuration delta = sample->timestamp - sStartTime;
     utb__addEntry( utb, ProfileEntry('t', delta.ToMilliseconds()) );
   }
 
   if (sLastFrameNumber != sFrameNumber) {
     utb__addEntry( utb, ProfileEntry('f', sFrameNumber) );
     sLastFrameNumber = sFrameNumber;
   }
 
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -137,16 +137,18 @@ static inline void profiler_unlock() {}
 
 static inline void profiler_register_thread(const char* name) {}
 static inline void profiler_unregister_thread() {}
 
 // Call by the JSRuntime's operation callback. This is used to enable
 // profiling on auxilerary threads.
 static inline void profiler_js_operation_callback() {}
 
+static inline double profiler_time() { return 0; }
+
 #else
 
 #include "GeckoProfilerImpl.h"
 
 #endif
 
 class GeckoProfilerInitRAII {
 public:
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -56,13 +56,15 @@ void mozilla_sampler_lock();
 
 // Unlock the profiler, leaving it stopped and fires profiler-unlocked.
 void mozilla_sampler_unlock();
 
 // Register/unregister threads with the profiler
 bool mozilla_sampler_register_thread(const char* name);
 void mozilla_sampler_unregister_thread();
 
+double mozilla_sampler_time();
+
 /* Returns true if env var SPS_NEW is set to anything, else false. */
 extern bool sps_version2();
 
 #endif
 
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -158,16 +158,22 @@ void profiler_js_operation_callback()
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     return;
   }
 
   stack->jsOperationCallback();
 }
 
+static inline
+double profiler_time()
+{
+  return mozilla_sampler_time();
+}
+
 // we want the class and function name but can't easily get that using preprocessor macros
 // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
 
 #define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
 #define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line)
 #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
 
 #define PROFILER_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -29,16 +29,20 @@
 #include "nsIXULRuntime.h"
 #include "nsIXULAppInfo.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "PlatformMacros.h"
 
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+  #include "AndroidBridge.h"
+#endif
+
 // JS
 #include "jsdbgapi.h"
 
 // we eventually want to make this runtime switchable
 #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
  #ifndef ANDROID
   #define USE_BACKTRACE
  #endif
@@ -155,16 +159,64 @@ JSObject* TableTicker::ToJSObject(JSCont
   JSObjectBuilder b(aCx);
   JSCustomObject* profile = b.CreateObject();
   BuildJSObject(b, profile);
   JSObject* jsProfile = b.GetJSObject(profile);
 
   return jsProfile;
 }
 
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+static
+JSCustomObject* BuildJavaThreadJSObject(JSAObjectBuilder& b)
+{
+  JSCustomObject* javaThread = b.CreateObject();
+  b.DefineProperty(javaThread, "name", "Java Main Thread");
+
+  JSCustomArray *samples = b.CreateArray();
+  b.DefineProperty(javaThread, "samples", samples);
+
+  int sampleId = 0;
+  while (true) {
+    int frameId = 0;
+    JSCustomObject *sample = nullptr;
+    JSCustomArray *frames = nullptr;
+    while (true) {
+      nsCString result;
+      bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
+      if (!hasFrame) {
+        if (frames) {
+          b.DefineProperty(sample, "frames", frames);
+        }
+        break;
+      }
+      if (!sample) {
+        sample = b.CreateObject();
+        frames = b.CreateArray();
+        b.DefineProperty(sample, "frames", frames);
+        b.ArrayPush(samples, sample);
+
+        double sampleTime = AndroidBridge::Bridge()->GetSampleTimeJavaProfiling(0, sampleId);
+        b.DefineProperty(sample, "time", sampleTime);
+      }
+      JSCustomObject *frame = b.CreateObject();
+      b.DefineProperty(frame, "location", result.BeginReading());
+      b.ArrayPush(frames, frame);
+      frameId++;
+    }
+    if (frameId == 0) {
+      break;
+    }
+    sampleId++;
+  }
+
+  return javaThread;
+}
+#endif
+
 void TableTicker::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile)
 {
   // Put shared library info
   b.DefineProperty(profile, "libs", GetSharedLibraryInfoString().c_str());
 
   // Put meta data
   JSCustomObject *meta = GetMetaJSCustomObject(b);
   b.DefineProperty(profile, "meta", meta);
@@ -186,18 +238,29 @@ void TableTicker::BuildJSObject(JSAObjec
       MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
 
       JSCustomObject* threadSamples = b.CreateObject();
       sRegisteredThreads->at(i)->Profile()->BuildJSObject(b, threadSamples);
       b.ArrayPush(threads, threadSamples);
     }
   }
 
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+  if (ProfileJava()) {
+    AndroidBridge::Bridge()->PauseJavaProfiling();
+
+    JSCustomObject* javaThread = BuildJavaThreadJSObject(b);
+    b.ArrayPush(threads, javaThread);
+
+    AndroidBridge::Bridge()->UnpauseJavaProfiling();
+  }
+#endif
+
   SetPaused(false);
-} 
+}
 
 // END SaveProfileTask et al
 ////////////////////////////////////////////////////////////////////////
 
 static
 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
 {
   aProfile.addTag(ProfileEntry(aTagName, ""));
@@ -447,17 +510,17 @@ void TableTicker::InplaceTick(TickSample
     currThreadProfile.flush();
 
   if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) {
     TimeDuration delta = sample->timestamp - sLastTracerEvent;
     currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
   }
 
   if (sample) {
-    TimeDuration delta = sample->timestamp - mStartTime;
+    TimeDuration delta = sample->timestamp - sStartTime;
     currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
   }
 
   if (sLastFrameNumber != sFrameNumber) {
     currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
     sLastFrameNumber = sFrameNumber;
   }
 }
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -25,29 +25,31 @@ extern unsigned int sLastSampledEventGen
 class BreakpadSampler;
 
 class TableTicker: public Sampler {
  public:
   TableTicker(int aInterval, int aEntrySize,
               const char** aFeatures, uint32_t aFeatureCount)
     : Sampler(aInterval, true, aEntrySize)
     , mPrimaryThreadProfile(nullptr)
-    , mStartTime(TimeStamp::Now())
     , mSaveRequested(false)
     , mUnwinderThread(false)
   {
     mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
 
     //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
     mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
     mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
-    mProfileThreads = true || hasFeature(aFeatures, aFeatureCount, "threads");
+    mProfileJava = hasFeature(aFeatures, aFeatureCount, "java");
+    mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads");
     mUnwinderThread = hasFeature(aFeatures, aFeatureCount, "unwinder") || sps_version2();
     mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
 
+    sStartTime = TimeStamp::Now();
+
     {
       mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
       // Create ThreadProfile for each registered thread
       for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
         ThreadInfo* info = sRegisteredThreads->at(i);
 
         if (!info->IsMainThread() && !mProfileThreads)
@@ -118,34 +120,35 @@ class TableTicker: public Sampler {
   }
 
   void ToStreamAsJSON(std::ostream& stream);
   virtual JSObject *ToJSObject(JSContext *aCx);
   JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
 
   bool HasUnwinderThread() const { return mUnwinderThread; }
   bool ProfileJS() const { return mProfileJS; }
+  bool ProfileJava() const { return mProfileJava; }
   bool ProfileThreads() const { return mProfileThreads; }
 
 protected:
   // Called within a signal. This function must be reentrant
   virtual void UnwinderTick(TickSample* sample);
 
   // Called within a signal. This function must be reentrant
   virtual void InplaceTick(TickSample* sample);
 
   // Not implemented on platforms which do not support backtracing
   void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
 
   void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
 
   // This represent the application's main thread (SAMPLER_INIT)
   ThreadProfile* mPrimaryThreadProfile;
-  TimeStamp mStartTime;
   bool mSaveRequested;
   bool mAddLeafAddresses;
   bool mUseStackWalk;
   bool mJankOnly;
   bool mProfileJS;
   bool mProfileThreads;
   bool mUnwinderThread;
+  bool mProfileJava;
 };
 
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -15,25 +15,30 @@
 #include "TableTicker.h"
 #include "UnwinderThread2.h"
 #include "nsIObserverService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+  #include "AndroidBridge.h"
+#endif
+
 mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
 mozilla::ThreadLocal<TableTicker *> tlsTicker;
 // We need to track whether we've been initialized otherwise
 // we end up using tlsStack without initializing it.
 // Because tlsStack is totally opaque to us we can't reuse
 // it as the flag itself.
 bool stack_key_initialized;
 
 TimeStamp   sLastTracerEvent; // is raced on
+TimeStamp   sStartTime;
 int         sFrameNumber = 0;
 int         sLastFrameNumber = 0;
 int         sInitCount = 0; // Each init must have a matched shutdown.
 static bool sIsProfiling = false; // is raced on
 
 /* used to keep track of the last event that we sampled during */
 unsigned int sLastSampledEventGeneration = 0;
 
@@ -378,16 +383,17 @@ const char** mozilla_sampler_get_feature
     // Include the C++ leaf node if not stackwalking. DevTools
     // profiler doesn't want the native addresses.
     "leaf",
 #endif
 #if !defined(SPS_OS_windows)
     // Use a seperate thread of walking the stack.
     "unwinder",
 #endif
+    "java",
     // Only record samples during periods of bad responsiveness
     "jank",
     // Tell the JS engine to emmit pseudostack entries in the
     // pro/epilogue.
     "js",
     // Profile the registered secondary threads.
     "threads",
     NULL
@@ -440,16 +446,27 @@ void mozilla_sampler_start(int aProfileE
         ThreadProfile* thread_profile = info->Profile();
         if (!thread_profile) {
           continue;
         }
         thread_profile->GetPseudoStack()->enableJSSampling();
       }
   }
 
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+  if (t->ProfileJava()) {
+    int javaInterval = aInterval;
+    // Java sampling doesn't accuratly keep up with 1ms sampling
+    if (javaInterval < 10) {
+      aInterval = 10;
+    }
+    mozilla::AndroidBridge::Bridge()->StartJavaProfiling(javaInterval, 1000);
+  }
+#endif
+
   sIsProfiling = true;
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os)
     os->NotifyObservers(nullptr, "profiler-started", nullptr);
 }
 
 void mozilla_sampler_stop()
@@ -555,12 +572,18 @@ bool mozilla_sampler_register_thread(con
   return Sampler::RegisterCurrentThread(aName, stack, false);
 }
 
 void mozilla_sampler_unregister_thread()
 {
   Sampler::UnregisterCurrentThread();
 }
 
+double mozilla_sampler_time()
+{
+  TimeDuration delta = TimeStamp::Now() - sStartTime;
+  return delta.ToMilliseconds();
+}
+
 // END externally visible functions
 ////////////////////////////////////////////////////////////////////////
 
 
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -75,16 +75,18 @@
     } while (0)
 
 #endif
 
 #if defined(XP_MACOSX) || defined(XP_WIN)
 #define ENABLE_SPS_LEAF_DATA
 #endif
 
+extern mozilla::TimeStamp sStartTime;
+
 typedef uint8_t* Address;
 
 // ----------------------------------------------------------------------------
 // Mutex
 //
 // Mutexes are used for serializing access to non-reentrant sections of code.
 // The implementations of mutex should allow for nested/recursive locking.
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -171,16 +171,25 @@ AndroidBridge::Init(JNIEnv *jEnv,
     jDisableNetworkNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableNetworkNotifications", "()V");
 
     jGetScreenOrientation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getScreenOrientation", "()S");
     jEnableScreenOrientationNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableScreenOrientationNotifications", "()V");
     jDisableScreenOrientationNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableScreenOrientationNotifications", "()V");
     jLockScreenOrientation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "lockScreenOrientation", "(I)V");
     jUnlockScreenOrientation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "unlockScreenOrientation", "()V");
 
+    jGeckoJavaSamplerClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/GeckoJavaSampler"));
+    jStart = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "start", "(II)V");
+    jStop = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "stop", "()V");
+    jPause = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "pause", "()V");
+    jUnpause = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "unpause", "()V");
+    jGetThreadName = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "getThreadName", "(I)Ljava/lang/String;");
+    jGetFrameName = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "getFrameName", "(III)Ljava/lang/String;");
+    jGetSampleTime = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "getSampleTime", "(II)D");
+
     jThumbnailHelperClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/ThumbnailHelper"));
     jNotifyThumbnail = jEnv->GetStaticMethodID(jThumbnailHelperClass, "notifyThumbnail", "(Ljava/nio/ByteBuffer;IZ)V");
 
     jStringClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("java/lang/String"));
 
     jSurfaceClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("android/view/Surface"));
 
     if (!GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &mAPIVersion, jEnv))
@@ -2408,16 +2417,130 @@ AndroidBridge::RemovePluginView(jobject 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jRemovePluginView, view, isFullScreen);
 }
 
 extern "C"
 __attribute__ ((visibility("default")))
 jobject JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size);
 
+void
+AndroidBridge::StartJavaProfiling(int aInterval, int aSamples)
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                              AndroidBridge::Bridge()->jStart,
+                              aInterval, aSamples);
+}
+
+void
+AndroidBridge::StopJavaProfiling()
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                              AndroidBridge::Bridge()->jStop);
+}
+
+void
+AndroidBridge::PauseJavaProfiling()
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                              AndroidBridge::Bridge()->jPause);
+}
+
+void
+AndroidBridge::UnpauseJavaProfiling()
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                              AndroidBridge::Bridge()->jUnpause);
+}
+
+bool
+AndroidBridge::GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult)
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return false;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    jstring jstrThreadName = static_cast<jstring>(
+        env->CallStaticObjectMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                                    AndroidBridge::Bridge()->jGetThreadName,
+                                    aThreadId));
+
+    if (!jstrThreadName)
+        return false;
+
+    nsJNIString jniStr(jstrThreadName, env);
+    CopyUTF16toUTF8(jniStr.get(), aResult);
+    return true;
+}
+
+bool
+AndroidBridge::GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId,
+                                          uint32_t aFrameId, nsCString & aResult)
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return false;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    jstring jstrSampleName = static_cast<jstring>(
+        env->CallStaticObjectMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                                    AndroidBridge::Bridge()->jGetFrameName,
+                                    aThreadId, aSampleId, aFrameId));
+
+    if (!jstrSampleName)
+        return false;
+
+    nsJNIString jniStr(jstrSampleName, env);
+    CopyUTF16toUTF8(jniStr.get(), aResult);
+    return true;
+}
+
+double
+AndroidBridge::GetSampleTimeJavaProfiling(uint32_t aThreadId, uint32_t aSampleId)
+{
+    JNIEnv* env = GetJNIForThread();
+    if (!env)
+        return 0;
+
+    AutoLocalJNIFrame jniFrame(env);
+
+    jdouble jSampleTime =
+        env->CallStaticDoubleMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass,
+                                    AndroidBridge::Bridge()->jGetSampleTime,
+                                    aThreadId, aSampleId);
+
+    return jSampleTime;
+}
 
 void
 AndroidBridge::SendThumbnail(jobject buffer, int32_t tabId, bool success) {
     // Regardless of whether we successfully captured a thumbnail, we need to
     // send a response to process the remaining entries in the queue. If we
     // don't get an env here, we'll stall the thumbnail loop, but there isn't
     // much we can do about it.
     JNIEnv* env = GetJNIEnv();
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -147,16 +147,24 @@ public:
     /* These are all implemented in Java */
     static void NotifyIME(int aType);
 
     static void NotifyIMEContext(int aState, const nsAString& aTypeHint,
                                  const nsAString& aModeHint, const nsAString& aActionHint);
 
     static void NotifyIMEChange(const PRUnichar *aText, uint32_t aTextLen, int aStart, int aEnd, int aNewEnd);
 
+    void StartJavaProfiling(int aInterval, int aSamples);
+    void StopJavaProfiling();
+    void PauseJavaProfiling();
+    void UnpauseJavaProfiling();
+    bool GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult);
+    bool GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, uint32_t aFrameId, nsCString & aResult);
+    double GetSampleTimeJavaProfiling(uint32_t aThreadId, uint32_t aSampleId);
+
     nsresult CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, jobject buffer);
     void SendThumbnail(jobject buffer, int32_t tabId, bool success);
     nsresult GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort);
 
     bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const gfx::Rect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, gfx::Rect& aViewport, float& aScaleX, float& aScaleY);
 
     void AcknowledgeEvent();
 
@@ -355,16 +363,25 @@ public:
 
     int GetAPIVersion() { return mAPIVersion; }
     bool IsHoneycomb() { return mAPIVersion >= 11 && mAPIVersion <= 13; }
 
     void ScheduleComposite();
     void RegisterSurfaceTextureFrameListener(jobject surfaceTexture, int id);
     void UnregisterSurfaceTextureFrameListener(jobject surfaceTexture);
 
+    jclass jGeckoJavaSamplerClass;
+    jmethodID jStart;
+    jmethodID jStop;
+    jmethodID jPause;
+    jmethodID jUnpause;
+    jmethodID jGetThreadName;
+    jmethodID jGetFrameName;
+    jmethodID jGetSampleTime;
+
     void GetGfxInfoData(nsACString& aRet);
     nsresult GetProxyForURI(const nsACString & aSpec,
                             const nsACString & aScheme,
                             const nsACString & aHost,
                             const int32_t      aPort,
                             nsACString & aResult);
 protected:
     static AndroidBridge *sBridge;
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -33,16 +33,17 @@
 #include "mozilla/dom/SmsMessage.h"
 #include "mozilla/dom/mobilemessage/Constants.h"
 #include "mozilla/dom/mobilemessage/Types.h"
 #include "mozilla/dom/mobilemessage/PSms.h"
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "nsIMobileMessageDatabaseService.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsSurfaceTexture.h"
+#include "GeckoProfiler.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobilemessage;
 
 /* Forward declare all the JNI methods as extern "C" */
 
 extern "C" {
@@ -871,9 +872,15 @@ Java_org_mozilla_gecko_GeckoAppShell_onS
   if (!st) {
     __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "Failed to find nsSurfaceTexture with id %d", id);
     return;
   }
 
   st->NotifyFrameAvailable();
 }
 
+NS_EXPORT jdouble JNICALL
+Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc)
+{
+  return profiler_time();
 }
+
+}
--- a/xpcom/glue/FileUtils.cpp
+++ b/xpcom/glue/FileUtils.cpp
@@ -112,17 +112,17 @@ mozilla::fallocate(PRFileDesc *aFD, int6
   }
 
   PR_Seek64(aFD, oldpos, PR_SEEK_SET);
   return nWrite == 1;
 #endif
   return false;
 }
 
-#ifdef MOZ_WIDGET_GONK
+#ifdef ReadSysFile_PRESENT
 
 #undef TEMP_FAILURE_RETRY
 #define TEMP_FAILURE_RETRY(exp) (__extension__({ \
   typeof (exp) _rc; \
   do { \
     _rc = (exp); \
   } while (_rc == -1 && errno == EINTR); \
   _rc; \
@@ -183,17 +183,17 @@ mozilla::ReadSysFile(
   int v;
   if (!ReadSysFile(aFilename, &v)) {
     return false;
   }
   *aVal = (v != 0);
   return true;
 }
 
-#endif /* MOZ_WIDGET_GONK */
+#endif /* ReadSysFile_PRESENT */
 
 void
 mozilla::ReadAheadLib(nsIFile* aFile)
 {
 #if defined(XP_WIN)
   nsAutoString path;
   if (!aFile || NS_FAILED(aFile->GetPath(path))) {
     return;
--- a/xpcom/glue/FileUtils.h
+++ b/xpcom/glue/FileUtils.h
@@ -140,17 +140,24 @@ NS_COM_GLUE void ReadAheadFile(pathstr_t
  * (on Windows, file must be opened with FILE_FLAG_SEQUENTIAL_SCAN)
  * @param aOffset Offset into the file to begin preloading
  * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
  */
 NS_COM_GLUE void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
                            const size_t aCount = SIZE_MAX);
 
 
-#ifdef MOZ_WIDGET_GONK
+/* Define ReadSysFile() only on GONK to avoid unnecessary lubxul bloat.
+Also define it in debug builds, so that unit tests for it can be written
+and run in non-GONK builds. */
+#if (defined(MOZ_WIDGET_GONK) || defined(DEBUG)) && defined(XP_UNIX)
+
+#ifndef ReadSysFile_PRESENT
+#define ReadSysFile_PRESENT
+#endif /* ReadSysFile_PRESENT */
 
 /**
  * Read the contents of a file.
  * This function is intended for reading a single-lined text files from
  * /sys/. If the file ends with a newline ('\n') then it will be discarded.
  * The output buffer will always be '\0'-terminated on successful completion.
  * If aBufSize == 0, then this function will return true if the file exists
  * and is readable (it will not attempt to read anything from it).
@@ -179,12 +186,12 @@ ReadSysFile(
  * (either 0 or 1).
  * @return true on success
  */
 bool
 ReadSysFile(
   const char* aFilename,
   bool* aVal);
 
-#endif /* MOZ_WIDGET_GONK */
+#endif /* (MOZ_WIDGET_GONK || DEBUG) && XP_UNIX */
 
 } // namespace mozilla
 #endif
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/Makefile.in
@@ -0,0 +1,27 @@
+# vim:set ts=8 sw=8 sts=8 noet:
+# 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/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE_NAME = xpcom_glue_gtest
+LIBRARY_NAME = xpcom_glue_gtest
+EXPORT_LIBRARY = 1
+LIBXUL_LIBRARY = 1
+IS_COMPONENT = 1
+
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../.. \
+  $(NULL)
+
+CPPSRCS = \
+  TestFileUtils.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/TestFileUtils.cpp
@@ -0,0 +1,279 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* Test ReadSysFile() */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "FileUtils.h"
+
+#include "gtest/gtest.h"
+
+namespace mozilla {
+
+#ifdef ReadSysFile_PRESENT
+
+/**
+ * Create a file with the specified contents.
+ */
+static bool
+WriteFile(
+  const char* aFilename,
+  const void* aContents,
+  size_t aContentsLen)
+{
+  int fd;
+  ssize_t ret;
+  size_t offt;
+
+  fd = TEMP_FAILURE_RETRY(
+    open(aFilename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
+  if (fd == -1) {
+    fprintf(stderr, "open(): %s: %s\n", aFilename, strerror(errno));
+    return false;
+  }
+
+  offt = 0;
+  do {
+    ret = TEMP_FAILURE_RETRY(write(fd, (char*)aContents + offt, aContentsLen - offt));
+    if (ret == -1) {
+      fprintf(stderr, "write(): %s: %s\n", aFilename, strerror(errno));
+      close(fd);
+      return false;
+    }
+    offt += ret;
+  } while (offt < aContentsLen);
+
+  ret = TEMP_FAILURE_RETRY(close(fd));
+  if (ret == -1) {
+    fprintf(stderr, "close(): %s: %s\n", aFilename, strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+TEST(ReadSysFile, Nonexistent) {
+  bool ret;
+  int errno_saved;
+
+  ret = ReadSysFile("/nonexistent", NULL, 0);
+  errno_saved = errno;
+
+  ASSERT_FALSE(ret);
+  ASSERT_EQ(errno_saved, ENOENT);
+}
+
+TEST(ReadSysFile, Main) {
+  /* Use a different file name for each test since different tests could be
+  executed concurrently. */
+  static const char* fn = "TestReadSysFileMain";
+  /* If we have a file which contains "abcd" and we read it with ReadSysFile(),
+  providing a buffer of size 10 bytes, we would expect 5 bytes to be written
+  to that buffer: "abcd\0". */
+  struct {
+    /* input (file contents), e.g. "abcd" */
+    const char* input;
+    /* pretended output buffer size, e.g. 10; the actual buffer is larger
+    and we check if anything was written past the end of the allowed length */
+    size_t output_size;
+    /* expected number of bytes written to the output buffer, including the
+    terminating '\0', e.g. 5 */
+    size_t output_len;
+    /* expected output buffer contents, e.g. "abcd\0", the first output_len
+    bytes of the output buffer should match the first 'output_len' bytes from
+    'output', the rest of the output buffer should be untouched. */
+    const char* output;
+  } tests[] = {
+    /* No new lines */
+    {"", 0, 0, ""},
+    {"", 1, 1, "\0"}, /* \0 is redundant, but we write it for clarity */
+    {"", 9, 1, "\0"},
+
+    {"a", 0, 0, ""},
+    {"a", 1, 1, "\0"},
+    {"a", 2, 2, "a\0"},
+    {"a", 9, 2, "a\0"},
+
+    {"abcd", 0, 0, ""},
+    {"abcd", 1, 1, "\0"},
+    {"abcd", 2, 2, "a\0"},
+    {"abcd", 3, 3, "ab\0"},
+    {"abcd", 4, 4, "abc\0"},
+    {"abcd", 5, 5, "abcd\0"},
+    {"abcd", 9, 5, "abcd\0"},
+
+    /* A single trailing new line */
+    {"\n", 0, 0, ""},
+    {"\n", 1, 1, "\0"},
+    {"\n", 2, 1, "\0"},
+    {"\n", 9, 1, "\0"},
+
+    {"a\n", 0, 0, ""},
+    {"a\n", 1, 1, "\0"},
+    {"a\n", 2, 2, "a\0"},
+    {"a\n", 3, 2, "a\0"},
+    {"a\n", 9, 2, "a\0"},
+
+    {"abcd\n", 0, 0, ""},
+    {"abcd\n", 1, 1, "\0"},
+    {"abcd\n", 2, 2, "a\0"},
+    {"abcd\n", 3, 3, "ab\0"},
+    {"abcd\n", 4, 4, "abc\0"},
+    {"abcd\n", 5, 5, "abcd\0"},
+    {"abcd\n", 6, 5, "abcd\0"},
+    {"abcd\n", 9, 5, "abcd\0"},
+
+    /* Multiple trailing new lines */
+    {"\n\n", 0, 0, ""},
+    {"\n\n", 1, 1, "\0"},
+    {"\n\n", 2, 2, "\n\0"},
+    {"\n\n", 3, 2, "\n\0"},
+    {"\n\n", 9, 2, "\n\0"},
+
+    {"a\n\n", 0, 0, ""},
+    {"a\n\n", 1, 1, "\0"},
+    {"a\n\n", 2, 2, "a\0"},
+    {"a\n\n", 3, 3, "a\n\0"},
+    {"a\n\n", 4, 3, "a\n\0"},
+    {"a\n\n", 9, 3, "a\n\0"},
+
+    {"abcd\n\n", 0, 0, ""},
+    {"abcd\n\n", 1, 1, "\0"},
+    {"abcd\n\n", 2, 2, "a\0"},
+    {"abcd\n\n", 3, 3, "ab\0"},
+    {"abcd\n\n", 4, 4, "abc\0"},
+    {"abcd\n\n", 5, 5, "abcd\0"},
+    {"abcd\n\n", 6, 6, "abcd\n\0"},
+    {"abcd\n\n", 7, 6, "abcd\n\0"},
+    {"abcd\n\n", 9, 6, "abcd\n\0"},
+
+    /* New line in the middle */
+    {"ab\ncd", 9, 6, "ab\ncd\0"},
+  };
+
+  for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+    ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+    /* Leave the file to exist if some of the assertions fail. */
+
+    char buf[128];
+    static const char unmodified = 'X';
+
+    memset(buf, unmodified, sizeof(buf));
+
+    ASSERT_TRUE(ReadSysFile(fn, buf, tests[i].output_size));
+
+    if (tests[i].output_size == 0) {
+      /* The buffer must be unmodified. We check only the first byte. */
+      ASSERT_EQ(unmodified, buf[0]);
+    } else {
+      ASSERT_EQ(tests[i].output_len, strlen(buf) + 1);
+      ASSERT_STREQ(tests[i].output, buf);
+      /* Check that the first byte after the trailing '\0' has not been
+      modified. */
+      ASSERT_EQ(unmodified, buf[tests[i].output_len]);
+    }
+  }
+
+  unlink(fn);
+}
+
+TEST(ReadSysFile, Int) {
+  static const char* fn = "TestReadSysFileInt";
+  struct {
+    /* input (file contents), e.g. "5" */
+    const char* input;
+    /* expected return value, if false, then the output is not checked */
+    bool ret;
+    /* expected result */
+    int output;
+  } tests[] = {
+    {"0", true, 0},
+    {"00", true, 0},
+    {"1", true, 1},
+    {"5", true, 5},
+    {"55", true, 55},
+
+    {" 123", true, 123},
+    {"123 ", true, 123},
+    {" 123 ", true, 123},
+    {"123\n", true, 123},
+
+    {"", false, 0},
+    {" ", false, 0},
+    {"a", false, 0},
+
+    {"-1", true, -1},
+    {" -456 ", true, -456},
+    {" -78.9 ", true, -78},
+  };
+
+  for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+    ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+    /* Leave the file to exist if some of the assertions fail. */
+
+    bool ret;
+    int output = 424242;
+
+    ret = ReadSysFile(fn, &output);
+
+    ASSERT_EQ(tests[i].ret, ret);
+
+    if (ret) {
+      ASSERT_EQ(tests[i].output, output);
+    }
+  }
+
+  unlink(fn);
+}
+
+TEST(ReadSysFile, Bool) {
+  static const char* fn = "TestReadSysFileBool";
+  struct {
+    /* input (file contents), e.g. "1" */
+    const char* input;
+    /* expected return value */
+    bool ret;
+    /* expected result */
+    bool output;
+  } tests[] = {
+    {"0", true, false},
+    {"00", true, false},
+    {"1", true, true},
+    {"5", true, true},
+    {"23", true, true},
+    {"-1", true, true},
+
+    {"", false, true /* unused */},
+  };
+
+  for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+    ASSERT_TRUE(WriteFile(fn, tests[i].input, strlen(tests[i].input)));
+    /* Leave the file to exist if some of the assertions fail. */
+
+    bool ret;
+    bool output;
+
+    ret = ReadSysFile(fn, &output);
+
+    ASSERT_EQ(tests[i].ret, ret);
+
+    if (ret) {
+      ASSERT_EQ(tests[i].output, output);
+    }
+  }
+
+  unlink(fn);
+}
+
+#endif /* ReadSysFile_PRESENT */
+
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/tests/gtest/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MODULE = 'xpcom_glue_gtest'
--- a/xpcom/threads/nsTimerImpl.cpp
+++ b/xpcom/threads/nsTimerImpl.cpp
@@ -314,16 +314,20 @@ void nsTimerImpl::Shutdown()
 }
 
 
 nsresult nsTimerImpl::InitCommon(uint32_t aType, uint32_t aDelay)
 {
   nsresult rv;
 
   NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED);
+  if (!mEventTarget) {
+    NS_ERROR("mEventTarget is NULL");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
 
   rv = gThread->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   /**
    * In case of re-Init, both with and without a preceding Cancel, clear the
    * mCanceled flag and assign a new mGeneration.  But first, remove any armed
    * timer from the timer thread's list.
@@ -629,16 +633,21 @@ NS_IMETHODIMP nsTimerEvent::Run()
 
   timer->Fire();
 
   return NS_OK;
 }
 
 nsresult nsTimerImpl::PostTimerEvent()
 {
+  if (!mEventTarget) {
+    NS_ERROR("Attempt to post timer event to NULL event target");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
   // XXX we may want to reuse this nsTimerEvent in the case of repeating timers.
 
   // Since TimerThread addref'd 'this' for us, we don't need to addref here.
   // We will release in destroyMyEvent.  We need to copy the generation number
   // from this timer into the event, so we can avoid firing a timer that was
   // re-initialized after being canceled.
 
   nsRefPtr<nsTimerEvent> event = new nsTimerEvent(this, mGeneration);